我正在创建一个数据网格,列标题中包含过滤器。它有效,但我认为这不是一个好方法。让我告诉你代码,非常简单的例子:
视图
<Window x:Class="TestDataGridApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:TestDataGridApp.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Window.DataContext>
<viewModels:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="DataGridHeader">
<DockPanel>
<TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterName, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
视图模型
namespace TestDataGridApp.ViewModels
{
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using TestDataGridApp.Entities;
using Prism.Mvvm;
public class MainWindowViewModel : BindableBase
{
private string _filterId;
private string _filterName;
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public MainWindowViewModel()
{
for (int i = 1; i <= 100; ++i)
{
Items.Add(new Item() {Id = i, Name = $"Item{i}"});
}
}
public string FilterId
{
get { return _filterId; }
set
{
SetProperty(ref _filterId, value);
TriggerFilters();
}
}
public string FilterName
{
get { return _filterName; }
set
{
SetProperty(ref _filterName, value);
TriggerFilters();
}
}
public ObservableCollection<Item> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
public ICollectionView ItemCollection => CollectionViewSource.GetDefaultView(Items);
private void TriggerFilters()
{
ItemCollection.Filter = o => FilterItem((Item)o);
}
private bool FilterItem(Item item)
{
try
{
bool checkId = false;
bool checkName = false;
int itemId = 0;
if (!string.IsNullOrEmpty(FilterId) && int.TryParse(FilterId, out itemId)) checkId = true;
if (!string.IsNullOrEmpty(FilterName)) checkName = true;
if (!checkId && !checkName) return true;
if (item == null) return false;
bool checkIdIsOk = (checkId && item.Id == int.Parse(FilterId) || !checkId);
bool checkNameIsOk = (checkName && item.Name.ToUpper().Contains(FilterName.ToUpper()) || !checkName);
if (checkIdIsOk && checkNameIsOk) return true;
}
catch (Exception e)
{
Console.WriteLine(e);
}
return false;
}
}
}
项目
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
基本上简单的数据网格,2列。在每列中都有一个带有绑定过滤器的TextBox
。每个过滤器都有自己的字段,因此在失去焦点后,我可以通过所有过滤器过滤网格。
我的问题是......我有很多专栏。这是自定义的datagrid,因此您可以动态添加和删除列,并且有很多重复的代码。基本上这是重复的:
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<DockPanel>
<TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<TextBox DockPanel.Dock="Top"
Text="{Binding DataContext.FilterId, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
</DockPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
...只有这个<TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterId, ...
正在针对不同的列进行更改。
所以,我想,我可以轻松地用这个解决方案替换它,但是现在..我在ViewModel中失去了对我的过滤器字段的绑定:
<Window.Resources>
<DataTemplate x:Key="DataGridHeader">
<DockPanel>
<TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterName, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
SOO ..我在想,为过滤器创建一个Dictionary
,其中key是列的名称,在值中我将存储当前过滤器(如果此时没有过滤器,则为null这一栏)。像...这样的东西。
<TextBox x:Name="Foo" DockPanel.Dock="Top" Text="{Binding DataContext.FiltersDictionary[Foo], RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
但是我必须将Biding上下文...用于一个TextBox。我真的不确定这个解决方案..
我的问题是,如何在上述场景中为DataTemplate创建参数?
感谢您的帮助!
PS。这不是重复的。这个问题是关于“如何为DataTemplate创建参数”。 “重复”的问题是关于字典作为一种约束 - 这个问题的潜在解决方案......虽然可能不是。正如另一位用户建议的那样,解决这个问题可能会有完全不同的更好的解决方两件不同的事情。我很震惊,我必须解释这个
答案 0 :(得分:2)
最简单的方法是不仅仅依赖于xaml并添加一些代码来提供帮助。例如,使用Loaded
这样的TextBox
事件:
<DataTemplate x:Key="DataGridHeader">
<DockPanel>
<TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<TextBox DockPanel.Dock="Top" Loaded="OnFilterBoxLoaded" />
</DockPanel>
</DataTemplate>
加载时设置绑定:
private void OnFilterBoxLoaded(object sender, RoutedEventArgs e) {
var tb = (TextBox)sender;
// find column
DataGridColumnHeader parent = null;
DependencyObject current = tb;
do {
current = VisualTreeHelper.GetParent(current);
parent = current as DataGridColumnHeader;
}
while (parent == null);
// setup binding
var binding = new Binding();
// use parent column header as name of the filter property
binding.Path = new PropertyPath("DataContext.Filter" + parent.Column.Header);
binding.Source = this;
binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
tb.SetBinding(TextBox.TextProperty, binding);
}
您可以使用附加属性来实现相同的目标,但我不认为在这种情况下需要它。
答案 1 :(得分:1)
我使用DependencyProperty
的Evk解决方案,而不是使用Header
<controls:FilterDataGridTextColumn FilterName="Name" Header="Name" Binding="{Binding Path=Name}" Width="200" HeaderTemplate="{StaticResource HeaderTemplate}" />
FilterDataGridTextColumn:
public class FilterDataGridTextColumn : DataGridTextColumn
{
public static readonly DependencyProperty FilterNameProperty =
DependencyProperty.Register("FilterName", typeof(string), typeof(FilterDataGridTextColumn));
public string FilterName
{
get { return (string) GetValue(FilterNameProperty); }
set { SetValue(FilterNameProperty, value); }
}
}