Установите для источника привязки WPF столбцы DataGrid - PullRequest
0 голосов
/ 10 января 2019

У меня есть WPF DataGrid с 18 столбцами, и у каждого столбца есть TextBox над ним, чтобы я мог отфильтровать столбец.

Каждый 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>

Я знаю, что могу связать 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 из 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 вместо StackPanel, но так не работает .

Есть ли другой способ добиться этого?

Ответы [ 3 ]

0 голосов
/ 10 января 2019

Вы действительно можете привязать столбцы вашего DataGrid, сделав это

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

Но это не сработает для того, чего вы хотите достичь. Как только вы измените порядок столбцов во время выполнения, порядок больше не будет соответствовать порядку ваших текстовых полей. Таким образом, вы должны изменить их порядок.
Примечание: DataGridColumn.DisplayIndex возвращает текущий индекс в пределах вашего DataGrid.


Гораздо лучшим подходом и рекомендуемым способом будет размещение ваших FilterTextBoxes внутри заголовков ваших столбцов путем изменения DataGridColumn.HeaderTemplate

0 голосов
/ 12 января 2019

Как использовать ItemsControl с TextBox для фильтрации столбцов 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, чтобы снова иметь доступ к 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 };
    }
}

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();
    }
}
0 голосов
/ 10 января 2019

Невозможно связать со DataGrid свойствами столбцов, поскольку они находятся только в логическом дереве, а не в визуальном дереве. Единственный способ сделать что-то подобное - это изменить DataGridTextColumn.HeaderTemplate и создать новый DataTemplate с фильтром TextBox внутри.

...