WPFのListViewを文字検索で絞り込み2 – ListViewFilterBehavior

C# コンピュータ
C#

ListViewで文字フィルターをするビヘイビア版です。

ただ、フィルターの場合フィルター用の文字を入力するテキストボックスが必要で、それとリストビューをどのようにして連携してビヘイビアに落とし込むかが課題になります。

ソースコード

ファイル名:FileInfoModel.cs

public class FileInfoModel
{
    public string Name { get; set; } = "";
    public int Length { get; set; } = 0;    
}

ファイル名:ListViewFilterBehavior.cs

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Xaml.Behaviors;

namespace ListViewSort03;
public class ListViewFilterBehavior : Behavior<ListView>
{
    public TextBox FilterTextBox
    {
        get { return (TextBox)GetValue(FilterTextBoxProperty); }
        set { SetValue(FilterTextBoxProperty, value); }
    }

    public static readonly DependencyProperty FilterTextBoxProperty =
        DependencyProperty.Register("FilterTextBox", typeof(TextBox), typeof(ListViewFilterBehavior));

    private ICollectionView? _collectionView;

    protected override void OnAttached()
    {
        base.OnAttached();
        _collectionView = CollectionViewSource.GetDefaultView(AssociatedObject.ItemsSource);
        if (FilterTextBox != null)
        {
            FilterTextBox.TextChanged += FilterTextBox_TextChanged;
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (FilterTextBox != null)
        {
            FilterTextBox.TextChanged -= FilterTextBox_TextChanged;
        }
    }

    private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (_collectionView != null)
        {
            _collectionView.Filter = o =>
            {
                if (string.IsNullOrEmpty(FilterTextBox.Text))
                {
                    return true;
                }
                else
                {
                    // フィルタリングのロジックをここに記述します。
                    if (o is FileInfoModel item)
                    {
                        return item.Name.Contains(FilterTextBox.Text, StringComparison.OrdinalIgnoreCase);
                    }
                    else
                    {
                        return false;
                    }
                }
            };
        }
    }
}

ファイル名:MainWindow.xaml

<Window
x:Class="ListViewSort03.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListViewSort03"
mc:Ignorable="d"
Title="ListViewSort03"
Height="450" Width="800"
xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors">
  <Window.DataContext>
    <local:MainWindowViewModel />
  </Window.DataContext>
  <Grid>
    <StackPanel>
      <TextBox x:Name="filterTextBox"/>
      <ListView ItemsSource="{Binding Files}">
          <i:Interaction.Behaviors>
              <local:ListViewFilterBehavior FilterTextBox="{Binding ElementName=filterTextBox}" />
          </i:Interaction.Behaviors>
          <ListView.View>
            <GridView>
                <GridViewColumn Width="200">
                    <GridViewColumnHeader
                        x:Name="HeadName1"
                        Content="名前" />
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                              <TextBlock
                                Width="100"
                                TextAlignment="Left"
                                Text="{Binding Name}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Width="200">
                    <GridViewColumnHeader
                        x:Name="HeadLength1"
                        Content="長さ" />
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                              <TextBlock
                                Width="100"
                                TextAlignment="Right"
                                Text="{Binding Length}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>    
        </ListView.View>
      </ListView>
    </StackPanel>
  </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using System.Diagnostics;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;

namespace ListViewSort03;
public class MainWindowViewModel : INotifyPropertyChanged
{
    // INotifyPropertyChanged
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged(string name) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    /**************************************************************************
    * プロパティ
    **************************************************************************/
    // ファイルの一覧
    public ReactiveCollection<FileInfoModel> Files { get; private set; } = [];

    public MainWindowViewModel()
    {
        PropertyChanged += (s, e) => {};

        Files.AddOnScheduler(new FileInfoModel(){Name = "a.txt", Length = 99});
        Files.AddOnScheduler(new FileInfoModel(){Name = "b.txt", Length = 88});
        Files.AddOnScheduler(new FileInfoModel(){Name = "c.txt", Length = 77});
    }
}

実行

テキストボックスに「a」と入力するとリストビューがフィルタリングされます。

説明

リストビューフィルターのビヘイビアをテキストボックスにバインドしています。
なるべくViewとビヘイビアで完結するようにしていますが、FileInfoModelを使っているので再利用の場合セットで考える必要があります。

コメント