WPFでGoogle desktopの検索結果を出す

Google desktopの検索を実行して,その結果をリスト表示してみます.

サンプルコード

全コードはこちら.http://febhare.bake-neko.net/Src/Sample_GoogleDesktop_Search.zip

使用可能な検索APIと使用するAPI

検索方法はhttp://code.google.com/apis/desktop/docs/queryapi.html#httpxmlに書かれているように,COMを使う方法とHTTP/XML-Based Query APIとの2方法があります.COMを使うにはプログラムを登録する手間が必要です.そこでそのような登録が不要な分手軽なQuery APIを使います.

コード詳細

コードは,

  • 検索URLをレジストリから取得する
  • 結果をWebRequestで取得する
  • XML形式の結果をリスト表示する

の3段階です.

検索URLをレジストリから取得
string get_baseuri()
{
    Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Google\Google Desktop\API");
    string uri = key.GetValue("search_url") as string;
    key.Close();
    return uri;
}
空白で区切られた文字列から問い合わせURLを作る.
string get_query(string query)
{            
    StringBuilder sb = new StringBuilder();
    foreach(String item in query.Split(new char [] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
    {
        sb.Append("+" + item);
    }
    string txt = sb.ToString().Trim(new char [] {'+'});

    string baseuri = get_baseuri();
    //サンプル http://127.0.0.1:4664/search&s=認証用の文字列?q=Google&format=xml
    return string.Format("{0}{1}&format=xml", get_baseuri(), txt);
}

***検索結果をWebRequestで取得
>|java|
string get_xml(string query)
{
    WebRequest wreq = WebRequest.Create(get_query(query));
    WebResponse wres = wreq.GetResponse();
    using(Stream src = wres.GetResponseStream())
    using (StreamReader rdr = new StreamReader(src))
    {
        return rdr.ReadToEnd();
    }            
}
結果を表示する

問い合わせ結果からXmlDataProviderのインスタンスを作りDataContextに入れます.
XAMLはこのように書いています.

<ListBox ItemsSource="{Binding XPath=//result}">
<ListBox.ItemTemplate>
  <DataTemplate>
    <Border>
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Title: "/>                                
                <local:HtmlTextBlock HtmlText="{Binding XPath=title}" />
            </StackPanel>
            ...中略...
    </Border>
  </DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
アイコンの表示

iconにパスが入っているので,QueryのURLを組み立てるときに使ったSystem.Uriから,スキームとUri.Authorityとパスを組み立てて,それをImageのSourceにバインドすればOK.

ちょっと困ったこと,文字列の書式変換をどうするか

Google desktopの結果文字列はHTMLでマークアップされていて,この書き方ではマークアップがそのまま表示されてしまいます.

<StackPanel Orientation="Horizontal">
  <TextBlock Text="Title: "/>                                
  <TextBlock Text="{Binding XPath=title}"/>
</StackPanel>

そのマークアップをちゃんと表記してやろうとしてはまりました.
文字列の書式設定をするにはTextBlockのInlinesに要素を追加していけばよいのですが,Inlinesは依存関係プロパティではないのでバインディングができません.Textbox with dynamic inlines - WPF Reflections,ここにあるとおりです.
イベントを拾いコードからInlinesを設定しようとしました.下記のようなコードを書いて,1.上記コードのバインディングをDataContextに設定してDataContextChangedイベントを拾う,2.ターゲット更新イベントを拾う,をそれぞれ試しましたがイベントが発生しませんでした.XPathで指定したXmlElementがBindされることを期待して,DataContextにXmlDataProviderの新しいインスタンスを与えてはいるのですが.

<StackPanel Orientation="Horizontal">
  <TextBlock Text="Title: "/>                                
  <TextBlock Text="{Binding XPath=title, NotifyOnTargetUpdated=True}" TargetUpdated="TextBlock_TargetUpdated"/>
</StackPanel>

仕方がないので,TextBlockを継承するクラスを作成して,HtmlTextという依存関係プロパティを入れました.このテキストに入力されたマークアップ文字列はInlinesに設定されるコードを追加しました.