代码之家  ›  专栏  ›  技术社区  ›  Jinjinov

将WPF绑定源设置为DataGrid列

  •  1
  • Jinjinov  · 技术社区  · 6 年前

    我有一个WPF DataGrid 有18列,每列有一个 TextBox 在上面,这样我可以过滤列。

    文本框 绑定 Width 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>
    

    我知道我可以装订 文本框 Text 到A 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]}"/>
    

    我想装订 宽度 文本框 实际宽度 列中有如下内容:

    <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 而不是 StackPanel 但是 这样不行 .

    我还有别的办法可以做到吗?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Dean Chalk    6 年前

    你不能绑定到 DataGrid 列属性,因为它们只在逻辑树中,而不是可视化树中。 唯一的方法就是改变 DataGridTextColumn.HeaderTemplate 并创建一个新的 DataTemplate 带滤波器 TextBox 内。

        2
  •  1
  •   nosale    6 年前

    您确实可以绑定到 DataGrid 这样做

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

    但这并不能满足你想要达到的目标。一旦在运行时对列重新排序,顺序将不再与文本框的顺序匹配。所以你也必须重新订购它们。
    注: DataGridColumn.DisplayIndex 返回您的 数据网格 .


    更好的方法和推荐的方法是通过更改 DataGridColumn.HeaderTemplate

        3
  •  0
  •   Jinjinov    6 年前

    如何使用 ItemsControl 具有 TextBox 筛选数据报列

    可以使用 项目控制 并绑定到 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 数据报列 而不是 DataContext.Filters 你必须设置 DataGridColumn.DisplayIndex 使用 IMultiValueConverter 能够访问 数据上下文筛选器 再一次:

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

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