Friday 15 August 2014

WPF use same datatemplate with different binding -



WPF use same datatemplate with different binding -

i have next datagrid

<window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d" x:class="venusproductinfoquerywpf.mainwindow" height="350" width="646" windowstyle="none" resizemode="noresize" topmost="false" mouseleftbuttondown="window_mouseleftbuttondown" allowstransparency="true" windowstartuplocation="centerscreen" showintaskbar="true"> <window.resources> <datatemplate x:key="datatemplate1"> <button tag="{binding name}" content="show" click="linkbutton_click"></button> </datatemplate> <datatemplate x:key="datatemplate2"> <button tag="{binding sex}" content="show" click="linkbutton_click"></button> </datatemplate> </window.resources>` <grid> <datagrid x:name="mydatagrid" horizontalalignment="left" margin="60,44,0,0" verticalalignment="top" height="223" width="402" autogeneratecolumns="false" autogeneratedcolumns="mydatagrid_autogeneratedcolumns"> <datagrid.columns> <datagridtextcolumn header="age" binding="{binding path=age}"></datagridtextcolumn> <datagridtemplatecolumn header="sex" celltemplate="{staticresource datatemplate2}"/> <datagridtemplatecolumn header="name" celltemplate="{staticresource datatemplate1}"/> </datagrid.columns> </datagrid> </grid> </window>

and in codebehind have:

using system.collections.generic; using system.collections.objectmodel; using system.windows; using system.windows.media; namespace wpfapplication1 { public partial class mainwindow : window { public mainwindow() { initializecomponent(); datasource = new datatable(); datasource.columns.add("age"); datasource.columns.add("name"); datasource.columns.add("sex"); addnewrow(new object[] { 10, "wang", "male" }); addnewrow(new object[] { 15, "huang", "male" }); addnewrow(new object[] { 20, "gao", "female" }); datagrid1.itemssource = datasource.asdataview(); } } }

the datatable has more 30 columns (i wrote 2 in here create easier follow).. question is: if want show same template style different binging source in every column, have define many different datatemplates (like datatemplate1, datatemplate2, ... see above) bind celltemplate of each datagridtemplatecolumn ? can define 1 datatemplate , in code or through other way dynamic set binding? give thanks answer!

there way not pretty,

for brevity using code behind view model , have removed exception handling , unnecessary lines.

i have convert object array person object (for reasons should become evident later in solution)

public class person { public string name { get; set; } public int age { get; set; } public string sex { get; set; } }

i have converted datasource observablecollection code behind now

public partial class mainwindow : window { public mainwindow() { items = new observablecollection<person>() { new person {age = 10, name = "wang", sex="male"}, new person {age = 15, name = "huang", sex="male"}, new person {age = 20, name = "gao", sex="female"} }; showcommand = new delegatecommand(executeshowcommand, canexecuteshowcommand); initializecomponent(); } private bool canexecuteshowcommand(object arg) { homecoming true; } private void executeshowcommand(object obj) { messagebox.show(obj != null ? obj.tostring() : "no parameter received"); } public delegatecommand showcommand { get; set; } public observablecollection<person> items { get; set; } }

the delegatecommand defined as

public class delegatecommand : icommand { private func<object, bool> _canexecute; private action<object> _execute; public delegatecommand(action<object> execute, func<object, bool> canexecute) { _canexecute = canexecute; _execute = execute; } public bool canexecute(object parameter) { homecoming _canexecute.invoke(parameter); } void icommand.execute(object parameter) { _execute.invoke(parameter); } public event eventhandler canexecutechanged; }

this allows utilize of commands , commandparameters in single template.

we utilize multivalueconverter info each button. uses header of column interrogate, using reflection, person object required value , pass parameter of command.

public class gridcelltovalueconverter : imultivalueconverter { public object convert(object[] values, type targettype, object parameter, system.globalization.cultureinfo culture) { object returnvalue = null; var person = values.first() person; var propertyname = values[1] == dependencyproperty.unsetvalue ? string.empty : (string)values[1]; if ((person != null) && (!string.isnullorwhitespace(propertyname))) { returnvalue = person.gettype().getproperty(propertyname).getvalue(person); } homecoming returnvalue; } public object[] convertback(object value, type[] targettypes, object parameter, system.globalization.cultureinfo culture) { throw new notimplementedexception(); } }

the final piece of puzzle xaml

<window x:class="stackoverflow.q26731995.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:q26731995="clr-namespace:stackoverflow.q26731995" title="mainwindow" height="350" width="525" datacontext="{binding relativesource={relativesource self}}"> <window.resources> <q26731995:gridcelltovalueconverter x:key="gridcell2value" /> <datatemplate x:key="buttoncolumndatatemplate"> <button content="show" command="{binding relativesource={relativesource mode=findancestor, ancestortype={x:type window}}, path=showcommand}"> <button.commandparameter> <multibinding converter="{staticresource gridcell2value}"> <binding /> <!-- person object --> <binding relativesource="{relativesource mode=findancestor, ancestortype={x:type datagridcell}}" path="column.header" /> <!-- name of field --> </multibinding> </button.commandparameter> </button> </datatemplate> </window.resources> <grid> <datagrid x:name="mydatagrid" horizontalalignment="left" margin="60,44,0,0" itemssource="{binding path=items}" verticalalignment="top" height="223" width="402" autogeneratecolumns="false"> <datagrid.columns> <datagridtextcolumn header="age" binding="{binding path=age}"></datagridtextcolumn> <datagridtemplatecolumn header="sex" celltemplate="{staticresource buttoncolumndatatemplate}"/> <datagridtemplatecolumn header="name" celltemplate="{staticresource buttoncolumndatatemplate}"/> </datagrid.columns> </datagrid> </grid> </window>

you should able cutting , past code new wpf app , see work. not have not take care of addnewitem row grid adds (because have not turned off).

this can done collection of object array rather collection of person. so, need pass datagridcellspanel converter , utilize header calculate index of required value. convert like

<multibinding converter="{staticresource gridcell2value}"> <binding /> <!-- info --> <binding relativesource="{relativesource mode=findancestor, ancestortype={x:type datagridcell}}" path="column.header}" /> <!-- name of field --> <binding relativesource="{relativesource mode=findancestor, ancestortype={x:type datagridcellspanel}}" /> <!-- panel contains row --> </multibinding>

the converter code along lines

public object convert(object[] values, type targettype, object parameter, system.globalization.cultureinfo culture) { object returnvalue = null; var info = values.first() object[]; var property = values[1] == dependencyproperty.unsetvalue ? string.empty : (string)values[1]; var panel = values[2] datagridcellspanel; var column = panel.children.oftype<datagridcell>().firstordefault(c => c.column.header.equals(property)); if (column != null) { returnvalue = data[panel.children.indexof(column)]; } homecoming returnvalue; }

i hope helps.

wpf binding datatemplate

No comments:

Post a Comment