C# Window.Resources中的WPF DataTemplate绑定参数

C# Window.Resources中的WPF DataTemplate绑定参数,c#,wpf,xaml,mvvm,datagrid,C#,Wpf,Xaml,Mvvm,Datagrid,我正在创建一个datagrid,列标题中有过滤器。这是可行的,但我认为这不是一个好办法。让我向您展示代码,非常简单的示例: 视图 <Window x:Class="TestDataGridApp.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/

我正在创建一个datagrid,列标题中有过滤器。这是可行的,但我认为这不是一个好办法。让我向您展示代码,非常简单的示例:

视图

<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>
基本上是简单的datagrid,2列。在每列中都有一个带有绑定过滤器的
文本框。每个过滤器都有自己的字段,因此在焦点丢失后,我可以使用所有过滤器过滤网格

我的问题是。。我有很多专栏。这是定制的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>
但是,我必须等待上下文。。一个文本框。我真的不确定这个解决方案

我的问题是,如何在上述场景中为DataTemplate创建参数?

谢谢你的帮助


另外,这不是重复的。这个问题是关于“如何为DataTemplate创建参数”。“重复”问题是关于词典作为一个绑定-这个问题的一个潜在解决方案。。虽然可能不是。正如另一位用户所建议的,可能有完全不同的更好的解决方案来解决这个问题。两件不同的事情。我感到震惊的是,我不得不解释这一点,最简单的方法是不要只依赖xaml,还要添加一些代码来帮助。例如,使用
TextBox
Loaded
事件,如下所示:

<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>

您可以使用attached属性来实现相同的功能,但我认为在这种情况下不需要它。

我使用了Evk解决方案和
DependencyProperty
而不是使用

<controls:FilterDataGridTextColumn FilterName="Name" Header="Name" Binding="{Binding Path=Name}" Width="200" HeaderTemplate="{StaticResource HeaderTemplate}" />

可能是@Rekshino的复制品你是认真的吗?这不是复制品。这里我问的是“如何为DataTemplate创建参数”,另一个问题是关于“作为绑定的字典”。这个问题的答案可能与词典毫无关系。你读过这两个问题吗?谢谢!我没有想到这种方法。但这可能奏效!尽管如此,我可能无法使用
parent.Column.Header
,因为它可能包含空格、括号等。它肯定会起作用,因为我在发布之前对它进行了测试:)至于Header-您可以使用任何其他属性(可能是绑定路径)或创建附加属性,并将其用于网格列以提供明确的筛选器名称。我可以张贴一个例子,如果你有一些麻烦,使用附加属性为这个。为什么附加属性?我使用了您的解决方案,但使用了简单的
DependencyProperty
我将此作为答案发布。你能评论一下这是否正确吗?它起作用了。。我只是不明白为什么要附财产?@LouisaBickley是的,这也是正确的。我考虑的是附加属性,因为有了它们,您可以使用额外的元数据扩展默认类(比如使用过滤器名称扩展DataGridColumn)。在您的情况下,确实可以从列继承,这并不比使用附加属性更糟糕(甚至更好)。
<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>
<TextBox x:Name="Foo" DockPanel.Dock="Top" Text="{Binding DataContext.FiltersDictionary[Foo], RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
<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);
}
<controls:FilterDataGridTextColumn FilterName="Name" Header="Name" Binding="{Binding Path=Name}" Width="200" HeaderTemplate="{StaticResource HeaderTemplate}" />
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); }
    }
}