I have a BindingProxy to Bind the Visibility-Property of DataGridColumns of a DataGrid to a Value in a Dictionary ("ColumnsVisibility"). I Also have a Context-Menu, that should make it possible to hide/show the columns of the Grid.
<DataGrid Name="dgMachines"
ItemsSource="{Binding HVMachineList,
UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False"
>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
<ContextMenu x:Key="DataGridColumnHeaderContextMenu">
<MenuItem Header="Names">
<CheckBox Content="Name" IsChecked="{Binding Data.ColumnsVisibility[ElementName], Source={StaticResource proxy}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</MenuItem>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding ElementName}" Visibility="{Binding Data.ColumnsVisibility[ElementName], UpdateSourceTrigger=PropertyChanged, Source={StaticResource proxy}, Converter={StaticResource BoolToVisibilityConv}, Mode=TwoWay}" />
</DataGrid.Columns>
</DataGrid>
The initial loading works, if the Dictionary "ColumnsVisibility" is filled with Information before InitializeComponent(), the value I set the DictionaryEntry to, is applied.
My target is to check the checkbox in the Contextmenu and the Column appears/disappears. Because the ContextMenu and the Columns are not Member of the same visual tree as the DataGrid or everything else, I'm using the Proxy.
My problem is, that the checking/unchecking of the CheckBox in the ContextMenu don't change the value of ColumnsVisibility[ElementName]. If I add a check/uncheck-Event to the Checkbox, I can change it by using it in the code, but triggering a PropertyChanged-Event don't change anything visual. The column stays as it is.
Is the BindingProxy forwarding Events to the GUI and vice versa? Currently it seems like it doesn't.
Anybody has an Idea how to solve this problem?
EDIT:
The BindingProxy:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Edit2:
The ColumnsVisibilty Property
private Dictionary<string, bool> _ColumnsVisibility = new Dictionary<string, bool>();
public Dictionary<string, bool> ColumnsVisibility
{
get{return(_ColumnsVisibility);}
set
{
_ColumnsVisibility = value;
if (PropertyChanged != null)
PropertyChanged(null, new PropertyChangedEventArgs("ColumnsVisibility"));
}
}
Before InitializeComponent() this is done on loading:
_ColumnsVisibility.Add("ElementName", false);
Edit3
OK, here is the full sourcecode:
Interaction logic:
using System.Collections.Generic;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace HyperV
{
/// <summary>
/// Interaction logic for HyperVControl.xaml
/// </summary>
public partial class HyperVControl : UserControl, INotifyPropertyChanged
{
public HyperVControl()
{
#region Set default visibility for Columns
_ColumnsVisibility.Add("ElementName", false);
//(...)
#endregion
InitializeComponent();
}
#region Control triggered
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
}
/// <summary>
/// Is Triggered by Checkboxes, that are in the contextmenu of the DataGrid-Header to show/hide columns
/// </summary>
/// <param name="sender">The Checkbox, that send this command</param>
/// <param name="e"></param>
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
//This sets the value in ColumnsVisibility to be sure. The value is NOT set by binding (but should...)
ColumnsVisibility[((CheckBox)sender).Tag.ToString()] = (bool)((CheckBox)sender).IsChecked;
//Nothing of this works
if (PropertyChanged != null)
{
PropertyChanged(null, new PropertyChangedEventArgs("ColumnsVisibility"));
PropertyChanged(null, new PropertyChangedEventArgs("ColumnsVisibility[Machinename]"));
PropertyChanged(null, new PropertyChangedEventArgs("Data.ColumnsVisibility"));
PropertyChanged(null, new PropertyChangedEventArgs("Data.ColumnsVisibility[Machinename]"));
}
}
#endregion
#region Properties (private and publics)
private ObservableCollection<HyperVMachine> _HVMachineList;
private Dictionary<string, bool> _ColumnsVisibility = new Dictionary<string, bool>();
/// <summary>
/// Contains all loaded information about the virtual Clients
/// </summary>
public ObservableCollection<HyperVMachine> HVMachineList
{
get { return _HVMachineList; }
set
{
_HVMachineList = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("HVMachineList"));
}
}
/// <summary>
/// To set
/// </summary>
public Dictionary<string, bool> ColumnsVisibility
{
get{return(_ColumnsVisibility);}
set
{
_ColumnsVisibility = value;
if (PropertyChanged != null)
PropertyChanged(null, new PropertyChangedEventArgs("ColumnsVisibility"));
}
}
#endregion
#region Events
//To Update Content on the Form
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
//Binding Proxy
#region Freezable for Context-Menu-Data-Transmition
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
#endregion
}
XAML:
<UserControl xmlns:Controls="clr-namespace:HyperV.Controls"
x:Class="HyperV.HyperVControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HyperV"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="900"
Loaded="UserControl_Loaded"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Language/language.xaml"/>
<ResourceDictionary Source="Language/language.de-DE.xaml"/>
</ResourceDictionary.MergedDictionaries>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConv"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<DataGrid Name="dgMachines"
ItemsSource="{Binding HVMachineList, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False"
>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
<ContextMenu x:Key="DataGridColumnHeaderContextMenu">
<MenuItem Header="{StaticResource MenHeadGeneral}">
<CheckBox Tag="ElementName" Content="{StaticResource MenMachinename}" IsChecked="{Binding Data.ColumnsVisibility[ElementName], Source={StaticResource proxy}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"/>
<!-- ... -->
</MenuItem>
<!-- ... -->
</ContextMenu>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource DataGridColumnHeaderContextMenu}" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="{StaticResource MenMachinename}" Binding="{Binding ElementName}" Visibility="{Binding Data.ColumnsVisibility[ElementName], UpdateSourceTrigger=PropertyChanged, Source={StaticResource proxy}, Converter={StaticResource BoolToVisibilityConv}, Mode=TwoWay}" />
<!-- ... -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
See Question&Answers more detail:
os