FilterDataGrid作成(3)ソース:DataGridFilterTextColumn

前回作成したDataGridFilterTextColumnを作成していく。
DataGridTextColumnのHeaderプロパティはオブジェクト型で中身を様々にカスタマイズできる。今回は、Headerプロパティに前回作成したDataGridFilterTextColumnHeaderを設定するようにする。
ヘッダ部に表示する列のタイトルについてはDataGridFilterTextColumnHeaderで添付プロパティを公開し、その値を表示するようにする。(HeaderTextプロパティ)

DataGridTextColumn.xaml

<DataGridTextColumn x:Class="FilterDataGridSample.DataGridFilterTextColumn"
             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" >

</DataGridTextColumn>

DataGridTextColumn.xaml.cs
    /// <summary>
    /// DataGridFilterTextColumn.xaml の相互作用ロジック
    /// </summary>
    public partial class DataGridFilterTextColumn : DataGridTextColumn
    {
        DataGridFilterTextColumnHeader filterHeader;

        public DataGridFilterTextColumn()
        {
            InitializeComponent();

            filterHeader= new DataGridFilterTextColumnHeader();
            filterHeader.OwnerColumn = this;
            this.Header = filterHeader;
        }

        #region 添付プロパティ
     
        #region HeaderText

        /// <summary>
        /// ヘッダに表示する文字列
        /// DataGridFilterTextColumnHeaderクラスのHeaderTextプロパティに値を渡すための添付プロパティ
        /// </summary>
        public string HeaderText
        {
            get { return base.GetValue(HeaderTextProperty) as string; }
            set { base.SetValue(HeaderTextProperty, value); }
        }
        public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.RegisterAttached(
            "HeaderText", typeof(string),
            typeof(DataGridFilterTextColumn), new PropertyMetadata(string.Empty,
                                                                                new PropertyChangedCallback((sender, e) =>
                                                                                {
                                                                                    (sender as DataGridFilterTextColumn).OnHeaderTextPropertyChanged(sender, e);
                                                                                })));
        private void OnHeaderTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            this.filterHeader.HeaderText = e.NewValue.ToString();
        }

        #endregion

        #endregion

        #region メソッド

        /// <summary>
        /// DataGridFilterTextColumnHeaderのチェックボックスリストを表示するためのリストを取得
        /// </summary>
        /// <returns>
        /// どのようなクラスがバインドされるかわからないので、リフレクションを利用して文字列からクラス名や
        /// PropertyInfoを取得して、値を取得するようにする
        /// </returns>
        public ObservableCollection<CheckedListItem<string>> GetCheckBoxItem()
        {
            FilterDataGrid dgOwner = (FilterDataGrid)this.DataGridOwner;

            // CurrentItemからバインドされているクラス名を取得するためにセルにフォーカスを設定
            _SetCurrentItemIfIsNullCurrentItem();

            // 文字列から型を取得し、型からバインドされているプロパティ名を取得
            if (dgOwner.CurrentItem == null) { return null; };
            string BindingName = _GetBindingPath(this);
            Type BindingType = Type.GetType(dgOwner.CurrentItem.GetType().FullName.ToString());
            if (BindingType == null) { return null; }
            PropertyInfo BindingPropertyInfo = BindingType.GetProperty(BindingName);

            // 戻り値を作成
            ObservableCollection<CheckedListItem<string>> checkedList = new ObservableCollection<CheckedListItem<string>>();
            var dgItemsSource = dgOwner.ItemsSource;
            List<string> lstDistinct = new List<string>();

            // dgOwner.ItemsSourceでは、フィルタした後の結果をさらに絞り込むときにフィルタする前の結果と変わらなくなるためViewの値を取得してループする
            // Excelの動きと合わせるため、最後にフィルタした列はすべての選択肢を表示し、追加でフィルタした列はフィルタした後の結果に対して行う
            IEnumerable enumerableList;
            if (dgOwner.FilteredColumns.Contains(this)){enumerableList = (IEnumerable)dgOwner.ItemsSource;}
            else{ enumerableList = (IEnumerable)CollectionViewSource.GetDefaultView(dgOwner.ItemsSource);}
            foreach (var bindingItem in enumerableList)
            {
                // 対象とする型が一致しているかチェックする
                if (BindingType.FullName != bindingItem.GetType().FullName) { break; }
                var value = BindingPropertyInfo.GetValue(bindingItem);
                if (value == null) { break; }
                if (lstDistinct.Contains(value.ToString()) == false)
                {
                    checkedList.Add(new CheckedListItem<string> { Item = value.ToString(), IsChecked = true });
                    lstDistinct.Add(value.ToString());
                }
            }

            // 後処理
            dgItemsSource = null;
            dgOwner = null;

            return checkedList;
        }

        /// <summary>
        /// CurrentItemが存在しない場合は、CurrentItemを設定する
        /// </summary>
        private void _SetCurrentItemIfIsNullCurrentItem()
        {
            FilterDataGrid dgOwner = (FilterDataGrid)this.DataGridOwner;
            // NewPreceholderがCurrentItemになっていることがある
            //if (dgOwner.CurrentItem == null)
            //{
            int mincount = 0;
            if (dgOwner.CanUserAddRows == true) { mincount = 1; }
            if (dgOwner.Items.Count == mincount) { return; }
            DataGridCellInfo cellInfo = new DataGridCellInfo(dgOwner.Items[0], dgOwner.Columns[0]);
            dgOwner.Focus();
            dgOwner.SelectedIndex = 0;
            dgOwner.CurrentCell = cellInfo;
            //}
            dgOwner = null;
        }

        /// <summary>
        /// フィルタ処理を実行
        /// DataGridFilterTextColumnHeaderのチェックボックスリストのチェック状態が変更された時に呼び出される
        /// </summary>
        /// <returns>
        /// DataGridのItemsSourceにバインドされている値のDefaultViewを取得し、Viewのリアルたむフィルタを実行
        /// どのようなクラスがバインドされるかわからないので、リフレクションを利用して文字列からクラス名や
        /// PropertyInfoを取得して、値を取得するようにする
        /// </returns>
        public void ApplayFilter(List<string> lstFilterValues)
        {
            // CurrentItemからバインドされているクラス名を取得するためにセルにフォーカスを設定
            _SetCurrentItemIfIsNullCurrentItem();

            // 文字列から型を取得し、型からバインドされているプロパティ名を取得
            string BindingName = _GetBindingPath(this);
            FilterDataGrid dgOwner = (FilterDataGrid)this.DataGridOwner;
            Type BindingType = Type.GetType(dgOwner.CurrentItem.GetType().FullName.ToString());
            if (BindingType == null) { return; }
            PropertyInfo BindingPropertyInfo = BindingType.GetProperty(BindingName);
            if (BindingPropertyInfo == null) { return; }

            // 引数として渡された値を利用してフィルタ条件を作成し、DataGridのItemsSourceにバインドされているリストのViewのフィルタ条件と指定する
            var CustomFilter = new Predicate<object>(item => lstFilterValues.Contains(BindingPropertyInfo.GetValue(item).ToString()));
            var ItemSourceView = CollectionViewSource.GetDefaultView(dgOwner.ItemsSource);
            // 複数のフィルタに適用するためFilterDataGridにフィルタ条件を追加
            //ItemSourceView.Filter = CustomFilter;
            dgOwner.Filters.RemoveFilterByColumn(this);
            dgOwner.FilteredColumns.Remove(this);
            dgOwner.Filters.AddFilter(CustomFilter, this);
            dgOwner.FilteredColumns.Add(this);
            dgOwner.LastFilterColumn = this;
            ItemSourceView.Filter = dgOwner.Filters.Filter;

            // 後処理
            dgOwner = null;
            CustomFilter = null;
            ItemSourceView = null;
        }

        public FilterDataGrid GetDataGridOwner()
        {
            return (FilterDataGrid)this.DataGridOwner;
        }

        public void RemoveFilter()
        {
            FilterDataGrid dgOwner = (FilterDataGrid)this.DataGridOwner;
            dgOwner.FilteredColumns.Remove(this);
            dgOwner.Filters.RemoveFilterByColumn(this);
        }

        /// <summary>
        /// 指定されたDataGridColumnのプロパティパスを取得
        /// ソートメンバーパスが設定されていなければ、バインドされているプロパティ名を取得
        /// </summary>
        /// <param name="column">DataGridColumnオブジェクト</param>
        /// <returns>バインディングされているプロパティのパス</returns>
        private string _GetBindingPath(DataGridColumn column)
        {
            var path = column.SortMemberPath;

            if (!string.IsNullOrWhiteSpace(path)) { return path; }

            if (column is DataGridBoundColumn)
            {
                var binding = (column as DataGridBoundColumn).Binding as Binding;
                if (binding != null)
                {

                    if (!string.IsNullOrEmpty(binding.XPath)) { return binding.XPath; }
                    else if (binding.Path != null) { return binding.Path.Path; }
                }
            }
            else if (column is DataGridComboBoxColumn)
            {
                return ((DataGridComboBoxColumn)column).SelectedValuePath;
            }

            column = null;
            return path;
        }
        #endregion
     
    }

どのようなクラスがバインドされるかわからないのでリフレクションで文字列からクラスやプロパティを取得している。
dllとして公開するときには「Type.GetType」を利用してもアセンブリが異なるのでリフレクションで値を取得できない。
この場合は、下記のようにアセンブリを指定してリフレクションを行う必要があるので注意する。

        /// <summary>
        /// Type.GetTypeはmscorlib.dll、実行中のassembly内にあるものでしか動かないため、
        /// dllを別プロジェクトで参照しているときには実行できない
        /// そのため、GetAssembliesメソッドで参照しているすべてのassemblyを取得し、
        /// そのassemblyに対してGetTypeメソッドを実行する
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static Type GetType(string typeName)
        {
            var type = Type.GetType(typeName);
            if (type != null) return type;
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = assembly.GetType(typeName);
                if (type != null)
                    return type;
            }
            return null;
        }

今日はここまで。

0 件のコメント:

コメントを投稿

PowerShellでEdgeを自動化(インストール不要。参考:郵便追跡サービス自動操作)

1.経緯について  RPAのソフトをインストールできないので、これまでVBSでCreateObjectでブラウザの自動操作をすることがたまにあった。 ※いざというときの手札として持っているだけで安心感が段違い  見た目上IEがインストールされていなくても、CreateObject...