项目总结-wpf DataGrid使用经验

340 阅读3分钟

项目总结-wpf 使用经验

刚做完一个使用了 wpf 技术的项目,总结一下项目中使用到的一些以前没用过的技术

DataGrid 过滤

nuget 库 DataGridExtensions 提供了自定义扩展 DataGrid 列过滤的方法

根据例子中的 MultipleChoiceFilter 可以自定义任意过滤。 我的日期过滤控件:

// xaml:

<Control x:Class="readLog.Views.Filters.DateFilter"

         x:Name="Control"

             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:readLog.Views.Filters"

             xmlns:dgx="urn:tom-englert.de/DataGridExtensions"

             xmlns:sys="clr-namespace:System;assembly=mscorlib"

             mc:Ignorable="d"

             d:DesignHeight="30" d:DesignWidth="30"

         >

    <Control.Resources>

        <ObjectDataProvider x:Key="dateTypeEnum" MethodName="GetValues"

                            ObjectType="{x:Type sys:Enum}">

            <ObjectDataProvider.MethodParameters>

                <x:Type TypeName="local:DateFilterType"></x:Type>

            </ObjectDataProvider.MethodParameters>

        </ObjectDataProvider>

    </Control.Resources>

    <Control.Template>

        <ControlTemplate>

            <Grid>

                <ToggleButton x:Name="ToggleButton">

                    <StackPanel Orientation="Horizontal">

                        <TextBlock x:Name="IsFilterActiveMarker" Text="." Margin="0,0,-4,0" Foreground="{Binding ElementName=FilterSymbol, Path=Foreground}" FontWeight="Bold" />

                        <Control x:Name="FilterSymbol" Style="{DynamicResource {x:Static dgx:DataGridFilter.IconStyleKey}}" />

                    </StackPanel>

                </ToggleButton>

                <Popup

                    DataContext="{Binding ElementName=Control}"

                       x:Name="Popup" IsOpen="{Binding Path=IsChecked, ElementName=ToggleButton, Mode=TwoWay}"

                       AllowsTransparency="True" StaysOpen="False">

                    <StackPanel>

                        <ComboBox x:Name="typeBox"

                                  ItemsSource="{Binding Source={StaticResource dateTypeEnum}}"

                                  SelectedItem="{Binding Path=FilterType}"/>

                        <DatePicker x:Name="begTm"

                                    Visibility="{Binding Path=BeginTimeVisible}"

                                    SelectedDate="{Binding Path=BeginTime}"

                                    />

                        <DatePicker x:Name="endTm"

                                    Visibility="{Binding Path=EndTimeVisible}"

                                    SelectedDate="{Binding Path=EndTime}"

                        />

                    </StackPanel>

                </Popup>

            </Grid>

        </ControlTemplate>

    </Control.Template>

</Control>
// 交互

    /// <summary>

    /// DateFilter.xaml 的交互逻辑

    /// </summary>

    public partial class DateFilter : Control

    {

        public DateFilter()

        {

            InitializeComponent();

        }

        #region dependency property

        public static readonly DependencyProperty FilterTypeProperty = DependencyProperty.Register(

            "FilterType", typeof(DateFilterType), typeof(DateFilter),

            new FrameworkPropertyMetadata(default(DateFilterType), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (s, e) => ((DateFilter)s).FilterTypeChange()));

        private void FilterTypeChange()

        {

            // 可见性

            switch (FilterType)

            {

                case DateFilterType.All:

                case DateFilterType.ThisYear:

                case DateFilterType.ThisMonth:

                case DateFilterType.PreYear:

                case DateFilterType.PreMonth:

                    BeginTimeVisible = Visibility.Collapsed;

                    EndTimeVisible = Visibility.Collapsed;

                    break;

                case DateFilterType.Earlier:

                    BeginTimeVisible = Visibility.Collapsed;

                    EndTimeVisible = Visibility.Visible;

                    break;

                case DateFilterType.Later:

                    BeginTimeVisible = Visibility.Visible;

                    EndTimeVisible = Visibility.Collapsed;

                    break;

                case DateFilterType.Between:

                    BeginTimeVisible = Visibility.Visible;

                    EndTimeVisible = Visibility.Visible;

                    break;

                default:

                    throw new ArgumentOutOfRangeException();

            }

            RangeChanged();

        }

        private void RangeChanged()

        {

            Filter = new ContentFilter(FilterType, BeginTime, EndTime);

        }

        public DateFilterType FilterType

        {

            get => (DateFilterType) GetValue(FilterTypeProperty);

            set => SetValue(FilterTypeProperty, value);

        }

        public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(

            "Filter", typeof(ContentFilter), typeof(DateFilter),

            new FrameworkPropertyMetadata(default(ContentFilter), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (s, e) => ((DateFilter)s).FilterChanged()));

        public ContentFilter Filter

        {

            get => (ContentFilter) GetValue(FilterProperty);

            set => SetValue(FilterProperty, value);

        }

        private void FilterChanged()

        {

            var filter = Filter as ContentFilter;

            if (null == filter)

            {

                return;

            }

            FilterType = filter.FilterType;

            BeginTime = filter.BeginTime;

            EndTime = filter.EndTime;

        }

        public static readonly DependencyProperty BeginTimeProperty = DependencyProperty.Register(

            "BeginTime", typeof(DateTime), typeof(DateFilter),

            new FrameworkPropertyMetadata(DateTime.Today, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,(s, e) => ((DateFilter)s).RangeChanged()));

        public DateTime BeginTime

        {

            get => (DateTime) GetValue(BeginTimeProperty);

            set => SetValue(BeginTimeProperty, value);

        }

        public static readonly DependencyProperty EndTimeProperty = DependencyProperty.Register(

            "EndTime", typeof(DateTime), typeof(DateFilter),

            new FrameworkPropertyMetadata(DateTime.Today, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,(s, e) => ((DateFilter)s).RangeChanged()));

        public DateTime EndTime

        {

            get => (DateTime) GetValue(EndTimeProperty);

            set => SetValue(EndTimeProperty, value);

        }

        public static readonly DependencyProperty PropertyTypeProperty = DependencyProperty.Register(

            "BeginTimeVisible", typeof(Visibility), typeof(DateFilter),

            new FrameworkPropertyMetadata(Visibility.Collapsed, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (s, e) => ((DateFilter)s).RangeChanged()));

        public Visibility BeginTimeVisible

        {

            get => (Visibility) GetValue(PropertyTypeProperty);

            set => SetValue(PropertyTypeProperty, value);

        }

        public static readonly DependencyProperty EndTimeVisibleProperty = DependencyProperty.Register(

            "EndTimeVisible", typeof(Visibility), typeof(DateFilter),

            new FrameworkPropertyMetadata(Visibility.Collapsed, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (s, e) => ((DateFilter)s).RangeChanged()));

        public Visibility EndTimeVisible

        {

            get => (Visibility) GetValue(EndTimeVisibleProperty);

            set => SetValue(EndTimeVisibleProperty, value);

        }

        #endregion

        public class ContentFilter : IContentFilter

        {

            public ContentFilter(DateFilterType type, DateTime beg, DateTime end)

            {

                FilterType = type;

                BeginTime = beg;

                EndTime = end;

            }

            public bool IsMatch(object value)

            {

                if (null == value)

                {

                    return false;

                }

                try

                {

                    var tm = (DateTime) value;

                    switch (FilterType)

                    {

                        case DateFilterType.All:

                            return true;

                        case DateFilterType.ThisYear:

                            return IsMatchYear(tm, DateTime.Today);

                        case DateFilterType.ThisMonth:

                            return IsMatchMonth(tm, DateTime.Today);

                        case DateFilterType.PreYear:

                            return IsMatchYear(tm, DateTime.Today.AddYears(-1));

                        case DateFilterType.PreMonth:

                            return IsMatchMonth(tm, DateTime.Today.AddMonths(-1));

                        case DateFilterType.Earlier:

                            return tm.Date <= EndTime.Date;

                        case DateFilterType.Later:

                            return tm.Date >= BeginTime.Date;

                        case DateFilterType.Between:

                            return IsMatchDate(tm, BeginTime, EndTime);

                        default:

                            throw new ArgumentOutOfRangeException();

                    }

                }

                catch (Exception e)

                {

                    return false;

                }

            }

            public DateFilterType FilterType { get; }

            public DateTime BeginTime { get; }

            public DateTime EndTime { get; }

            #region Datetime

            private bool IsMatchMonth(DateTime tm, DateTime toMatch)

            {

                return IsMatchDate(tm, GetFirstDayOfMonth(toMatch), GetLastDayOfMonth(toMatch));

            }

            private bool IsMatchDate(DateTime tm, DateTime beg, DateTime end)

            {

                tm = tm.Date;

                return tm <= beg && tm >= end;

            }

            private bool IsMatchYear(DateTime tm, DateTime toMatch)

            {

                return IsMatchDate(tm, GetLastDayOfYear(toMatch), GetFirstDayOfYear(toMatch));

            }

            private DateTime GetFirstDayOfMonth(DateTime dt)

            {

                var dtFrom = dt;

                dtFrom = dtFrom.AddDays(-(dtFrom.Day - 1));

                return dtFrom;

            }

            private DateTime GetLastDayOfMonth(DateTime dt)

            {

                var dtTo = dt;

                dtTo = dtTo.AddMonths(1);

                dtTo = dtTo.AddDays(-(dtTo.Day));

                return dtTo;

            }

            private DateTime GetFirstDayOfYear(DateTime dt)

            {

                var dtFrom = dt;

                dtFrom = dtFrom.AddMonths(-(dtFrom.Month - 1));

                return GetFirstDayOfMonth(dtFrom);

            }

            private DateTime GetLastDayOfYear(DateTime dt)

            {

                var dtTo = dt;

                dtTo = dtTo.AddYears(1);

                dtTo = dtTo.AddMonths(-dt.Month);

                return GetLastDayOfMonth(dtTo);

            }

            #endregion

        }

    }

    [TypeConverter(typeof(EnumDescriptionTypeConverter))]

    public enum DateFilterType

    {

        [Description("所有")]

        All, // 全部

        [Description("今年")]

        ThisYear,

        [Description("本月")]

        ThisMonth,

        [Description("去年")]

        PreYear,

        [Description("上个月")]

        PreMonth,

        [Description("指定日期以前")]

        Earlier,

        [Description("指定日期以后")]

        Later,

        [Description("之间")]

        Between,

    }

枚举显示

枚举显示有两个方案,

  1. 转换器, 在 xaml 中定义一个转换器资源
  2. 定义一个用来显示的字符串属性,界面绑定这个字符串属性。

读取 DataGrid 某一行某一列的值

从控件 DataGridExtensions 中学习到的。拿到的是真实的绑定对象,而不是显示的字符串。一开始用 GetCelllContent(),但对未显示的行无法取到。

        class GetCellValueClass : DependencyObject

        {

            /// <summary>

            /// Identifies the CellValue dependency property, a private helper property used to evaluate the property path for the list items.

            /// </summary>

            private static readonly DependencyProperty _cellValueProperty =

                DependencyProperty.Register("_cellValue", typeof(object), typeof(GetCellValueClass));

            public object GetCellValue(DataGridColumn col, object item)

            {

                var propertyPath = col.SortMemberPath;

                if (string.IsNullOrEmpty(propertyPath))

                    return null;

                BindingOperations.SetBinding(this, _cellValueProperty, new Binding(propertyPath) { Source = item });

                var propertyValue = GetValue(_cellValueProperty);

                BindingOperations.ClearBinding(this, _cellValueProperty);

                return propertyValue;

            }

        }

使用方法: new GetCellValueClass().GetCellValue(col, item);


文章写于 2019-03-26, 于2023-03-21 迁移到掘金。