VB C# .NET Freamworkの備忘録

C#, VB.NET Freamworkの備忘録を掲載しています。 コントロール、WPF、スレッド、共通関数と実用的なコードを掲載してきます。

SortableBindingList

   ソート用のバインディングリストです。
第2キーを設定したい場合には以下のように使用します。

// Nameがソートされる際はIdでソートするようにソート方法変更
var idCompare = new Dictionary<string, IComparer<Data>>
{
   {"Name", ComparerUtils.By((Data r) => r.Id)}
};
 
//ソートの条件
var sortCompare = ComparerUtils.By((Data r) => r.Date).Append(ComparerUtils.By((Data r) => r.Name));

// 検索データの設定
this.dgvList.DataSource = null;
this.dgvList.DataSource = new SortableBindingList<Data>(filterList, sortCompare);

↓↓↓↓↓↓↓↓↓↓↓↓

     /// <summary>
    /// ソート用の拡張バインディングリスト
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SortableBindingList<T> :BindingList<T>
    {
        /// <summary>
        /// ソート項目
        /// </summary>
        private PropertyDescriptor _sortProp;
        /// <summary>
        /// ソート方向(昇順・降順)の保持を行います。
        /// </summary>
        private ListSortDirection _sortDir = ListSortDirection.Ascending;
        /// <summary>
        /// ソート済みかを示す値
        /// </summary>
        private bool _isSorted;

        /// <summary>
        /// 最優先比較オブジェクト
        /// </summary>
        private readonly Dictionary<string, IComparer<T>> _prioritySort;

        /// <summary>
        /// 第2キーの追加比較オブジェクト
        /// </summary>
        private readonly IComparer<T> _baseComparer;
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SortableBindingList()
        {
            
        }
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SortableBindingList(IList<T> list)
            : this(list,null)
        {
            
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SortableBindingList(IList<T> list,IComparer<T> baseComparer)
            : this(list, baseComparer,null)
        {
            _baseComparer = baseComparer;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SortableBindingList(IList<T> list, IComparer<T> baseComparer, Dictionary<string, IComparer<T>> prioritySort)
            : base(list)
        {
            _baseComparer = baseComparer;
            _prioritySort = prioritySort;
        }

        public void ResetSort()
        {
            _sortProp = null;
            _sortDir = ListSortDirection.Ascending;
            _isSorted = false;
        }

        /// <summary>
        /// ソート処理
        /// </summary>
        /// <param name="property">ソート項目</param>
        /// <param name="direction">ソート方向</param>
        protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
        {
            var list = this.Items as List<T>;
            if (list != null)
            {
              
                // 比較処理を実行します。
                IComparer<T> compare;
                if (_prioritySort != null && _prioritySort.ContainsKey(property.DisplayName))
                {
                    // 比較オブジェクトの取得
                    compare = direction == ListSortDirection.Ascending ? _prioritySort[property.DisplayName] : _prioritySort[property.DisplayName].Reverse();
                }
                else
                {
                    // 比較オブジェクト作成
                    compare = PropertyComparerFactory.Factory<T>(property, direction);
                }

                // 比較処理を実行します。
                list.Sort(_baseComparer == null ? compare : compare.Append(_baseComparer));


                // ソート処理の終了を設定
                this._isSorted = true;
                this._sortProp = property;
                this._sortDir = direction;

                this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }

        /// <summary>
        /// ソートのサポートを行う事を宣言
        /// </summary>
        protected override bool SupportsSortingCore
        {
            get { return true; }
        }
        /// <summary>
        /// 削除処理用
        /// </summary>
        protected override void RemoveSortCore()
        {
            
        }
        /// <summary>
        /// ソート済みかを示す値の取得用
        /// </summary>
        protected override bool IsSortedCore
        {
            get { return this._isSorted; }
        }

        /// <summary>
        /// ソート対象
        /// </summary>
        protected override PropertyDescriptor SortPropertyCore
        {
            get { return this._sortProp; }
        }
        /// <summary>
        /// ソート方向(昇順・降順)の保持を行います。
        /// </summary>
        protected override ListSortDirection SortDirectionCore
        {
            get { return this._sortDir; }
        }
    }



    /// <summary>
    /// ソート用のクラス
    /// </summary>
    public static class PropertyComparerFactory
    {
        public static IComparer<T> Factory<T>(PropertyDescriptor property, ListSortDirection direction)
        {
            var seed = typeof(PropertyComparer<,>);
            Type[] typeArgs = { typeof(T), property.PropertyType };
            var pcType = seed.MakeGenericType(typeArgs);
         
            var comparer = (IComparer<T>)Activator.CreateInstance(pcType, new object[] { property, direction });
            return comparer;
        }
    }

    /// <summary>
    /// 比較クラス
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="U"></typeparam>
    public sealed class PropertyComparer<T, U> : IComparer<T>
    {
        private readonly PropertyDescriptor _property;
        private readonly ListSortDirection _direction;
        private readonly Comparer<U> _comparer;

        public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
        {
            this._property = property;
            this._direction = direction;
            this._comparer = Comparer<U>.Default;
        }

        public int Compare(T x, T y)
        {
            var xValue = (U)this._property.GetValue(x);
            var yValue = (U)this._property.GetValue(y);


            if (this._direction == ListSortDirection.Ascending)
                return this._comparer.Compare(xValue, yValue);
            else
                return this._comparer.Compare(yValue, xValue);
        }
    }


 
  /// <summary>
    /// IComparer[T]補助関数
    /// </summary>
    public static class ComparerUtils
    {
        /// <summary>
        /// 動的にデリゲートから生成するIComparer[T]実装
        /// </summary>
        /// <typeparam name="T">比較対象の型</typeparam>
        private sealed class DynamicComparer<T> : IComparer<T>
        {
            private readonly Func<T, T, int> _compare;

            /// <summary>
            ///  2 つのオブジェクトを比較し、一方が他方より小さいか、等しいか、大きいかを示す値を返します。
            /// </summary>
            /// <param name="x">比較する最初のオブジェクトです。</param>
            /// <param name="y">比較する 2 番目のオブジェクト。</param>
            /// <returns>
            /// x と y の相対的な値を示す符号付き整数 (次の表を参照)。
            /// 値 : 説明
            /// number < 0 : x が y より小さい。
            /// 0 : x と y は等しい。
            /// 0 より大 : x が y より大きい。
            /// </returns>
            public int Compare(T x, T y)
            {

                return _compare(x, y);
            }

            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="compare">比較関数</param>
            public DynamicComparer(Func<T, T, int> compare)
            {
                this._compare = compare;
            }
        }

        /// <summary>
        /// 比較するキーを指定してComparerを生成します。
        /// </summary>
        /// <typeparam name="T">比較対象の型</typeparam>
        /// <typeparam name="TP">キーの型</typeparam>
        /// <param name="selector">比較対象からキーを取得する関数</param>
        /// <param name="comparer">キーのComparer</param>
        /// <returns>Comparer</returns>
        public static IComparer<T> By<T, TP>(Func<T, TP> selector, IComparer<TP> comparer)
        {
            return new DynamicComparer<T>((x, y) => comparer.Compare(selector(x), selector(y)));
        }

        /// <summary>
        /// 比較するキーを指定してComparerを生成します。比較にはComparer[TP].Defaultを使用します
        /// </summary>
        /// <typeparam name="T">比較対象の型</typeparam>
        /// <typeparam name="TP">キーの型</typeparam>
        /// <param name="selector">比較対象からキーを取得する関数</param>
        /// <returns>Comparer</returns>
        public static IComparer<T> By<T, TP>(Func<T, TP> selector)
            where TP : IComparable<TP>
        {
            return By(selector, Comparer<TP>.Default);
        }

        /// <summary>
        /// Comparerでの比較結果が同一だった場合に、第二引数のComparerで比較を行うようなComparerを生成します
        /// </summary>
        /// <typeparam name="T">比較対象の型</typeparam>
        /// <param name="comparer">優先する第一のComparer</param>
        /// <param name="second">第二のComparer</param>
        /// <returns>Comparer</returns>
        public static IComparer<T> Append<T>(this IComparer<T> comparer, IComparer<T> second)
        {
            return new DynamicComparer<T>((x, y) =>
            {
                var first = comparer.Compare(x, y);
                if (first != 0)
                {
                    return first;
                }
                return second.Compare(x, y);
            });
        }

        /// <summary>
        /// Comparerの比較順序を逆転させます
        /// </summary>
        /// <typeparam name="T">比較対象の型</typeparam>
        /// <param name="comparer">元となるComparer</param>
        /// <returns>Comparer</returns>
        public static IComparer<T> Reverse<T>(this IComparer<T> comparer)
        {
            return new DynamicComparer<T>((x, y) => comparer.Compare(y, x));
        }
    } 


    /// <summary>
    /// Enumerableの拡張メソッド
    /// </summary>
    public static class EnumerableExtensions
    {
        public static void ForEach<T>(this IEnumerable<T> source,Action<T> action)
        {
            foreach (T element in source)
                action(element);
        }

        public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
        {
            var index = 0;

            foreach (var elem in list)
                action(index++, elem);

            return index;
        }

        public static T With<T>(this T item, Action<T> action)
        {
            action(item);
            return item;
        }
    } 

使用方法

バインディング時に文字列ではなくて、プロパティ情報からプロパティ名を取得することで、変数名の変更に自動的に対応できる。

           this.rdbPlan.DataBindings.Add(new Binding("Checked", dto, PropertyUtils.GetPropertyName(dto, o => o.IsPLAN), false, DataSourceUpdateMode.OnPropertyChanged));

プロパティ情報の取得用のクラスです。
 

    public static class PropertyUtils
    {
        /// <summary>
        /// プロパティ名の取得を行います。
        /// </summary>
        /// <typeparam name="TSource">対象のオブジェクト</typeparam>
        /// <typeparam name="TProp">ラムダ式のプロパティ</typeparam>
        /// <param name="source">対象のオブジェクト</param>
        /// <param name="expression">ラムダ式のプロパティ</param>
        /// <returns>プロパティ名</returns>
        public static string GetPropertyName<TSource,TProp>(TSource source,Expression<Func<TSource, TProp>> expression)
        {

            var body = expression.Body as MemberExpression;
            if (body == null) throw new ArgumentException("'expression' should be a member expression");

            return body.Member.Name;

        }

        /// <summary>
        /// プロパティ情報名の取得を行います。
        /// </summary>
        /// <typeparam name="TSource">対象のオブジェクト</typeparam>
        /// <typeparam name="TProp">ラムダ式のプロパティ</typeparam>
        /// <param name="source">対象のオブジェクト</param>
        /// <param name="expression">ラムダ式のプロパティ</param>
        /// <returns>プロパティ名</returns>
        public static PropertyInfo GetPropertyInfo<TSource, TProp>(TSource source, Expression<Func<TSource, TProp>> expression)
        {
            return source.GetType().GetProperty(GetPropertyName(source, expression));
        }

        public static PropertyInfo GetPropertyInfo(object property, string propertyName)
        {
            var arrayProperties = property.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in arrayProperties)
            {
                if (propertyInfo.Name == propertyName)
                {
                    return propertyInfo;
                }
            }

            return null;
        } 
  } 

↑このページのトップヘ