c# - BindingProxy: binding to the indexed property -
i have bindingproxy bind visibility-property of datagridcolumns of datagrid value in dictionary ("columnsvisibility"). have context-menu, should make possible hide/show columns of 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 dictionary "columnsvisibility" filled information before initializecomponent(), value set dictionaryentry to, applied.
my target check checkbox in contextmenu , column appears/disappears. because contextmenu , columns not member of same visual tree datagrid or else, i'm using proxy. problem is, checking/unchecking of checkbox in contextmenu don't change value of columnsvisibility[elementname]. if add check/uncheck-event checkbox, can change using in code, triggering propertychanged-event don't change visual. column stays is.
is bindingproxy forwarding events gui , vice versa? seems doesn't. has idea how solve problem?
edit: bindingproxy:
public class bindingproxy : freezable { #region overrides of freezable protected override freezable createinstancecore() { return new bindingproxy(); } #endregion public object data { { return (object)getvalue(dataproperty); } set { setvalue(dataproperty, value); } } // using dependencyproperty backing store data. enables animation, styling, binding, etc... public static readonly dependencyproperty dataproperty = dependencyproperty.register("data", typeof(object), typeof(bindingproxy), new uipropertymetadata(null)); }
edit2: 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() done on loading:
_columnsvisibility.add("elementname", false);
edit3 ok, here 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 hypervcontrol.xaml /// </summary> public partial class hypervcontrol : usercontrol, inotifypropertychanged { public hypervcontrol() { #region set default visibility columns _columnsvisibility.add("elementname", false); //(...) #endregion initializecomponent(); } #region control triggered private void usercontrol_loaded(object sender, routedeventargs e) { } /// <summary> /// triggered checkboxes, in contextmenu of datagrid-header show/hide columns /// </summary> /// <param name="sender">the checkbox, send command</param> /// <param name="e"></param> private void checkbox_checked(object sender, routedeventargs e) { //this sets value in columnsvisibility sure. value not set binding (but should...) columnsvisibility[((checkbox)sender).tag.tostring()] = (bool)((checkbox)sender).ischecked; //nothing of 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 , publics) private observablecollection<hypervmachine> _hvmachinelist; private dictionary<string, bool> _columnsvisibility = new dictionary<string, bool>(); /// <summary> /// contains loaded information virtual clients /// </summary> public observablecollection<hypervmachine> hvmachinelist { { return _hvmachinelist; } set { _hvmachinelist = value; if (propertychanged != null) propertychanged(this, new propertychangedeventargs("hvmachinelist")); } } /// <summary> /// 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 form public event propertychangedeventhandler propertychanged; #endregion } //binding proxy #region freezable context-menu-data-transmition public class bindingproxy : freezable { #region overrides of freezable protected override freezable createinstancecore() { return new bindingproxy(); } #endregion public object data { { return (object)getvalue(dataproperty); } set { setvalue(dataproperty, value); } } // using dependencyproperty backing store data. 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>
edit:
it error sure:
propertychanged(
null, new propertychangedeventargs("columnsvisibility"));
it should be:
propertychanged(
this, new propertychangedeventargs("columnsvisibility"));
i have blindly copied first edit code. well, don't see things before eyes
for future recommend use sort of function in base class like
public class notifypropertychangeablebase: inotifypropertychanged // name 'viewmodelbase' in projects, actual class control, not appropriate name { protected void onpropertychanged(string propertyname) { if (this.propertychanged != null) this.propertychanged(this, new propertychangedeventargs(propertyname)); } public event propertychangedeventhandler propertychanged; }
it did not solve issue, @ least 1 problem down
edit with(i hope) final solution:
it looks cannot notify wpf engine dictionary has changed items. , standard dictionary not on own(it not implement icollectionchanged or inotifypropertychanged).
that's why have use own dictionary, or more precise wrapper dictionary class:
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 null"); this.innerdictionary = innerdictionary; } #endregion #region idictionary implementation public tvalue this[tkey key] { { return this.innerdictionary[key]; } set { this.innerdictionary[key] = value; this.onpropertychanged("item[]"); this.onpropertychanged("count"); } } #endregion #region not implemented idictionary members - free finish work public void add(tkey key, tvalue value) { throw new notimplementedexception(); } public bool containskey(tkey key) { throw new notimplementedexception(); } public icollection<tkey> keys { { 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 { { 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 { { throw new notimplementedexception(); } } public bool isreadonly { { 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 }
with such class:
/// <summary> /// interaction logic 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 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()); }
you able notify visual components without code-behind.
p.s.: have implemented inotifyproperychanged notifies changes in item[] indexed property, try implement inotifycollectionchanged interface - unsure how work indexed bindings.
p.p.s.: haven't seen comment have found this.propertychanged(
this, new ...
issue.
p.p.p.s.: if have time change question title "bindingproxy: binding indexed property" better reflect problem , leave code last edit(to avoid duplication) - think of community service.
Comments
Post a Comment