datagrid - WPF troubles hooking CollectionChanged events -
i have datagrid need calculate total of price column of nested datagrid, so:
i'm trying follow this example items observable collection each person object gets notified on changes. difference i'm implementing inside class, not view model.
public class person : notifyobject { private observablecollection<item> _items; public observablecollection<item> items { { return _items; } set { _items = value; onpropertychanged("items"); } } private string _name; public string name { { return _name; } set { _name = value; onpropertychanged("name"); } } public double total { { return items.sum(i => i.price); } set { onpropertychanged("total"); } } public person() { console.writeline("0001 constructor"); this.items = new observablecollection<item>(); this.items.collectionchanged += items_collectionchanged; this.items.add(new item()); } private void items_collectionchanged(object sender, notifycollectionchangedeventargs e) { console.writeline("0002 collectionchanged"); if (e.newitems != null) foreach (item item in e.newitems) item.propertychanged += items_propertychanged; if (e.olditems != null) foreach (item item in e.olditems) item.propertychanged -= items_propertychanged; } private void items_propertychanged(object sender, propertychangedeventargs e) { console.writeline("0003 propertychanged"); this.total = items.sum(i => i.price); } }
the code inside constructor doesn't hook events when new item initialized or existing 1 has been edited. therefore, items_propertychanged event never fires. can refresh entire list manually. doing wrong here?
or maybe there's different approach calculate total each person's purchase list?
below entire code if cares @ it.
xaml
<window x:class="collection_changed_2.mainwindow" 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" xmlns:local="clr-namespace:collection_changed_2" mc:ignorable="d" title="mainwindow" sizetocontent="height" width="525"> <grid> <grid.rowdefinitions> <rowdefinition height="auto"/> <rowdefinition /> </grid.rowdefinitions> <datagrid x:name="datagrid1" grid.row="0" itemssource="{binding datacollection}" selecteditem="{binding datacollectionselecteditem}" autogeneratecolumns="false" canuseraddrows="false" > <datagrid.columns> <datagridtextcolumn header="name" binding="{binding name}" width="2*"/> <datagridtemplatecolumn header="item/price" width="3*"> <datagridtemplatecolumn.celltemplate > <datatemplate> <datagrid x:name="datagriditem" itemssource="{binding items}" selecteditem="{binding relativesource={relativesource findancestor, ancestortype={x:type window}}, path=datacontext.itemsselecteditem}" background="transparent" headersvisibility="none" autogeneratecolumns="false" canuseraddrows="false" > <datagrid.columns> <datagridtextcolumn binding="{binding itemname}" width="*"/> <datagridtextcolumn binding="{binding price}" width="50"/> <datagridtemplatecolumn header="button" width="auto"> <datagridtemplatecolumn.celltemplate> <datatemplate> <stackpanel> <button content="+" command="{binding relativesource={relativesource findancestor, ancestortype={x:type window}}, path=datacontext.additem }" width="20" height="20"> </button> </stackpanel> </datatemplate> </datagridtemplatecolumn.celltemplate> </datagridtemplatecolumn> </datagrid.columns> </datagrid> </datatemplate> </datagridtemplatecolumn.celltemplate> </datagridtemplatecolumn> <datagridtextcolumn header="total" binding="{binding total, mode=twoway, updatesourcetrigger=propertychanged}" width="auto"/> <datagridtemplatecolumn header="buttons" width="auto"> <datagridtemplatecolumn.celltemplate> <datatemplate> <stackpanel verticalalignment="center"> <button command="{binding relativesource={relativesource findancestor, ancestortype={x:type window}}, path=datacontext.addperson}" width="20" height="20">+</button> </stackpanel> </datatemplate> </datagridtemplatecolumn.celltemplate> </datagridtemplatecolumn> </datagrid.columns> </datagrid> <stackpanel grid.row="1" margin="10"> <button width="150" height="30" content="refresh" command="{binding refresh}" /> </stackpanel> </grid> </window>
c#
using system; using system.collections.objectmodel; using system.collections.specialized; using system.componentmodel; using system.linq; using system.windows; using system.windows.data; using system.windows.input; namespace collection_changed_2 { public class item : notifyobject { private string _itemname; public string itemname { { return _itemname; } set { _itemname = value; onpropertychanged("itemname"); } } private double _price; public double price { { return _price; } set { _price = value; onpropertychanged("price"); } } } public class person : notifyobject { private observablecollection<item> _items; public observablecollection<item> items { { return _items; } set { _items = value; onpropertychanged("items"); } } private string _name; public string name { { return _name; } set { _name = value; onpropertychanged("name"); } } public double total { { return items.sum(i => i.price); } set { onpropertychanged("total"); } } public person() { console.writeline("0001 constructor"); this.items = new observablecollection<item>(); this.items.collectionchanged += items_collectionchanged; this.items.add(new item()); } private void items_collectionchanged(object sender, notifycollectionchangedeventargs e) { console.writeline("0002 collectionchanged"); if (e.newitems != null) foreach (item item in e.newitems) item.propertychanged += items_propertychanged; if (e.olditems != null) foreach (item item in e.olditems) item.propertychanged -= items_propertychanged; } private void items_propertychanged(object sender, propertychangedeventargs e) { console.writeline("0003 propertychanged"); this.total = items.sum(i => i.price); } } public abstract class notifyobject : inotifypropertychanged { public event propertychangedeventhandler propertychanged; protected void onpropertychanged(string property) { if (propertychanged != null) propertychanged(this, new propertychangedeventargs(property)); } } public class relaycommand : icommand { private action<object> executedelegate; readonly predicate<object> canexecutedelegate; public relaycommand(action<object> execute, predicate<object> canexecute) { if (execute == null) throw new nullreferenceexception("execute"); executedelegate = execute; canexecutedelegate = canexecute; } public relaycommand(action<object> execute) : this(execute, null) { } public event eventhandler canexecutechanged { add { commandmanager.requerysuggested += value; } remove { commandmanager.requerysuggested -= value; } } public bool canexecute(object parameter) { return canexecutedelegate == null ? true : canexecutedelegate(parameter); } public void execute(object parameter) { executedelegate.invoke(parameter); } } public class viewmodel : notifyobject { public observablecollection<person> datacollection { get; set; } public person datacollectionselecteditem { get; set; } public item itemsselecteditem { get; set; } public relaycommand addperson { get; private set; } public relaycommand additem { get; private set; } public relaycommand refresh { get; private set; } public viewmodel() { datacollection = new observablecollection<person> { new person() { name = "friedrich nietzsche", items = new observablecollection<item> { new item { itemname = "phone", price = 220 }, new item { itemname = "tablet", price = 350 }, } }, new person() { name = "jean baudrillard", items = new observablecollection<item> { new item { itemname = "teddy bear deluxe", price = 2200 }, new item { itemname = "pokemon", price = 100 } } } }; additem = new relaycommand(additemcode, null); addperson = new relaycommand(addpersoncode, null); refresh = new relaycommand(refreshcode, null); } public void additemcode(object parameter) { var collectionindex = datacollection.indexof(datacollectionselecteditem); var itemindex = datacollection[collectionindex].items.indexof(itemsselecteditem); item newitem = new item() { itemname = "item_name", price = 100 }; datacollection[collectionindex].items.insert(itemindex + 1, newitem); } public void addpersoncode(object parameter) { var collectionindex = datacollection.indexof(datacollectionselecteditem); person newlist = new person() { name = "new_name", items = new observablecollection<item>() { new item() { itemname = "item_name", price = 100 } } }; datacollection.insert(collectionindex + 1, newlist); } private void refreshcode(object parameter) { collectionviewsource.getdefaultview(datacollection).refresh(); } } public partial class mainwindow : window { public mainwindow() { initializecomponent(); this.datacontext = new viewmodel(); } } }
do not use eventhandlers between viewmodels
- black magic , can bring u memory leaks, because of created references.
public interface iupdatesum { void updatesum(); } public class person : iupdatesum { /* ... */ public void updatesum() { this.total = items.sum(i => i.price); } /* ... */ } public class item { private iupdatesum sumupdate; private double price; public item(iupdatesum sumupdate) { sumupdate = sumupdate; } public double price { { return price; } set { raisepropertychanged("price"); sumupdate.updatesum(); } } }
i know not beautiful works
Comments
Post a Comment