项目总结-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,
}
枚举显示
枚举显示有两个方案,
- 转换器, 在 xaml 中定义一个转换器资源
- 定义一个用来显示的字符串属性,界面绑定这个字符串属性。
读取 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 迁移到掘金。