c# - WPF Command CanExecute Not Being Re-Evaluated When DataContext Changes -
i'm having trouble getting canexecute method of command work property. i've bound command button inside of datagrid. i've bound commandparameter button's datacontext, happens record row in datagrid.
what expect happen canexecute method re-evaluated when commandparameter binding changes, in case row's datacontext property being set. instead of evaluating canexecute method against row data, looks canexecute method being evaluated before row gets datacontext , never re-evaluated after datacontext has been updated.
can tell me how canexecute method of command evaluated against each row's datacontext?
i've created sample application demonstrate problem. here's code:
the code-behind mainwindow.xaml
public partial class mainwindow : window { public observablecollection<logrecord> records { get; private set; } public icommand signoutcommand { get; private set; } public mainwindow() { initializecomponent(); datacontext = this; records = new observablecollection<logrecord>(); signoutcommand = new signoutcommand(); createdemodata(); } private void createdemodata() { (int = 0; < 5; i++) { records.add(new logrecord()); } } } public class logrecord : inotifypropertychanged { private datetime _entrytime; public datetime entrytime { { return _entrytime; } set { if (_entrytime == value) return; _entrytime = value; raisepropertychanged("entrytime"); } } private datetime? _exittime; public datetime? exittime { { return _exittime; } set { if (_exittime == value) return; _exittime = value; raisepropertychanged("exittime"); } } public logrecord() { entrytime = datetime.now; } #region implementation of inotifypropertychanged public event propertychangedeventhandler propertychanged; public void raisepropertychanged(string propertyname) { if (propertychanged != null) propertychanged(this, new propertychangedeventargs(propertyname)); } #endregion } public class signoutcommand : icommand { #region implementation of icommand public void execute(object parameter) { var record = parameter logrecord; if (record == null) return; record.exittime = datetime.now; } public bool canexecute(object parameter) { var record = parameter logrecord; return record != null && !record.exittime.hasvalue; } public event eventhandler canexecutechanged; #endregion }
the xaml mainwindow.xaml
<window x:class="command_spike.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" title="mainwindow" width="525" height="350"> <datagrid itemssource="{binding path=records}" isreadonly="true" autogeneratecolumns="false"> <datagrid.columns> <datagridtextcolumn header="entry time" binding="{binding path=entrytime}" /> <datagridtextcolumn header="exit time" binding="{binding path=exittime}" /> <datagridtemplatecolumn> <datagridtemplatecolumn.celltemplate> <datatemplate> <button command="{binding relativesource={relativesource mode=findancestor, ancestortype=window}, path=datacontext.signoutcommand}" commandparameter="{binding}" content="sign out" /> </datatemplate> </datagridtemplatecolumn.celltemplate> </datagridtemplatecolumn> </datagrid.columns> </datagrid>
if load example code, can see of sign out buttons disabled because in each row, canexecute method receiving null parameter instead of row-specific data want. if sample working properly, of buttons enabled , disable after value in exit time column set.
you not setting custom command correctly. in current example, don't need create command manually implements icommand, need create routed or routedui command , wire appropriate handlers. remove signoutcommand object, modify window code following:
public partial class mainwindow: window { public observablecollection<logrecord> records { get; private set; } public static routeduicommand signoutcommand { get; private set; } public mainwindow() { initializecomponent(); datacontext = this; records = new observablecollection<logrecord>(); createdemodata(); signoutcommand = new routeduicommand(); commandbinding cb = new commandbinding(signoutcommand, onsignout, oncansignout); this.commandbindings.add(cb); } private void createdemodata() { (int = 0; < 5; i++) { records.add(new logrecord()); } } private void oncansignout(object sender, canexecuteroutedeventargs e) { var record = e.parameter logrecord; e.canexecute = record != null && !record.exittime.hasvalue; } private void onsignout(object sender, executedroutedeventargs e) { var record = e.parameter logrecord; if (record == null) return; record.exittime = datetime.now; } }
then, modify datatemplate follows (basically, remove datacontext path):
<dg:datagridtemplatecolumn> <dg:datagridtemplatecolumn.celltemplate> <datatemplate> <button command="{binding relativesource={relativesource mode=findancestor, ancestortype=window}, path=signoutcommand}" commandparameter="{binding}" content="sign out" /> </datatemplate> </dg:datagridtemplatecolumn.celltemplate> </dg:datagridtemplatecolumn>
using approach, sign out button correctly enabled when datacontext set.
Comments
Post a Comment