Флажок с DataGrid WPF
Я пытаюсь создать DataGrid в WPF 4.0 с помощью MVVM...
Требуемые функции -
- Muti-выбор строк с помощью флажка (один клик)
- a установите флажок все, чтобы проверить все флажки в datagrid
Что-то вроде этого -

Прошло 2 дня, и я не могу понять, как эффективно решить эту проблему..
Рабочий пример - это то, что мне нужно сейчас как можно скорее..
Я буду очень признателен, если у кого-то есть рабочее решение, чтобы поделиться со мной...
N пожалуйста, не говорите мне гуглить эту вещь, потому что ни одна из вещей не сработала для меня...
Обновление -
- я использую автогенерацию колонок
- я не хочу добавлять "IsSelected" или любое из таких свойств в мою модель..
- я просто столкнулся с 2 проблемами -
Во-первых, функция "Select all", то есть проверка всех флажков на флажке, щелчок которого присутствует в столбце. заголовок...(Я могу выбрать и отменить выбор datagrid, но не могу поставить / снять флажки)
Во-вторых, множественное выделение при щелчке мыши без удержания клавиши Ctrl..
3 ответов:
Когда вы работаете с MVVM,вы должны знать, что считается данными, а что-строго пользовательским интерфейсом.
Будет ли ваш
SelectedItemsчастью ваших данных или только вашим пользовательским интерфейсом?Если это часть ваших данных, вы действительно должны иметь свойство
IsSelectedв вашей модели данных, даже если это означает расширение класса данных, чтобы включить свойствоIsSelected, или создание класса-оболочки, который содержит толькоbool IsSelectedиobject MyDataItem. Первый вариант, вероятно, предпочтительнее, так как вы можете сохранитьAutoGenerateColumns="True", и это упрощает привязку столбцов.Тогда вы просто свяжете свой
DataGridRow.SelectedItemсо свойствомIsSelectedэлемента данных:Но если ваш<Style TargetType="{x:Type DataGridRow}"> <Setter Property="IsSelected" Value="{Binding IsSelected}" /> </Style>SelectedItemsпредназначен только для пользовательского интерфейса, или если вы по какой-то причине нарушаете шаблон MVVM в этом случае, то вы можете создать несвязанныйCheckBoxи использовать некоторый код позади, чтобы гарантировать, чтоCheckBoxправильно синхронизирован сSelectedItem.Я сделал быстрый пример приложения, и вот как выглядел мой код:
Во-первых, я просто добавил несвязанный столбец
CheckBoxв список столбцов с помощьюDataGridTemplateColumn. Это будет добавлено перед списком столбцовAutoGenerateColumns.Во-вторых, я добавил событие<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding Test}" SelectionMode="Extended" CanUserAddRows="False" PreviewMouseLeftButtonDown="TestDataGrid_PreviewMouseLeftButtonDown_1"> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox x:Name="TestCheckBox" PreviewMouseLeftButtonDown="CheckBox_PreviewMouseLeftButtonDown" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid>PreviewMouseDownкCheckBox, чтобы оно установило свойствоIsSelectedстроки.private void CheckBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var chk = (CheckBox)sender; var row = VisualTreeHelpers.FindAncestor<DataGridRow>(chk); var newValue = !chk.IsChecked.GetValueOrDefault(); row.IsSelected = newValue; chk.IsChecked = newValue; // Mark event as handled so that the default // DataGridPreviewMouseDown doesn't handle the event e.Handled = true; }Он должен перемещаться по
VisualTree, чтобы найтиDataGridRow, связанный с нажатойCheckBox, Чтобы выбрать его, и чтобы облегчить жизнь, я использую некоторые пользовательские VisualTreeHelpers, которые у меня есть в моем блоге, чтобы найтиDataGridRow. Вы можете использовать то же самое код, или вы можете создать свой собственный метод для поискаVisualTree.И последнее, если пользователь нажимает на любое другое место, кроме
CheckBox, мы хотим отключить событие выбора по умолчаниюDataGrid. Это гарантирует, что значениеIsSelectedизменится только при нажатии наCheckBox.Есть несколько способов сделать это, которые отключат выбор на разных уровнях, но чтобы упростить жизнь, я просто отключил событие
DataGrid.PreviewMouseLeftButtonDown, если пользователь не нажал на кнопку.CheckBox.private void TestDataGrid_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e) { var chk = VisualTreeHelpers.FindAncestor<CheckBox>((DependencyObject)e.OriginalSource, "TestCheckBox"); if (chk == null) e.Handled = true; }Я снова использую свой пользовательский VisualTreeHelpers для навигации по визуальному дереву и выяснения, был ли установлен флажок, и отмены события, если пользователь щелкнул в любом другом месте, кроме
CheckBox.Что касается вашего 2-го запроса на добавление
CheckBoxкSelectAllилиUnselectAllэлементам, это снова будет зависеть от того, является ли ваш выбор частью пользовательского интерфейса или данных.Если это часть пользовательского интерфейса, просто добавьте
CheckBoxкDataGridTemplateColumn.HeaderTemplate, и когда это щелкнув, выполните цикл поDataGrid.Rows, найдитеCheckBoxв первом столбце и установите или снимите флажок.Если это часть данных, вы можете сделать то же самое (только установить привязанное значение в
DataGrid.ItemsвместоCheckBox.IsCheckedизDataGrid.Rows), или вы можете сделать так, как предложил Адольфо Перес, и привязать его к свойству наViewModel.
Для решения MVVM вы можете попробовать следующее:
<StackPanel> <DataGrid ItemsSource="{Binding Path=TestItems}" AutoGenerateColumns="False" Name="MyDataGrid" CanUserAddRows="False"> <DataGrid.Columns> <DataGridCheckBoxColumn Binding="{Binding IsSelected}" Width="50" > <DataGridCheckBoxColumn.HeaderTemplate> <DataTemplate x:Name="dtAllChkBx"> <CheckBox Name="cbxAll" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=DataGrid}}"/> </DataTemplate> </DataGridCheckBoxColumn.HeaderTemplate> </DataGridCheckBoxColumn> <DataGridTemplateColumn Header="Name" Width="SizeToCells" IsReadOnly="True"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </StackPanel>В вашем
ViewModel:private void PopulateTestItems() { TestItems = new ObservableCollection<TestItem>(); for (int i = 0; i < 5; i++) { TestItem ti = new TestItem(); ti.Name = "TestItem" + i; ti.IsSelected = true; TestItems.Add(ti); } } private bool _AllSelected; public bool AllSelected { get { return _AllSelected; } set { _AllSelected = value; TestItems.ToList().ForEach(x => x.IsSelected = value); NotifyPropertyChanged(m => m.AllSelected); } } private ObservableCollection<TestItem> _TestItems; public ObservableCollection<TestItem> TestItems { get { return _TestItems; } set { _TestItems = value; NotifyPropertyChanged(m => m.TestItems); } }И, наконец, пример класса модели:
public class TestItem : ModelBase<TestItem> { private string _Name; public string Name { get { return _Name; } set { _Name = value; NotifyPropertyChanged(m => m.Name); } } private bool _IsSelected; public bool IsSelected { get { return _IsSelected; } set { _IsSelected = value; NotifyPropertyChanged(m => m.IsSelected); } } }Большая часть кода выше должна быть понятна, но если у вас есть какие-либо вопросы, дайте мне знать
Ваш взгляд может быть чем-то вроде
<DataGrid Name="SomeDataGrid" Grid.Row="0" ItemsSource="{Binding Path=SomeCollection}"> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.HeaderTemplate> <DataTemplate> <CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.AllItemsAreChecked}" /> </DataTemplate> </DataGridTemplateColumn.HeaderTemplate> <DataGridTemplateColumn.CellTemplate> <DataTemplate DataType="{x:Type local:SomeType}"> <CheckBox Focusable="False" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" VerticalAlignment="Center"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="RandomNumber" Width="160"> <DataGridTemplateColumn.CellTemplate> <DataTemplate DataType="{x:Type local:SomeType}"> <TextBlock Text="{Binding Path=RandomNumber}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Date" Width="160"> <DataGridTemplateColumn.CellTemplate> <DataTemplate DataType="{x:Type local:SomeType}"> <TextBlock Text="{Binding Path=Date}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Time" Width="50"> <DataGridTemplateColumn.CellTemplate> <DataTemplate DataType="{x:Type local:SomeType}"> <TextBlock Text="{Binding Time}" HorizontalAlignment="Left" VerticalAlignment="Center"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid>И в viewmodel Свойство привязки SomeCollection является наблюдаемым sometype содержит такие свойства, как IsSelected, RandomNumber, Date, Time
Например:
class ViewModel { public ObservableCollection<SomeType> SomeCollection{get;set;} } class SomeType { public string Date {get;set;} public string Time {get;set;} public string RandomNumber {get;set;} public bool IsSelected {get;set;} }
Comments