1つのTextBoxに入力した文字から、2つの列を持つListViewの項目を絞り込む場合、それぞれの列のList系データを連結し、重複する項目を削除し、TextBoxに入力に応じてフィルタリングする方法が考えられます。
前回は、2つの列を持つListViewのリストから、TeextBoxに入力された値をもとに、1つの列の値を絞り込みました。
今回は、2つの列どちらに入力されていても、リストの項目を絞り込めるプログラムを作成することを目指します。
実行結果
完成したアプリケーションの動作を見て、プログラムをイメージしてみてください。
TextBoxに、ListViewにある2つの列のどちらの内容を入力しても、ListViewに表示されるリストが絞り込まれます。
クラスに、同じかどうか調べるメソッドを追加する
値の重複を調べるためには、2つの値が、同じか、あるいは、異なるかを判断できる必要があります。
複数のデータメンバを持つクラスでは、同じどうかをどのように判断するか明示したメソッドがないと、判断することができません。
一般的に、C#のクラスでは、値が、同じどうか調べるには、==やEqualsメソッドを使用します。
メソッドは、機能ごとに統一した名前を使用したほうが覚え安く、使いやすいので、一般的につかわれているメソッド名がある場合は、同じメソッド名を使用します。
特に理由がなければ、IEquatable<T> インターフェイスを実装することで対応します。
IEquatable
ハッシュコードを実装する
IEquatable<T> インターフェイスを実装する際に悩ましいのがハッシュコードです。
ハッシュコードは、比較速度向上とした簡略比較のための目印です。同じオブジェクトが、必ず同じ値を変えす必要があります。しかし、すべて同じ値でも、処理速度は遅くなるものの問題はおきないはずです。速度上昇のために、できるだけ重複しない値を指定します。
ここでは、productCode文字列をint型に、変換し、ハッシュコードに指定しています。productCode文字列が、int型に変換できない場合は、String.GetHashCode メソッドで、文字列からハッシュコードを生成します。
- String.GetHashCode メソッド
文字列からハッシュコードを生成するメソッドです。
Productクラスに、IEquatable インターフェイスを実装する
以前作成した、プログラムを修正していきます。
Productクラスに、IEquatable
Product.cs
using System;
namespace ListChoiceAssist2
{
class Product : IEquatable<Product>
{
public string productCode { set; get; }
public string productName { set; get; }
// コンストラクタを定義
public Product(string code, string name)
{
productCode = code;
productName = name;
}
// 同じかどうか比較する
public bool Equals(Product other)
{
if (other == null)
return false;
if (this.productCode == other.productCode)
return true;
else
return false;
}
public override bool Equals(Object obj)
{
if (obj == null)
return false;
Product productObj = obj as Product;
if (productObj == null)
return false;
else
return Equals(productObj);
}
public override int GetHashCode()
{
// productCodeをint型に変換し、ハッシュコードにします。
if (Int32.TryParse(this.productCode, out int j))
return j;
else
// 文字列からハッシュコードを生成します。
return this.productCode.GetHashCode();
// productCodeが存在せず、productNameだけが存在することはない。
// productCodeは、重複していない。
// という仮定が存在します。
}
}
}
Window.xamlは、そのまま。
Window.xamlには、変更はありません。
<Window x:Class="ListChoiceAssist2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="250" Width="400">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="ProductCode" Grid.Column="0"/>
<TextBlock Name="ProductName" Grid.Column="1"/>
</Grid>
<TextBox DockPanel.Dock="Top" Name="ItemSerch" TextChanged="ItemSerch_TextChanged"/>
<ListView DockPanel.Dock="Bottom" Name="ProductList" Width="Auto" SelectionChanged="ProductList_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="商品コード" DisplayMemberBinding="{Binding productCode}"/>
<GridViewColumn Header="商品名" DisplayMemberBinding="{Binding productName}" />
</GridView>
</ListView.View>
</ListView>
</DockPanel>
</Window>
ItemSerch_TextChangedイベントのフィルタリング処理コードを変更する。
productCode列、productName列のそれぞれで、入力文字でフィルタリングしたリストを作成した後、 2つのリストを結合して、重複のないリストを作成し、作成したリストをItemsSourceに指定しました。
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace ListChoiceAssist2
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SetDemoData();
}
ObservableCollection<Product> products = new ObservableCollection<Product>();
public void SetDemoData()
{
// 動作確認用のデモデータ
products.Add(new Product("1001", "みかん"));
products.Add(new Product("1002", "りんご"));
products.Add(new Product("1003", "ばなな"));
products.Add(new Product("1011", "もも"));
products.Add(new Product("1012", "いちご"));
products.Add(new Product("1013", "ぶどう"));
products.Add(new Product("1101", "いちじく"));
products.Add(new Product("1102", "キューイフルーツ"));
products.Add(new Product("1103", "パインナップル"));
products.Add(new Product("2001", "さくらんぼ"));
products.Add(new Product("2002", "オレンジ"));
products.Add(new Product("2003", "レモン"));
products.Add(new Product("3011", "メロン"));
products.Add(new Product("3021", "スイカ"));
products.Add(new Product("3031", "かき"));
products.Add(new Product("4001", "ブルーベリー"));
products.Add(new Product("4002", "ラズベリー"));
products.Add(new Product("4003", "マンゴー"));
products.Add(new Product("5001", "プラム"));
products.Add(new Product("5002", "かぼす"));
products.Add(new Product("5003", "すだち"));
products.Add(new Product("6001", "ドラゴンフルーツ"));
products.Add(new Product("6002", "びわ"));
products.Add(new Product("6003", "クレープフルーツ"));
ProductList.ItemsSource = products;
}
private void ProductList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// 選択した内容をTextBlockに表示する
// ListViewで何も選択されていない場合は何もしない
if (ProductList.SelectedItem == null) return;
// ListViewで選択されている項目を取り出す
Product ListItem = (Product)ProductList.SelectedItem;
// ListViewで選択されている項目をテキストボックスに表示する
ProductCode.Text = ListItem.productCode;
ProductName.Text = ListItem.productName;
}
private void ItemSerch_TextChanged(object sender, TextChangedEventArgs e)
{
// 検索ボックスへの入力で、ListViewに表示する内容を変更する
// productCode列の値を入力文字でフィルタリングしたリストを作成する
var filterList_code =
products.Where(x => x.productCode.Contains(ItemSerch.Text)).ToList();
// productName列の値を入力文字でフィルタリングしたリストを作成する
var filterList_name =
products.Where(x => x.productName.Contains(ItemSerch.Text)).ToList();
// 2つのリストを結合して、重複のないリストを作る
var filterList = filterList_code.Union(filterList_name).ToList();
// 作成したリストをItemsSourceに指定する
ProductList.ItemsSource = filterList;
}
}
}
- Enumerable.Union メソッド
2 つのシーケンスの和集合を生成するメソッドです。