[英]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. 每个
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>
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”放在列的标题内
ItemsControl
with TextBox
to filter DataGrid columns ItemsControl
和TextBox
来过滤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.