简体   繁体   English

将WPF绑定源设置为DataGrid列

[英]Set WPF Binding Source to DataGrid Columns

I have a WPF DataGrid with 18 columns and each column has a TextBox above it so I can filter the column. 我有一个18列的WPF DataGrid ,每列都有一个TextBox ,所以我可以过滤列。

Each TextBox binds Width to ActualWidth of the column. 每个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>

I know that I can bind TextBox Text to a List<string> like this: 我知道我可以将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]}"/>

I would like to bind Width of the TextBox to ActualWidth of the column with something like this: 我想将TextBox Width绑定到列的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]}"/>

because then I could use ItemsControl instead of StackPanel but it doesn't work this way . 因为那时我可以使用ItemsControl而不是StackPanel它不能以这种方式工作

Is there any other way I could achieve this? 有没有其他方法可以达到这个目的?

You cant bind to DataGrid Columns properties because they are only in the logical tree - not the visual tree. 您无法绑定到DataGrid Columns属性,因为它们仅位于逻辑树中 - 而不是可视树。 The only way to do something like this is to change the DataGridTextColumn.HeaderTemplate and create a new DataTemplate with the filter TextBox within. 执行此类操作的唯一方法是更改DataGridTextColumn.HeaderTemplate并使用其中的过滤器TextBox创建新的DataTemplate

You can indeed bind to the Columns of your DataGrid by doing so 您确实可以通过这样做绑定到DataGrid的Columns

<TextBox Width="{Binding Columns[0].ActualWidth, ElementName=dataGridUsers}" />

But this won't work for what you want to achieve. 但这对你想要实现的目标不起作用。 As soon as you reorder the columns at runtime the order won't match the order of your TextBoxes anymore. 只要在运行时重新排序列,订单就不再符合TextBoxes的顺序。 So you have to reorder them too. 所以你必须重新排序它们。
Note: DataGridColumn.DisplayIndex returns the current index within your DataGrid . 注: DataGridColumn.DisplayIndex返回你的内当前索引DataGrid


A much better approach and the recommended way would be placing your "FilterTextBoxes" inside the headers of your columns by changing the DataGridColumn.HeaderTemplate 一种更好的方法和推荐的方法是通过更改DataGridColumn.HeaderTemplate将“FilterTextBox”放在列的标题内

How to use ItemsControl with TextBox to filter DataGrid columns 如何使用ItemsControlTextBox来过滤DataGrid列

It is possible to use ItemsControl and bind to DataGrid.Columns like this: 可以使用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>

Because you set the ItemsControl.ItemsSource to DataGrid.Columns instead of DataContext.Filters you have to set DataGridColumn.DisplayIndex and use IMultiValueConverter to be able to access DataContext.Filters again: 因为您将ItemsControl.ItemsSource设置为DataGrid.Columns而不是DataContext.Filters您必须设置DataGridColumn.DisplayIndex并使用IMultiValueConverter再次访问DataContext.Filters

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

The ViewModel: ViewModel:

public class UsersViewModel : BindableBase
{
    public ObservableCollection<User> Users { get; set; }
    private ICollectionView _usersView;
    public ObservableCollection<string> Filters { get; set; }

    public UsersViewModel()
    {
        _usersView = CollectionViewSource.GetDefaultView(Users);
        _usersView.Filter = delegate (object item)
        {
            User user = item as User;

            List<string> columns = new List<string>() { user.FirstName, user.LastName, user.Age };

            bool include = true;

            for (int i = 0; i < columns.Count; ++i)
            {
                if (!string.IsNullOrEmpty(Filters[i]) && columns[i].IndexOf(Filters[i], StringComparison.OrdinalIgnoreCase) == -1)
                {
                    include = false;
                    break;
                }
            }

            return include;
        };
        Filters.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => _usersView.Refresh();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM