将WPF绑定源设置为DataGrid列

时间:2019-01-10 16:03:32

标签: c# wpf mvvm data-binding wpfdatagrid

我有一个WPF DataGrid,其中有18列,每列上方都有一个TextBox,因此我可以过滤该列。

每个TextBoxWidth绑定到列的ActualWidth

<StackPanel Grid.Row="0" Orientation="Horizontal">
    <TextBox Width="{Binding Path=ActualWidth, ElementName=Column1}" Text="{Binding FilterFirstName}"/>
    <TextBox Width="{Binding Path=ActualWidth, ElementName=Column2}" Text="{Binding FilterLastName}"/>
    <TextBox Width="{Binding Path=ActualWidth, ElementName=Column3}" Text="{Binding FilterAge}"/>
    <!-- 15 more -->
</StackPanel>
<DataGrid x:Name="dataGridUsers" Grid.Row="1" ItemsSource="{Binding Users}">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Column1" Width="*" Binding="{Binding FirstName}"/>
        <DataGridTextColumn x:Name="Column2" Width="*" Binding="{Binding LastName}"/>
        <DataGridTextColumn x:Name="Column3" Width="*" Binding="{Binding Age}"/>
        <!-- 15 more -->
    </DataGrid.Columns>
</DataGrid>

我知道我可以像这样将TextBox Text绑定到List<string>上:

<TextBox Width="{Binding Path=ActualWidth, ElementName=Column1}" Text="{Binding Filters[0]}"/>
<TextBox Width="{Binding Path=ActualWidth, ElementName=Column2}" Text="{Binding Filters[1]}"/>
<TextBox Width="{Binding Path=ActualWidth, ElementName=Column3}" Text="{Binding Filters[2]}"/>

我想将Width的{​​{1}}绑定到该列的TextBox,如下所示:

ActualWidth

因为这样我可以使用<TextBox Width="{Binding Path=ActualWidth, Source=dataGridUsers.Columns[0]}" Text="{Binding Filters[0]}"/> <TextBox Width="{Binding Path=ActualWidth, Source=dataGridUsers.Columns[1]}" Text="{Binding Filters[1]}"/> <TextBox Width="{Binding Path=ActualWidth, Source=dataGridUsers.Columns[2]}" Text="{Binding Filters[2]}"/> 代替ItemsControl,但是这种方式行不通

还有其他方法可以实现这一目标吗?

3 个答案:

答案 0 :(得分:2)

您无法绑定到DataGrid列属性,因为它们仅在逻辑树中,而不在可视树中。 这样做的唯一方法是更改​​DataGridTextColumn.HeaderTemplate并使用其中的过滤器DataTemplate创建一个新的TextBox

答案 1 :(得分:1)

这样做确实可以绑定到t的列

DataGrid

但这对您想要实现的目标无效。在运行时对列进行重新排序后,该顺序将不再与TextBoxes的顺序匹配。所以你也必须重新排序。
注意:DataGridColumn.DisplayIndex返回<TextBox Width="{Binding Columns[0].ActualWidth, ElementName=dataGridUsers}" /> 中的当前索引。


更好的方法和推荐的方法是通过更改DataGrid

将“ FilterTextBoxes”放置在列标题中

答案 2 :(得分:0)

如何结合使用ItemsControlTextBox来过滤DataGrid列

可以像这样使用ItemsControl并绑定到DataGrid.Columns

<ItemsControl Grid.Row="0" ItemsSource="{Binding Path=Columns, ElementName=dataGrid}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Width="{Binding ActualWidth}">
                <TextBox.Resources>
                    <local:ListIndexToValueConverter x:Key="listIndexToValueConverter"/>
                </TextBox.Resources>
                <TextBox.Text>
                    <MultiBinding Converter="{StaticResource listIndexToValueConverter}" UpdateSourceTrigger="PropertyChanged">
                        <Binding Path="DataContext.Filters" ElementName="userControl"/>
                        <Binding Path="DisplayIndex"/>
                    </MultiBinding>
                </TextBox.Text>
            </TextBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
<DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding Users}">
    <DataGrid.Columns>
        <DataGridTextColumn DisplayIndex="0" Width="*" Binding="{Binding FirstName}"/>
        <DataGridTextColumn DisplayIndex="1" Width="*" Binding="{Binding LastName}"/>
        <DataGridTextColumn DisplayIndex="2" Width="*" Binding="{Binding Age}"/>
    </DataGrid.Columns>
</DataGrid>

因为您将ItemsControl.ItemsSource设置为DataGrid.Columns而不是DataContext.Filters,所以必须设置DataGridColumn.DisplayIndex并使用IMultiValueConverter才能访问{{1} }:

DataContext.Filters

ViewModel:

public class ListIndexToValueConverter : IMultiValueConverter
{
    private IList _list;
    private int _index;

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length < 2)
            return Binding.DoNothing;

        if (values[0] is IList && values[1] is int)
        {
            _list = (IList)values[0];
            _index = (int)values[1];

            return _list[_index];
        }

        return Binding.DoNothing;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        _list[_index] = value;

        return new object[] { Binding.DoNothing, Binding.DoNothing };
    }
}