WPF DataGrid: привязка DataGridColumn к ContextMenu MenuItems IsChecked (MVVM)
Я хочу контролировать видимость столбца DataGrid
через ContextMenu
, доступную пользователю, щелкнув правой кнопкой мыши заголовок столбца. ContextMenu
отображает имена всех доступных столбцов. Я использую шаблон MVVM.
Мой вопрос: Как привязать свойство DataGridColumn
Visibility
к свойству IsChecked
объекта MenuItem
, расположенному в ContextMenu
.
Некоторые макеты кода:
<UserControl.Resources>
<ContextMenu x:Key="ColumnHeaderContextMenu">
<MenuItem Header="Menu Item..1" IsCheckable="True" />
</ContextMenu>
<Style x:Key="ColumnHeaderStyle"
TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
</UserControl.Resources>
... flaf flaf flaf
<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False"
ItemsSource="{Binding MyCollection, Mode=Default}"
EnableColumnVirtualization="True" IsReadOnly="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}"
Header="MyEntry" Visibility="{Binding IsChecked, Converter=
{StaticResource booleanToVisibilityConverter}.... />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
Если я неясен, пожалуйста, дайте мне знать, и я попытаюсь уточнить.
Приветствия,
Ответы
Ответ 1
Я только что написал сообщение в блоге по этой теме. Он позволяет отображать или скрывать DataGridColumn через ContextMenu, доступ к которому можно щелкнуть правой кнопкой мыши по любому заголовку столбца. Эта задача выполняется исключительно через прикрепленные свойства, поэтому она совместима с MVVM.
Посмотреть сообщение в блоге
Ответ 2
Я искал общий, XAML (т.е. без кода), автоматический и простой пример контекстного меню выбора столбца, который привязывается к заголовку столбца WPF DataGrid. Я прочитал буквально сотни статей, но ни один из них, похоже, не делает правильных действий, или они не являются достаточно обобщенными. Итак, вот что я считаю лучшим комбинированным решением:
Сначала поместите их в словарь ресурсов. Я оставлю это как упражнение для чтения, чтобы написать Видимость/Логический конвертер, чтобы убедиться, что флажки отмечены, когда столбец виден и наоборот. Обратите внимание, что, определяя x: Shared = "False" для ресурса контекстного меню, он получит конкретное состояние экземпляра, что означает, что вы можете использовать этот единственный шаблон/ресурс для всего вашего datagrid, и все они будут поддерживать свое собственное состояние.
<Converters:VisiblityToInverseBooleanConverter x:Key="VisiblityToInverseBooleanConverter"/>
<ContextMenu x:Key="ColumnChooserMenu" x:Shared="False"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType={x:Type sdk:DataGrid}}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="AutomationProperties.Name" Value="{Binding Header}"/>
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding Visibility, Mode=TwoWay, Converter={StaticResource VisiblityToInverseBooleanConverter}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type Primitives:DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource ColumnChooserMenu}" />
</Style>
<ContextMenu x:Key="GridItemsContextMenu" >
<MenuItem Header="Launch Do Some other action"/>
</ContextMenu>
Затем определите DataGrid следующим образом (где OrdersQuery - это некоторый источник данных, открытый моделью View):
<sdk:DataGrid ItemsSource="{Binding OrdersQuery}"
AutoGenerateColumns="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
ContextMenu="{StaticResource GridItemsContextMenu}">
<!-- rest of datagrid stuff goes here -->
</sdk:DataGrid>
Это даст вам следующее:
- Контекстное меню, связанное с заголовками столбцов, которое выступает в качестве выбора столбца.
- Контекстное меню, связанное с элементами в сетке (для выполнения действий над самими элементами - опять же, привязка действий - упражнение для читателя).
Надеюсь, это поможет людям, которые искали такой пример.
Ответ 3
Я знаю, что это немного устарело. Но я смотрел на это, и этот пост намного проще:
http://iimaginec.wordpress.com/2011/07/25/binding-wpf-toolkit%E2%80%99s-datagridcolumn-to-a-viewmodel-datacontext-propogation-for-datagrid-columns-the-mvvm-way-to-interact-with-datagridcolumn/
Все, что вам нужно сделать, - установить DataContext в столбцах, а затем привязать видимость к вашей модели ViewModel в соответствии с нормальным!
:) Простой и эффективный
Ответ 4
Хорошо, это было довольно упражнение для WPF n00b.
IanR благодарит за предложение, я использовал аналогичный aproach, но он доцент добирается до вас.
Вот что я придумал, если кто-то может найти более последовательный способ сделать это. Я буду благодарен за любые комментарии:
Препятствия:
-
DataGridColumnHeader не поддерживает контекстное меню. Поэтому контекстное меню должно применяться как стиль.
-
В контекстном меню есть свой собственный datacontext, поэтому нам нужно использовать findancestor для привязки его к datacontext ViewModels.
-
ATM. Элемент управления DataGrid не анализирует его datacontext в своих столбцах. Это можно было бы решить в коде, однако мы используем шаблон MVVM, поэтому я решил следовать jamiers
Решение:
Поместите следующие два блока кода в Window.Resources
<Style x:Key="ColumnHeaderStyle"
TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>
<ContextMenu x:Key="ColumnHeaderContextMenu">
<MenuItem x:Name="MyMenuItem"
IsCheckable="True"
IsChecked="{Binding DataContext.IsHidden, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type toolkit:DataGrid}}}"/>
</ContextMenu>
Затем datagrid выглядит примерно так в XAML
<toolkit:DataGrid x:Name="MyGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding SampleCollection, Mode=Default}"
EnableColumnVirtualization="True"
IsReadOnly="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=SamplingUser}"
Header="{Binding (FrameworkElement.DataContext).IsHidden, RelativeSource={x:Static RelativeSource.Self}}"
Visibility="{Binding (FrameworkElement.DataContext).IsHidden,
RelativeSource={x:Static RelativeSource.Self},
Converter={StaticResource booleanToVisibilityConverter}}"/>
Таким образом, свойство visibility на DataGridColumn и свойство ischeked являются привязкой к привязке к свойству IsHidden в viewModel.
В ViewModel:
public bool IsHidden
{
get { return isHidden; }
set
{ if (value != isHidden)
{
isHidden = value;
OnPropertyChanged("IsHidden");
OnPropertyChanged("IsVisible");
}
}
}
Класс Helper, определенный Jaimer:
class DataGridSupport
{
static DataGridSupport()
{
DependencyProperty dp = FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));
FrameworkElement.DataContextProperty.OverrideMetadata ( typeof(DataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged)));
}
public static void OnDataContextChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid grid = d as DataGrid ;
if ( grid != null )
{
foreach ( DataGridColumn col in grid.Columns )
{
col.SetValue ( FrameworkElement.DataContextProperty, e.NewValue );
}
}
}
}
Определено в viewmodel (просто для того, чтобы показать, сделанное через Unity в реальном проекте)
private static DataGridSupport dc = new DataGridSupport();
Приветствия,
Ответ 5
Вместо booleanToVisibilityConverter вы можете использовать x: static
<Setter TargetName="UIElement" Property="UIElement.Visibility" Value="x:Static Visibility.Hidden" />
Статичность в XAML:
http://msdn.microsoft.com/en-us/library/ms742135.aspx
Ответ 6
Я попытался привязать его к ContextMenu, используя "ElementName", но в конце концов, получил его работу с помощью "Свойства" в VM, например
bool _isHidden;
public bool IsHidden
{
get { return _isHidden; }
set
{
if (value != _isHidden)
{
_isHidden = value;
RaisePropertyChanged("IsHidden");
RaisePropertyChanged("IsVisible");
}
}
}
public Visibility IsVisible
{
get { return IsHidden ? Visibility.Hidden : Visibility.Visible; }
}
и в XAML:
<Window.ContextMenu>
<ContextMenu>
<MenuItem Header="Hidden" IsCheckable="True" IsChecked="{Binding IsHidden}" />
</ContextMenu>
</Window.ContextMenu>
<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection, Mode=Default}" EnableColumnVirtualization="True" IsReadOnly="True" ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}" Header="MyEntry" Visibility="{Binding Path=IsVisible, Mode=OneWay}" />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>