WPF DataGrid:将 DataGridColumn 可见性绑定到 ContextMenu MenuItems IsChecked (MVVM)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1560871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF DataGrid: Binding DataGridColumn visibility to ContextMenu MenuItems IsChecked (MVVM)
提问by Fubzot
I want to control DataGrid
column visibility through a ContextMenu
available to the user by right-clicking the column header. The ContextMenu
displays the names of all available columns. I am using MVVM design pattern.
我想通过右键单击列标题来通过用户可用的方式控制DataGrid
列可见性ContextMenu
。将ContextMenu
显示所有可用的列的名称。我正在使用 MVVM 设计模式。
My question is: How do I bind the DataGridColumn
's Visibility
property to the IsChecked
property of a MenuItem
located in the ContextMenu
.
我的问题是:如何将绑定DataGridColumn
的Visibility
属性设置IsChecked
的属性MenuItem
地处ContextMenu
。
Some mockup code:
一些样机代码:
<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
...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>
If I am being unclear please let me know and I will attempt to elaborate.
如果我不清楚,请告诉我,我会尝试详细说明。
Cheers,
干杯,
回答by Tim Valentine
I just wrote a blog post on this topic. It allows DataGridColumns to be shown or hidden through a ContextMenu that is accessible by right-clicking any column header. This task is accomplished purely through attached properties so it is MVVM-compliant.
我刚刚写了一篇关于这个主题的博客文章。它允许通过右键单击任何列标题访问的 ContextMenu 显示或隐藏 DataGridColumns。此任务完全通过附加属性完成,因此符合 MVVM。
回答by Webreaper
I've been looking for a generic, XAML(i.e., no code-behind), automatic and simpleexample of a column chooser context menu that binds to a WPF DataGrid column header. I've read literally hundreds of articles, but none of them seem to do exactlythe right thing, or they're no generic enough. So here's what I think is the best combined solution:
我一直在寻找一个通用的、XAML(即没有代码隐藏)、自动和简单的列选择器上下文菜单示例,它绑定到 WPF DataGrid 列标题。我已经阅读了数百篇文章,但似乎没有一篇文章做得完全正确,或者它们不够通用。所以这是我认为最好的组合解决方案:
First, put these in the resource dictionary. I'll leave it as an exercise to the reader to write the Visibility/Boolean converter to ensure the checkboxes check when the column is visible and vice-versa. Note that by defining x:Shared="False" for the context menu resource, it'll get instance-specific state which means that you can use this single template/resource for all your datagrids and they'll all maintain their own state.
首先,将这些放入资源字典中。我将把它作为练习留给读者编写 Visibility/Boolean 转换器以确保复选框在列可见时检查,反之亦然。请注意,通过为上下文菜单资源定义 x:Shared="False",它将获得特定于实例的状态,这意味着您可以将此单个模板/资源用于所有数据网格,并且它们都将保持自己的状态。
<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>
Then define the DataGrid as follows (where OrdersQuery is some data source exposed by the View-model):
然后如下定义 DataGrid(其中 OrdersQuery 是 View-model 公开的一些数据源):
<sdk:DataGrid ItemsSource="{Binding OrdersQuery}"
AutoGenerateColumns="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
ContextMenu="{StaticResource GridItemsContextMenu}">
<!-- rest of datagrid stuff goes here -->
</sdk:DataGrid>
This will give you the following:
这将为您提供以下信息:
- A context menu bound to the column headingswhich acts as a column chooser.
- A context menu bound to the items in the grid (to perform actions on the items themselves - again, the binding of the actions is an exercise for the reader).
- 绑定到列标题的上下文菜单,用作列选择器。
- 绑定到网格中的项目的上下文菜单(对项目本身执行操作 - 同样,操作的绑定是读者的练习)。
Hope this helps people who've been looking for an example like this.
希望这可以帮助那些一直在寻找这样的例子的人。
回答by Roman
I know this is a bit old. But I was looking at doing this and this post is much simpler: 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/
我知道这有点旧。但我正在考虑这样做,这篇文章要简单得多:http: //imaginec.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/
All you need to do is set DataContext on the Columns and then bind Visibility to your ViewModel as per normal! :) Simple and effective
您需要做的就是在 Columns 上设置 DataContext,然后按照正常方式将 Visibility 绑定到您的 ViewModel!:) 简单有效
回答by Fubzot
Ok, this has been quite the exercise for a WPF n00b.
好的,这对于 WPF n00b 来说是一个很好的练习。
IanR thanks for the suggestion I used a similar aproach but it dosent take you all the way.
IanR 感谢您的建议,我使用了类似的方法,但它不会带您一路走来。
Here is what I have come up with if anyone can find a more consistent way of doing it I will appreciate any comments:
如果有人能找到更一致的方法,这就是我的想法,我将不胜感激:
Impediments:
障碍:
DataGridColumnHeader does not support a context menu. Therefore the context menu needs to be applied as a Style.
The contextmenu has its own datacontext so we have to use findancestor to link it to the ViewModels datacontext.
ATM the DataGrid control does not parse its datacontext to its Columns. This could be solved in codebehind however we are using the MVVM pattern so I decided to follow jamiersapproach
DataGridColumnHeader 不支持上下文菜单。因此上下文菜单需要作为样式应用。
上下文菜单有自己的数据上下文,因此我们必须使用 findancestor 将其链接到 ViewModels 数据上下文。
ATM DataGrid 控件不会将其数据上下文解析为它的列。这可以在代码隐藏中解决,但是我们使用的是 MVVM 模式,所以我决定遵循jamiers方法
Solution:
解决方案:
Place the following two blocks of code in Window.Resources
在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>
The datagrid then looks something like this in XAML
然后数据网格在 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}}"/>
So the visibility property on the DataGridColumn and the ischeked property are both databound to the IsHidden property on the viewModel.
因此,DataGridColumn 上的可见性属性和 ischeked 属性都数据绑定到 viewModel 上的 IsHidden 属性。
In the ViewModel:
在视图模型中:
public bool IsHidden
{
get { return isHidden; }
set
{ if (value != isHidden)
{
isHidden = value;
OnPropertyChanged("IsHidden");
OnPropertyChanged("IsVisible");
}
}
}
The Helper class defined by Jaimer:
Jaimer 定义的 Helper 类:
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 );
}
}
}
}
Instanciated in the viewmodel (just to show done through Unity in real project)
在视图模型中实例化(只是为了在实际项目中通过 Unity 显示完成)
private static DataGridSupport dc = new DataGridSupport();
Cheers,
干杯,
回答by Sergey Malyan
Instead of booleanToVisibilityConverter you can use x:static
您可以使用 x:static 代替 booleanToVisibilityConverter
<Setter TargetName="UIElement" Property="UIElement.Visibility" Value="x:Static Visibility.Hidden" />
Statics in XAML: http://msdn.microsoft.com/en-us/library/ms742135.aspx
XAML 中的静态:http: //msdn.microsoft.com/en-us/library/ms742135.aspx
回答by kiwipom
I did try to ge this to bind to the ContextMenu using 'ElementName', but in the end, got it work using Properties in the VM, e.g.
我确实尝试使用“ElementName”将其绑定到 ContextMenu,但最后,使用 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; }
}
and in the XAML:
并在 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>