c# - WPF custom composite user controls -
first of all, wpf beginner! approach potentially not right way want not hesitate tell me if case. want composite user control in wpf, using mvvm.
some classes better presentation i, here view models:
interface iparameter : inotifypropertychanged { string name { get; set;} string value { get; set;} } class textparameter : viewmodelbase, iparameter { private string _value; public string name { get; private set; } public string value { { return _value; } set { _value = value; raisepropertychanged(); } } public textparameter (string name) { this.name = name; } } class parameterlist : viewmodelbase, iparameter { private string _value; public string name { get; private set; } public string value { { return _value; } set { _value = value; raisepropertychanged(); } } observablecollection<iparameter> parameters { get; set; } public parameterlist (string name, ienumerable<iparameter> parameters = null) { this.name = name; this.parameters = new observablecollection<iparameter>(parameters ?? new list<iparameter>()); } }
i using mvvm light, propertychanged stuff managed viewmodelbase. also, not exhaustive list of parameters, there others, more complex issue these ones.
here custom user controls:
textparametercontrol.xaml:
<usercontrol x:class="stuff.textparametercontrol" [..] x:name="parent"> <stackpanel datacontext="{binding elementname=parent}" orientation="horizontal"> <textblock text="{binding path=paramname, stringformat='{}{0}:'}" width="100"></textblock> <textbox text="{binding path=value}" width="100"></textbox> </stackpanel> </usercontrol>
textparametercontrol.xaml.cs :
public class textparametercontrol : usercontrol { #region param name public string paramname { { return (string)getvalue(paramnameproperty); } set { setvalue(paramnameproperty, value); } } // using dependencyproperty backing store paramname. enables animation, styling, binding, etc... public static readonly dependencyproperty paramnameproperty = dependencyproperty.register("paramname", typeof(string), typeof(textparametercontrol), new propertymetadata(string.empty)); #endregion #region value public string value { { return (string)getvalue(valueproperty); } set { setvalue(valueproperty, value); } } // using dependencyproperty backing store value. enables animation, styling, binding, etc... public static readonly dependencyproperty valueproperty = dependencyproperty.register("value", typeof(string), typeof(textparametercontrol), new propertymetadata(string.empty)); #endregion public textparametercontrol() { initializecomponent(); } }
parameterlistcontrol.xaml:
<usercontrol x:class="stuff.parameterlistcontrol" [..] x:name="parent"> <usercontrol.resources> <datatemplate x:key="textparametertemplate"> <c:textparametercontrol paramname="{binding name}" value="{binding value}"/> </datatemplate> <datatemplate x:key="parameterlisttemplate"> <c:parameterlistcontrol paramname="{binding name}" value="{binding value}" items="{binding parameters}" /> </datatemplate> <s:parametertemplateselector x:key="parameterselector" textparametertemplate="{staticresource textparametertemplate}" parameterlisttemplate="{staticresource parameterlisttemplate}"/> </usercontrol.resources> <expander datacontext="{binding elementname=parent}" header="{binding path=paramname}" isexpanded="true" expanddirection="down"> <stackpanel> <itemscontrol itemssource="{binding path=items}" itemtemplateselector="{staticresource parameterselector}"></itemscontrol> </stackpanel> </expander> </usercontrol>
parameterlistcontrol.xaml.cs:
public partial class parameterlistcontrol: usercontrol { #region param name public string paramname { { return (string)getvalue(paramnameproperty); } set { setvalue(paramnameproperty, value); } } // using dependencyproperty backing store paramname. enables animation, styling, binding, etc... public static readonly dependencyproperty paramnameproperty = dependencyproperty.register("paramname", typeof(string), typeof(parameterlistcontrol), new propertymetadata(string.empty)); #endregion #region value public string value { { return (string)getvalue(valueproperty); } set { setvalue(valueproperty, value); } } // using dependencyproperty backing store value. enables animation, styling, binding, etc... public static readonly dependencyproperty valueproperty = dependencyproperty.register("value", typeof(string), typeof(parameterlistcontrol), new propertymetadata(string.empty)); #endregion #region items public ilist<string> items { { return (list<string>)getvalue(itemsproperty); } set { setvalue(itemsproperty, value); } } public static readonly dependencyproperty itemsproperty = dependencyproperty.register("items", typeof(ilist<string>), typeof(parameterlistcontrol), new propertymetadata(new list<string>())); #endregion public parameterlistcontrol() { initializecomponent(); } }
here custom template selector:
class parametertemplateselector : datatemplateselector { public datatemplate parameterlisttemplate { get; set; } public datatemplate textparametertemplate { get; set; } public override datatemplate selecttemplate(object item, dependencyobject container) { if (item textparameter) { return this.textparametertemplate; } else if (item parameterlist) { return this.parameterlisttemplate; } throw new exception(string.format("this parameter ({0}) not handled in application", item.gettype().name)); } }
and here calling view , viewmodel:
viewmodel:
public class mainviewmodel : viewmodelbase { public observablecollection<iparameter> parameters { get; set; } public mainviewmodel() { this.parameters = new observablecollection<iparameter>(); this.parameters.add(new textparameter("customer")); // here building complex composite parameter list }
view:
<usercontrol.resources> <datatemplate x:key="textparametertemplate"> <c:textparametercontrol paramname="{binding name}" value="{binding value}"/> </datatemplate> <datatemplate x:key="parameterlisttemplate"> <c:parameterlistcontrol paramname="{binding name}" value="{binding value}" items="{binding parameters}" /> </datatemplate> <s:parametertemplateselector x:key="parameterselector" textparametertemplate="{staticresource textparametertemplate}" parameterlisttemplate="{staticresource parameterlisttemplate}"/> </usercontrol.resources> <itemscontrol itemssource="{binding parameters}" itemtemplateselector="{staticresource parameterselector}"></itemscontrol>
when run application, textparameter
in mainviewmodel.parameters
loaded (vm.name
, vm.value
properties binded uc.paramname
, uc.value
. contrariwise, parameterlist
in mainviewmodel.parameters
partially loaded. uc.name
binded uc.paramname
vm.parameters
not binded uc.items
(the uc.datacontext
vm, vm.parameters
defined, uc.items
desperately null
).
do have idea of missing ? (i not native speaker, excuse me if english hurts you)
i see have binding mainviewmodel.parameters -> parameterlistcontrol.items might missing binding parameterlistcontrol.items -> parameterlist.parameters. (that's assuming parameterlist viewmodel parameterlistcontrol - provide code datacontext bindings.)
see the accepted answer on question. (ignore comment on caliburn.micro - same solution worked me in mvvm light.)
essentially, in constructor of parameterlistcontrol create binding between dependency property of view , viewmodel's property.
(also, dbl right in comments when debugging binding problems, "unimportant" "plumbing" code omitted important.)
Comments
Post a Comment