<やりたいこと>
Excelみたいなフィルタ機能を持ったDataGridColumnを作りたい
<どうするか>
Excelのように「▼」マークを押してフィルタ処理を行うためにはDataGridColumnのHeaderプロパティがクリックされたらポップアップで選択肢を表示してフィルタする
DataGridColumnはObject型のHeaderプロパティを持つので、Headerプロパティに独自で作成した「DataGridFilterTextColumnHeader」クラスを指定する。
DataGridFilterTextColumnHeaderクラスの役割はクリックされたらチェックボックスのあるリストを表示する。表示する内容は、DataGridFilterTextColumnにバインドされている値を候補とする。
・DataGridTextColumnを継承したフィルタ機能の付いたDataGridFilterTextColumnクラスを作成。
1.UserControlを追加
2.「UserControl」を「DataGridTextColumn」に変更し、エラーの発生する<Grid>タグや「d:DesignHeight」「d:DesignWidth」を削除する
3.完成
4.「DataGridFilterTextColumn.xaml.cs」を開いて、継承元のクラス名を修正
/// <summary>
/// DataGridFilterTextColumn.xaml の相互作用ロジック
/// </summary>
public partial class DataGridFilterTextColumn : UserControl
{
↓
/// <summary>
/// DataGridFilterTextColumn.xaml の相互作用ロジック
/// </summary>
public partial class DataGridFilterTextColumn : DataGridTextColumn
{
・DataGridTextColumnのHeaderプロパティに指定するためのUserControlを作成する
1.UserControlを追加
2.「Grid」「d:DesignHeight」「d:DesignWidth」を削除する
3.コンテンツ部にHeaderに表示したい内容を作成する。
ソース:DataGridFilterTextColumnHeader.xaml
<UserControl x:Class="FilterDataGridSample.DataGridFilterTextColumnHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FilterDataGridSample"
mc:Ignorable="d" >
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="tblHeaderText" Text="{Binding HeaderText}"/>
<Button Name="btnFilter" Margin="3,0,0,0" >
<Button.Template>
<ControlTemplate>
<Image Source="{Binding ImagePath}" Width="10" Height="10" />
</ControlTemplate>
</Button.Template>
</Button>
<!--フィルタ内容を選択するためのPopUpコントロール-->
<Popup Name="popupFilter" Placement="Bottom" PlacementTarget="{Binding ElementName=btnFilter}" StaysOpen="False" Width="200" >
<Border Background="White" BorderBrush="Gray" BorderThickness="1,1,1,1">
<StackPanel Margin="2,2,2,2">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Button Margin="0,0,0,0" Name="btnSelectAll" >
全選択
</Button>
<Button Margin="5,0,0,0" Name="btnUnselectAll">
全解除
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<TextBox Name="txtFilter" Width="150" Text="{Binding FilterText,UpdateSourceTrigger=PropertyChanged}"></TextBox>
</StackPanel>
<DockPanel Margin="0" Height="75">
<ListBox x:Name="lstCountries" BorderThickness="1" ItemsSource="{Binding FilterList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item}" Checked="ApplyFilters" Unchecked="ApplyFilters" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</StackPanel>
</Border>
</Popup>
</StackPanel>
</UserControl>
ソース:DataGridFilterTextColumnHeader.xaml.cs
/// <summary>
/// DataGridFilterTextColumnHeader.xaml の相互作用ロジック
/// </summary>
public partial class DataGridFilterTextColumnHeader : UserControl
{
public ObservableCollection<CheckedListItem<string>> customerFilters = new ObservableCollection<CheckedListItem<string>>();
public DataGridFilterTextColumn OwnerColumn;
public MainViewModel ViewModel = new MainViewModel();
public DataGridFilterTextColumnHeader()
{
InitializeComponent();
btnFilter.Click += btnFilter_Click;
btnSelectAll.Click += btnSelectAll_Click;
btnUnselectAll.Click += btnUnselectAll_Click;
popupFilter.Closed += popupFilter_Closed;
ViewModel.OwnerColumn = OwnerColumn;
this.DataContext = ViewModel;
}
#region 添付プロパティ
#region HeaderText
/// <summary>
/// ヘッダに表示する文字列
/// </summary>
public string HeaderText
{
get { return base.GetValue(HeaderTextProperty) as string; }
set { base.SetValue(HeaderTextProperty, value); }
}
// RegisterAttachedメソッドを使って添付プロパティを作成する
public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.RegisterAttached(
"HeaderText", typeof(string),
typeof(DataGridFilterTextColumnHeader), new PropertyMetadata(string.Empty,
new PropertyChangedCallback((sender, e) =>
{
(sender as DataGridFilterTextColumnHeader).OnHeaderTextPropertyChanged(sender, e);
})));
private void OnHeaderTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
this.tblHeaderText.Text = e.NewValue.ToString();
}
#endregion
#endregion
/// <summary>
/// フィルタ条件の画面を表示
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnFilter_Click(object sender, RoutedEventArgs e)
{
popupFilter.IsOpen = true;
// ▽ フィルタ対象の追加・削除があるので現在の設定値を引き継ぐ形にする
ObservableCollection<CheckedListItem<string>> _filterList = OwnerColumn.GetCheckBoxItem();
if (_filterList == null) { return; }
foreach (CheckedListItem<string> filter in _filterList)
{
CheckedListItem<string> _i = customerFilters.Where(c => c.Item == filter.Item).FirstOrDefault();
if (_i != null) { filter.IsChecked = _i.IsChecked; };
}
customerFilters = _filterList;
this.ViewModel.FilterList = customerFilters;
}
/// <summary>
/// フィルタ処理を適用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ApplyFilters(object sender, RoutedEventArgs e)
{
ViewModel.OwnerColumn = OwnerColumn;
List<string> filter = customerFilters.Where(c => c.IsChecked == true).Select(c => c.Item).ToList();
// フィルタ処理を呼び出す
OwnerColumn.ApplayFilter(filter);
if (filter.Count== customerFilters.Count) {
// フィルタ処理対象文字列とチェックボックスの項目の数が同じ場合はフィルタからなくす
ViewModel.OwnerColumn.RemoveFilter();
}
ViewModel.OnPropertyChanged_ImagePath();
}
/// <summary>
/// 全選択処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelectAll_Click(object sender, RoutedEventArgs e)
{
this.ViewModel.FilterText = "";
foreach (CheckedListItem<string> item in customerFilters)
{
item.IsChecked = true;
}
}
/// <summary>
/// 選択解除処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnUnselectAll_Click(object sender, RoutedEventArgs e)
{
this.ViewModel.FilterText = "";
foreach (CheckedListItem<string> item in customerFilters)
{
item.IsChecked = false;
}
}
/// <summary>
/// popupコントロールが閉じた時にFilterTextを空白にする
/// FilterTextを指定して自動でチェックボックスリストのチェックが入った後に
/// さらにチェックボックスリストでフィルタ条件を指定し、その後PopUpコントロールを開くと、手動で外したものがチェックされた状態で
/// 表示されてしまう
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void popupFilter_Closed(object sender, EventArgs e)
{
this.ViewModel.FilterText = "";
}
#region ViewModel
/// <summary>
/// ViewModel
/// </summary>
public class MainViewModel : INotifyPropertyChanged
{
public DataGridFilterTextColumn OwnerColumn;
#region INotifyPropertyChangedインターフェイスイベント実装
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region プロパティ
private string _headerText = "";
/// <summary>
/// ヘッダに表示する文字列
/// </summary>
public string HeaderText
{
get { return _headerText; }
set
{
_headerText = value;
NotifyPropertyChanged();
}
}
private string _filterText = "";
/// <summary>
/// CheckBoxリストボックスのフィルタ条件
/// </summary>
public string FilterText
{
get { return _filterText; }
set
{
_filterText = value;
NotifyPropertyChanged();
if (_filterText != "") { OnPropertyChanged_FilterList(); }
}
}
/// <summary>
/// 現在適用されているフィルタ状態をわかりやすくするためのイメージパス
/// フィルタ処理前とフィルタ処理適用中とでイメージを分けることで見やすくしている
/// </summary>
public string ImagePath
{
get {
string path = @".\Images\filter.png";
if (OwnerColumn != null)
{
if (OwnerColumn.GetDataGridOwner().FilteredColumns.Contains(OwnerColumn)) { path = @".\Images\filtered.png"; }
}
return path;
}
}
private ObservableCollection<CheckedListItem<string>> _filterList = new ObservableCollection<CheckedListItem<string>>();
/// <summary>
/// チェックリストボックスにバインドするプロパティ
/// コントロールのDataTemplateにバインドするのでチェックボックスが自動生成される
/// FilterTextプロパティが指定されている場合は一致するものをIsCheckedプロパティをTrueに指定
/// </summary>
public ObservableCollection<CheckedListItem<string>> FilterList
{
get
{
if (this.FilterText.Trim() != "")
{
// FilterTextプロパティに指定されている文字列を含むかどうかの結果をチェックボックスの初期値にする
foreach (var filterItem in _filterList) { filterItem.IsChecked = filterItem.Item.Contains(this.FilterText); }
return _filterList;
}
else { return _filterList; }
}
set
{
_filterList = value;
NotifyPropertyChanged();
}
}
#endregion
#region メソッド
/// <summary>
/// 外部からImagePathプロパティの変更通知を発生
/// </summary>
public void OnPropertyChanged_ImagePath()
{
NotifyPropertyChanged(nameof(MainViewModel.ImagePath));
}
/// <summary>
/// 外部からFilterListプロパティの変更通知を発生
/// </summary>
public void OnPropertyChanged_FilterList()
{
NotifyPropertyChanged(nameof(MainViewModel.FilterList));
}
#endregion
}
#endregion
}
今日はここまで。
0 件のコメント:
コメントを投稿