目录
介绍
本文和代码片段展示了DataGrid的RowDetails、Grouping和Filter如何使用xml文件作为数据源。
背景
该项目基于MS Learn示例。
使用代码
概述
- 具有分组和过滤器的DataGrid的基本源代码来自MS Learn示例[1] How to: Group, sort, and filter Data in the DataGrid control - WPF .NET Framework | Microsoft Learn
- 对于MVVM,该示例需要完全重新设计
- RowDetails区域的扩展器。RowDetails的灵感来自[2] How to: Add Row Details to a DataGrid Control - WPF .NET Framework | Microsoft Learn
- 如何触发超链接摘自[3] c# - Example using Hyperlink in WPF - Stack Overflow
- 从XML文件读取/写入数据时,使用强类型的MainDataSet.xsd
- MS Learn 示例用于过滤选中/未选中的任务的方法激发了我创建一个简单的文本搜索的灵感。
- RichTextBox 和RichTextBoxFormatBar 由Xceed Extended.Wpf.Toolkit GitHub提供支持——xceedsoftware/wpftoolkit:WPF中缺少的所有控件。超过100万次下载。从v4.0.0开始,此免费工具包根据 Xceed社区许可协议(用于非商业用途)提供。
分组和筛选
在MS Learn示例[1]中进行了详细介绍和解释。
我添加了“简单文本搜索”和“添加新行”按钮。
RowDetails 区域
这是在ResourceDictionary中定义的。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridUC1"
xmlns:local2="clr-namespace:DataGridUC1.Controls"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ViewModel="clr-namespace:DataGridUC1.ViewModel">
<Style x:Key="DataGridCellStyle"
TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="DataGridPlusRowDetailsTemplate">
<StackPanel HorizontalAlignment="Stretch"
Height="225" Orientation="Vertical" Width="NaN" Margin="31,0,0,0"
Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}">
<Label Content="Group" HorizontalAlignment="Left"
FontSize="14" FontWeight="Bold" />
<TextBox x:Name="Item"
Text="{Binding Item, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Margin="5,1,3,2"
IsEnabled="True" ToolTip="Item"
HorizontalAlignment="Left" MinWidth="50" />
<Label Content="Note" HorizontalAlignment="Left"
FontSize="14" FontWeight="Bold" />
<TextBox x:Name="Note"
Margin="5,1,3,2"
Text="{Binding Note, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ToolTip="Note" IsEnabled="True" MinWidth="50" />
<StackPanel Orientation="Horizontal" Height="32"
VerticalAlignment="Stretch"
HorizontalAlignment="Left" Width="500" >
<Label Content="Check" HorizontalAlignment="Left"
VerticalAlignment="Bottom" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
FontSize="14" FontWeight="Bold" />
<CheckBox
Margin="10,8,3,2"
IsChecked="{Binding Check, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ToolTip="CheckBox" IsEnabled="True"
AutomationProperties.HelpText="Check"
HorizontalAlignment="Left" VerticalAlignment="Center"
VerticalContentAlignment="Center" />
<Label Content="Rating" HorizontalAlignment="Center"
VerticalAlignment="Bottom" HorizontalContentAlignment="Right"
VerticalContentAlignment="Center" Width="184"
FontSize="14" FontWeight="Bold" />
<ComboBox
Margin="22,10,3,2"
Text="{Binding Rating, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ToolTip="ComboBox"
IsEnabled="True"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="88" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" >
<ComboBoxItem Content="Average"/>
<ComboBoxItem Content="Good"/>
<ComboBoxItem Content="Excellent"/>
</ComboBox>
</StackPanel>
<Label Content="Link" HorizontalAlignment="Left"
FontSize="14" FontWeight="Bold" />
<TextBlock FontFamily="Segoe UI" FontSize="16">
<Hyperlink NavigateUri="{Binding Text, ElementName=LinkTB,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
local2:HyperlinkExtensions.IsExternal="true">
--> Click here to fire the hyperlink
</Hyperlink>
</TextBlock>
<TextBox x:Name="LinkTB"
Margin="5,1,3,2"
Text="{Binding Link, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ToolTip="Link" IsEnabled="True"
Foreground="{DynamicResource {x:Static
SystemColors.InfoTextBrushKey}}"
Background="{DynamicResource {x:Static
SystemColors.ControlLightLightBrushKey}}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
“行详细信息”包含用于编辑当前行的文本框和用于触发相关超链接的“测试”按钮。
超链接扩展
此扩展基于[3],并在Row Details区域中使用,如上所述。
namespace DataGridUC1.Controls
{
// https://stackoverflow.com/questions/10238694/example-using-hyperlink-in-wpf
public static class HyperlinkExtensions
{
public static bool GetIsExternal(DependencyObject obj)
{
return (bool)obj.GetValue(IsExternalProperty);
}
public static void SetIsExternal(DependencyObject obj, bool value)
{
obj.SetValue(IsExternalProperty, value);
}
public static readonly DependencyProperty IsExternalProperty =
DependencyProperty.RegisterAttached("IsExternal", typeof(bool),
typeof(HyperlinkExtensions),
new UIPropertyMetadata(false, OnIsExternalChanged));
private static void OnIsExternalChanged(object sender,
DependencyPropertyChangedEventArgs args)
{
var hyperlink = sender as Hyperlink;
if ((bool)args.NewValue)
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
else
hyperlink.RequestNavigate -= Hyperlink_RequestNavigate;
}
private static void Hyperlink_RequestNavigate(object sender,
System.Windows.Navigation.RequestNavigateEventArgs e)
{
//Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
//e.Handled = true;
// https://www.codeproject.com/Questions/5380961/How-do-I-fix-net-8-process-start-url-issue
Hyperlink link = (Hyperlink)e.OriginalSource;
Process? process = Process.Start(new ProcessStartInfo(link.NavigateUri.AbsoluteUri)
{
UseShellExecute = true
});
process!.WaitForExit();
e.Handled = true;
}
}
}
文本搜索和过滤
MS Learn示例用于过滤选中/未选中的任务的方法激发了我创建以下超级简单的文本搜索。
使用FilterEventArgs,我们获得每个任务/行的DataRowView,这允许我们对该方法使用简单的if语句。因此,逻辑存在于VM中。
MVVM
为了将对象/控件从 View 传递给 ViewModel,我们使用Interaction.Triggers和ParameterCommand。
ViewModel 还包含用于读取/写入XML数据的逻辑。
private void LoadXML()
{
_ds.Clear();
_ds.ReadXml(_data.FullName);
}
// ds.WriteXml(path);
private void WriteXML()
{
_ds.AcceptChanges();
_ds.WriteXml(path);
MessageBox.Show("xml data saved. ");
}
筛选器和文本搜索的逻辑
private void CompleteFilter_Changed(object sender, RoutedEventArgs e)
{
if (sender != null)
{
cbCompleteFilter = (bool)((CheckBox)sender).IsChecked;
}
// Refresh the view to apply filters.
cvs.View.Refresh();
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
//Task t = e.Item as Task;
DataRowView drv = e.Item as DataRowView;
if (e.Item != null)
{
drv = (DataRowView)e.Item;
if (drv != null && cbCompleteFilter != null)
// If filter is turned on, filter completed items.
{
// if (this.cbCompleteFilter.IsChecked == true && t.Complete == true)
if (this.cbCompleteFilter == true && (bool)drv.Row["Check"] == true)
{
e.Accepted = false;
}
else
e.Accepted = true;
}
}
}
private void SearchBox_Changed(object sender, RoutedEventArgs e)
{
if (sender != null)
{
searchBox = (TextBox)sender;
}
// Refresh the view to apply filters.
cvs.View.Refresh();
}
private void CollectionViewSource_Search(object sender, FilterEventArgs e)
{
DataRowView drv = e.Item as DataRowView;
if (e.Item != null)
{
drv = (DataRowView)e.Item;
if (drv != null && searchBox != null
&& this.cbCompleteFilter == false)
{
if (drv.Row["Item"].ToString().ToLower().Contains(searchBox.Text.ToLower()) == false
&& drv.Row["Note"].ToString().ToLower().Contains(searchBox.Text.ToLower()) == false)
{
e.Accepted = false;
}
else
e.Accepted = true;
}
if (drv != null && searchBox != null
&& this.cbCompleteFilter == true)
{
if (drv.Row["Item"].ToString().ToLower().Contains(searchBox.Text.ToLower()) == false
&& drv.Row["Note"].ToString().ToLower().Contains(searchBox.Text.ToLower()) == false
|| (bool)drv.Row["Check"] == true)
{
e.Accepted = false;
}
else
e.Accepted = true;
}
}
}
Properties、ICommands和ParameterCommands始终以相同的方式工作,因此在下面显示一个示例就足够了。
集合视图和参数命令示例
我们使用文本搜索功能使其更清晰。
我们在XAML文件中使用Interaction.Triggers。这意味着每次当CollectionView是Refreshed时,都会处理过滤器。
当我们将Ds.Credits (表名)绑定为CollectinViewSource时,我们从XML文件中获取数据。
CollectionViewType是ListCollectionView。
<CollectionViewSource x:Key="cvsTasks" Source="{Binding Ds.Credits}"
CollectionViewType="ListCollectionView" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Item"/>
<scm:SortDescription PropertyName="Check" />
<!--<scm:SortDescription PropertyName="DueDate" />-->
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Item"/>
<PropertyGroupDescription PropertyName="Check"/>
</CollectionViewSource.GroupDescriptions>
<b:Interaction.Triggers>
<b:EventTrigger EventName= "Filter">
<b:InvokeCommandAction Command="{Binding
ParameterCmdFilter, Mode=OneWay}"
CommandParameter="{Binding cvsTasks,
RelativeSource={RelativeSource
AncestorType={x:Type CollectionViewSource}}}"
PassEventArgsToCommand="True" />
</b:EventTrigger>
<b:EventTrigger EventName= "Filter">
<b:InvokeCommandAction Command="{Binding
ParameterCmdSearch, Mode=OneWay}"
CommandParameter="{Binding cvsTasks,
RelativeSource={RelativeSource
AncestorType={x:Type CollectionViewSource}}}"
PassEventArgsToCommand="True" />
</b:EventTrigger>
</b:Interaction.Triggers>
</CollectionViewSource>
对于文本搜索,使用EventTrigger EventName= “Filter”,将调用Command ParameterCmdSearch,并且CommandParameter是cvsTasks(源Ds.Credits的密钥)。
然后,ParameterCmdSearch调用CollectionViewSource_Search。因此,我们将每个DataRow作为参数获取。
搜索TextBox的内容与另一个参数命令一起传递。
搜索string被转换ToLower,因此我们忽略UpperCase。
<TextBox x:Name="SearchBox"
Margin="5,1,3,2"
IsEnabled="True" ToolTip="Item" HorizontalAlignment="Left"
MinWidth="120" FontSize="14" AcceptsReturn="True"
MaxLines="1" >
<b:Interaction.Triggers>
<b:EventTrigger EventName= "TextChanged">
<b:InvokeCommandAction Command="{Binding
ParameterCmdSearchBox, Mode=OneWay}"
CommandParameter="{Binding ElementName= SearchBox,
Mode=OneWay}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</TextBox>
使用应用程序
当您启动应用程序时,DataGrid 应填充Credits。
选择一行后,RowDetails将展开。
您可以在RowDetails区域中编辑当前行,并测试触发超链接。
DataGrid 下方的按钮是:
使用“删除组”,您可以获得正常的 DataGrid 装备。
按组/状态分组可恢复分组装备。
“添加新行”和“保存积分”按其名称所示执行。
可以同时使用文本搜索框和过滤掉选中的项目(用于选中/未选中的行)。
可以使用上下文菜单进行复制和粘贴。
上下文菜单
DataGrid的上下文菜单提供了一些额外的命令/功能,例如切换“组”列可见性或撤销/重做,用于在当前行中进行的编辑。
在RowDetails上的RichTextBox有自己的上下文菜单,该菜单在右键单击时显示。
RichTextBoxFormatBar
以下文字摘自RichTextBoxFormatBar · xceedsoftware/wpftoolkit Wiki ·GitHub的
“RichTextBoxFormatBar是一个上下文格式工具栏,它模仿了Microsoft Office 2010格式设置栏的行为。可以使用RichTextBoxFormatBarManager将其附加到任何Richtextbox控件。您甚至可以创建自己的格式条并改用它,但仍然具有RichTextboxFormatBarManager提供的所有功能。
"RichTextBoxFormatBar是一个上下文文本格式设置工具栏,它将对RichTextBox控件的选定文本应用文本转换。当用户处于选择过程中时,RichTextBoxFormatBar将在选择后释放鼠标时出现。RichTextBoxFormatBar也将在最后一次单击“双击”选择时出现。当显示RichTextFormatBar时,您可以单击要应用于所选文本的任意数量的文本转换。"
致谢/参考资料
- [1]带有分组和过滤器的DataGrid的基本源代码来自MS Learn示例How to: Group, sort, and filter Data in the DataGrid control - WPF .NET Framework | Microsoft Learn
- [2]RowDetails区域的扩展器。RowDetails的灵感来自How to: Add Row Details to a DataGrid Control - WPF .NET Framework | Microsoft Learn
- [3]如何触发超链接摘自c# - Example using Hyperlink in WPF - Stack Overflow
- [4] 将ICommand与MVVM模式结合使用 - CodeProject
- [5] MVVM工具包简介——Windows Community Toolkit |Microsoft 文档
- [6] Xceed Extended.Wpf.ToolkitGitHub - xceedsoftware/wpftoolkit:WPF中缺少的所有控件。超过100万次下载。从v4.0.0开始,此免费工具包根据Xceed社区许可协议(用于非商业用途)提供。
https://www.codeproject.com/Tips/5381772/WPF-DataGrid-with-RichText-RowDetails-Grouping-Fil