バックグランド検索をするWPF TextBox

以前WPFで入力補完が効くTextBoxを作成してみました。似たところで、入力の度にバックグランドで非同期タスクにより検索を走らせる処理を考えてみます。

 

form

簡単にはこんなところでしょうか。

MainWindow.xaml.cs

<Window x:Class="BackgroundSearchTextBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="174" Width="334">
    <Grid>
        <Label Content="検索ワード" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="SearchWordTextBox" HorizontalAlignment="Left" Height="23" Margin="95,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="201"/>
        <Label Content="検索結果" HorizontalAlignment="Left" Margin="10,43,0,0" VerticalAlignment="Top"/>
        <TextBlock x:Name="SearchResultTextBlock" HorizontalAlignment="Left" Margin="95,43,0,0" TextWrapping="Wrap" Text="Enter Keyword" VerticalAlignment="Top" Height="91" Width="201"/>
    </Grid>
</Window>

MainWindow.xaml

namespace BackgroundSearchTextBox
{
    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows;

    public partial class MainWindow : Window
    {
        private string[] candidates = new string[]{
            "apple",
            "banana",
            "orange",
            "grape",
            "pineapple",
            "mango"
        };

        public MainWindow()
        {
            InitializeComponent();

            // Rxなし
            SearchWordTextBox.TextChanged += (s, e) =>
            {
                Task.Factory.StartNew(
                    search =>
                    {
                        string searchWord = search as string;
                        if (String.IsNullOrEmpty(searchWord)) return "Enter Keyword.";
                        var candidatesQuery = candidates.Where(cand => cand.StartsWith(searchWord));
                        return candidatesQuery.IsEmpty() ? "No Result." : candidatesQuery.Aggregate((a, b) => a + Environment.NewLine + b);
                    },
                    SearchWordTextBox.Text)
                .ContinueWith(t => SearchWordTextBox.Dispatcher.Invoke(() => SearchResultTextBlock.Text = t.Result));
            };

        }
    }
}

これを動かすにはNugetパッケージインストーラから

PM> install-package ix-main

としてInteractive Extensionsをインストールしておく必要があります。

上記はイマイチ。文字入力の度にタスクを起動させていますが、頻繁な文字入力の度にタスクを生成してたら結構オーバーヘッドが大きいです。そこでRxを使ってみます。

 

MainWindow.xaml.cs

namespace BackgroundSearchTextBox
{
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Reactive.Linq;
    using System.Reactive.Concurrency;

    public partial class MainWindow : Window
    {
        private string[] candidates = new string[]{
            "apple",
            "banana",
            "orange",
            "grape",
            "pineapple",
            "mango"
        };

        public MainWindow()
        {
            InitializeComponent();

            // Rxあり
            Observable.FromEvent<TextChangedEventHandler, TextChangedEventArgs>(
                action => (s, e) => action(e),
                handler => SearchWordTextBox.TextChanged += handler,
                handler => SearchWordTextBox.TextChanged -= handler)
                .Select(_ => SearchWordTextBox.Text)
                .Throttle(TimeSpan.FromMilliseconds(100))
                .Select(searchWord =>
                    {
                        if (String.IsNullOrEmpty(searchWord)) return "Enter Keyword.";
                        var candidatesQuery = candidates.Where(cand => cand.StartsWith(searchWord));
                        return candidatesQuery.IsEmpty() ? "No Result." : candidatesQuery.Aggregate((a, b) => a + Environment.NewLine + b);
                    }
                )
                .ObserveOn(new DispatcherScheduler(App.Current.Dispatcher))
                .Subscribe(t => SearchResultTextBlock.Text = t);

        }

    }
}

Rxあり版を動かすためにはNugetパッケージマネージャーから

PM> install-package rx-main
PM> install-package rx-wpf

としてReactive Extensions(Rx)のインストールも必要です。Rxを使うことで、

  • ちゃんとイベントリッスン解除ができる
  • 100ms間値が落ち着くのを待つことができる
  • 全体的に、特にスレッド行き来する処理の記述が容易&見やすい

と言えますね。まだまだ学習が必要だ・・。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中