Как отображать конкретную строку DataGrid в зависимости от выбранного элемента ListBox?
Создаю приложение с MVVM на wpf, возникла трудность. Нужно, чтобы при выборе элемента ListBox в DataGrid-е отображалась информация только о выбранном элементе. Сейчас все хорошо работает, но в DataGrid-е содержится информация о всех объектах.
Нужно, чтобы в DataGrid-е выводился только один ряд, в зависимости от выбранного элемента:
Model:
public class Model : INotifyPropertyChanged
{
public int x, y, z;
public string Name { get; set; }
public int X
{
get { return x; }
set
{
x = value;
OnPropertyChanged("X");
}
}
public int Y
{
get { return y; }
set
{
y = value;
OnPropertyChanged("Y");
}
}
public int Z
{
get { return z; }
set
{
z = value;
OnPropertyChanged("Z");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<Model> Models { get; set; }
public ViewModel()
{
Models = new ObservableCollection<Model>
{
new Model{Name="First", X=1, Y=1, Z = 1},
new Model{Name="Second", X=2, Y=2, Z = 2 },
new Model{Name="Third", X=3, Y=3, Z = 3 },
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox x:Name="list1" Grid.Column="0" ItemsSource="{Binding Models}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<TextBlock FontSize="18" Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid Grid.Column="1" ItemsSource="{Binding Models}" AutoGenerateColumns="False" CanUserAddRows="False" IsSynchronizedWithCurrentItem="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="X" Binding="{Binding X}" Width="*"/>
<DataGridTextColumn Header="Y" Binding="{Binding Y}" Width="*"/>
<DataGridTextColumn Header="Z" Binding="{Binding Z}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Ответы (2 шт):
Можно создать конвертер и прибиндиться к текущему элементу списка через него.
ItemToListConverter.cs
public class ItemToListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value != null ? new List<object> { value } : null;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null;
}
Добавить конвертер в ресурсы
MainWindow.xaml
<Window.Resources>
<local:ItemToListConverter x:Key="ItemToListConverter"/>
</Window.Resources>
IsSynchronizedWithCurrentItem теперь не требуется, я убрал
MainWindow.xaml
<ListBox x:Name="list1" Grid.Column="0" ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<TextBlock FontSize="18" Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid Grid.Column="1" ItemsSource="{Binding SelectedItem, ElementName=list1, Converter={StaticResource ItemToListConverter}}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="X" Binding="{Binding X}" Width="*"/>
<DataGridTextColumn Header="Y" Binding="{Binding Y}" Width="*"/>
<DataGridTextColumn Header="Z" Binding="{Binding Z}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Допустим у нас есть такая разметка (2 контрола, которые должны вывести разную информацию).
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding OriginCollection}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid Grid.Column="1" ItemsSource="{Binding OriginCollection}" />
</Grid>
Нам надо в DataGrid (странно конечно, почему он) вывести выделенный объект из ListBox:
Для начала нам нужен сам этот объект:
Делаем свойство
public TestClass Selected { get; set; }Привязываем
SelectedItemуListBoxSelectedItem="{Binding Selected}"
Далее нам нужно нечто, что будет фильтровать нам данные, для этой цели существует такая вещь, как
ICollectionView:Делаем свойство
public ICollectionView SortedCollection { get; }Инициализируем его (задав источником основную коллекцию)
SortedCollection = new CollectionViewSource { Source = OriginCollection }.View;Задаем фильтр, по которому будет эта коллекция нам выдавать значения (если объект равен выбранному)
SortedCollection.Filter = item => item == Selected;Вызываем у свойства
Selectedметод обновления коллекции:public TestClass Selected { get => selected; set { selected = value; SortedCollection.Refresh(); } }
Собственно и все, осталось привязать DataGrid
<DataGrid Grid.Column="1" ItemsSource="{Binding SortedCollection}" />
Без чего либо лишнего, за нас думает система, нам надо только сделать правильный фильтр и привязать, все.




