我有一个BindingProxy将DataGrid的DataGridColumns的Visibility属性绑定到Dictionary中的值(“ColumnsVisibility”)。我还有一个Context-Menu,可以隐藏/显示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>
初始加载有效,如果在InitializeComponent()之前字典“ColumnsVisibility”填充了Information,则应用我设置DictionaryEntry的值。
我的目标是检查Contextmenu中的复选框,并且列出现/消失。因为ContextMenu和Columns不是与DataGrid或其他所有相同的可视树的成员,所以我正在使用Proxy。 我的问题是,在ContextMenu中检查/取消选中CheckBox不会更改ColumnsVisibility [ElementName]的值。如果我在Checkbox中添加check / uncheck-Event,我可以在代码中使用它来更改它,但触发PropertyChanged-Event不会改变任何视觉效果。该列原样保留。
BindingProxy是否将事件转发到GUI,反之亦然?目前似乎没有。 任何人都有一个想法如何解决这个问题?
修改 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: ColumnsVisibilty属性
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"));
}
}
在InitializeComponent()之前,这是在加载时完成的:
_ColumnsVisibility.Add("ElementName", false);
EDIT3 好的,这是完整的源代码: 交互逻辑:
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>
答案 0 :(得分:1)
编辑:
肯定是错误的:
PropertyChanged(
的空强> , new PropertyChangedEventArgs("ColumnsVisibility"));
应该是:
PropertyChanged(
的此强> , new PropertyChangedEventArgs("ColumnsVisibility"));
我已经盲目地将它复制到我的第一次编辑中。好吧,有时你只是看不到眼前的东西
为了将来,我建议你在某些基类中使用某种函数,比如
public class NotifyPropertyChangeableBase: INotifyPropertyChanged // Usually I name it somewhat like 'ViewModelBase' in my projects, but your actual class is the control, so it is not the most appropriate name
{
protected void OnPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
它没有解决问题,但它至少存在一个问题
编辑(我希望)最终解决方案:
看起来您无法通知WPF引擎您的字典已更改其项目。而标准词典本身并不这样做(它没有实现ICollectionChanged甚至INotifyPropertyChanged)。
这就是为什么你必须使用自己的字典,或者更精确的包装字典类:
public class DictionaryNotificationWrapper<TKey, TValue> : IDictionary<TKey, TValue>, INotifyPropertyChanged
{
#region Fields
private IDictionary<TKey, TValue> innerDictionary;
#endregion
#region Constructors
public DictionaryNotificationWrapper(IDictionary<TKey, TValue> innerDictionary)
{
if (innerDictionary == null)
throw new ArgumentNullException("innerDictionary", "The inner dictionary is null");
this.innerDictionary = innerDictionary;
}
#endregion
#region IDictionary implementation
public TValue this[TKey key]
{
get
{
return this.innerDictionary[key];
}
set
{
this.innerDictionary[key] = value;
this.OnPropertyChanged("Item[]");
this.OnPropertyChanged("Count");
}
}
#endregion
#region not implemented IDictionary members - you are free to finish the work
public void Add(TKey key, TValue value)
{
throw new NotImplementedException();
}
public bool ContainsKey(TKey key)
{
throw new NotImplementedException();
}
public ICollection<TKey> Keys
{
get { throw new NotImplementedException(); }
}
public bool Remove(TKey key)
{
throw new NotImplementedException();
}
public bool TryGetValue(TKey key, out TValue value)
{
throw new NotImplementedException();
}
public ICollection<TValue> Values
{
get { throw new NotImplementedException(); }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { throw new NotImplementedException(); }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
#endregion
}
有了这样一堂课:
/// <summary>
/// Interaction logic for HyperVControl.xaml
/// </summary>
public partial class HyperVControl : UserControl, INotifyPropertyChanged
{
#region Constructors
public HyperVControl()
{
// Form initialization
InitializeComponent();
// Initialize columns visibility collection
IDictionary<String, Boolean> innerColumnsVisibilityDictionary = new Dictionary<String, Boolean>();
innerColumnsVisibilityDictionary.Add("ElementName", true);
// Wrap the visibility dictionary
this.ColumnsVisibility = new DictionaryNotificationWrapper<String, Boolean>(innerColumnsVisibilityDictionary);
// Initialize grid's datasource
this.HVMachineList = new ObservableCollection<HyperVMachine>();
this.HVMachineList.Add(new HyperVMachine());
this.HVMachineList.Add(new HyperVMachine());
this.HVMachineList.Add(new HyperVMachine());
}
您将能够在没有任何代码隐藏的情况下通知您的可视组件。
P.S。:我已经实现了INotifyProperyChanged,它通知了Item []索引属性的变化,但你可以尝试实现INotifyCollectionChanged接口 - 我只是不确定它如何与索引绑定一起使用。
P.P.S。:我没有看到你的评论,你发现this.PropertyChanged(
此 , new ...
问题。
P.P.P.S。:如果你有时间,那么将问题标题更改为&#34; BindingProxy:绑定到索引属性&#34;为了更好地反映问题,只留下上次编辑的代码(以避免重复) - 将其视为社区服务。