Monday, 15 February 2010

wpf - Binding on dynamically-added elements -



wpf - Binding on dynamically-added elements -

tptb have decided our app must run in single window, popping new windows in modal mode not allowed.

and naturally, have ui design involves popping modal dialogs on place.

so added top-level grid window. in grid defined no rows or columns, draws in row 0/column 0.

the first element in grid grid contained displayed in window. sec full-sized border gray, semi-transparent background. rest borders wide margins , white backgrounds, containing various usercontrols needed displayed popups. first had visibility="collapsed".

and then, when needed show popup, i'd set visibility="visible" on grayness background , on appropriate usercontrol. result nice shadowbox effect worked fine.

until decided popups needed able display popups. in non-predictable order.

the limitation of method had implemented, using visibility="collapsed" elements in grid order fixed. usercontrolb displayed on top of usercontrola, if usercontrolb asked have usercontrola displayed. , that's not acceptable.

so next effort define various usercontrols in window.resources, , add together them grid in code:

this.mastergrid.children.add(this.resources["usercontrola"] usercontrola);

and works. bindings messed up.

as example, 1 of controls supposed bind property currentitem of collection in fellow member object of window's viewmodel. when had command defined invisible item in grid, worked fine. when defined resource, property null - never bound.

so tried binding in code, after added grid:

usercontrola.setbinding(usercontrola.myproperty, new binding() { source = this.viewmodel.mycollection.currentitem });

and compiles , runs fine, i'm not binding right object.

the first time display usercontrol, see right object bound it. when close it, , move currentitem in collection different object, , display usercontrol again, still see first object bound. if close again, , open 3rd time, see right object bound control.

i've checked in code, , currentitem i'm binding right, every time, seems take every other time.

so tried explicitly clearing binding, first:

bindingoperations.clearbinding(usercontrola, usercontrola.myproperty); usercontrola.setbinding(usercontrola.myproperty, new binding() { source = this.viewmodel.mycollection.currentitem });

but doesn't seem have made difference.

in all, feels i'm running downwards rabbit hole, chasing deeper , deeper complexity, solve should simple problem.

does have suggestions to:

how binding work on dynamically-added elements, or how arbitrarily-ordered popups display, shadowboxes, without using dynamically-ordered elements?

thanks in advance.

while seems odd me can't create new windows, recommend not complicate much doing unnecesary things such storing views in mainwindow's resources.

it improve if added new instances of these elements observablecollection:

xaml:

<window x:class="wpfapplication4.window8" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:wpfapplication4" title="window8" height="300" width="300"> <window.resources> <datatemplate datatype="{x:type local:viewmodel1}"> <stackpanel background="green"> <textblock text="this viewmodel1!!"/> <textblock text="{binding text}"/> </stackpanel> </datatemplate> <datatemplate datatype="{x:type local:viewmodel2}"> <stackpanel background="blue" horizontalalignment="center"> <textblock text="this viewmodel2!!"/> <textblock text="{binding text2}"/> </stackpanel> </datatemplate> <datatemplate datatype="{x:type local:viewmodel3}"> <stackpanel background="red" verticalalignment="center"> <textblock text="this viewmodel3!!"/> <textblock text="{binding text3}"/> <textbox text="{binding text3}"/> </stackpanel> </datatemplate> </window.resources> <dockpanel> <button width="100" content="add" click="add_click" dockpanel.dock="top"/> <button width="100" content="remove" click="remove_click" dockpanel.dock="top"/> <listbox itemssource="{binding activewidgets}" selecteditem="{binding selectedwidget}"> <listbox.template> <controltemplate> <itemspresenter/> </controltemplate> </listbox.template> <itemscontrol.itemspanel> <itemspaneltemplate> <grid isitemshost="true"/> </itemspaneltemplate> </itemscontrol.itemspanel> <listbox.itemcontainerstyle> <style targettype="listboxitem"> <setter property="verticalalignment" value="stretch"/> <setter property="horizontalalignment" value="stretch"/> <setter property="template"> <setter.value> <controltemplate targettype="listboxitem"> <contentpresenter contentsource="content"/> </controltemplate> </setter.value> </setter> </style> </listbox.itemcontainerstyle> </listbox> </dockpanel> </window>

code behind:

using system.linq; using system.windows; using system.collections.objectmodel; using system; namespace wpfapplication4 { public partial class window8 : window { private widgetsviewmodel widgets { get; set; } public window8() { initializecomponent(); datacontext = widgets = new widgetsviewmodel(); } private random rnd = new random(); private int lastrandom; private void add_click(object sender, routedeventargs e) { var random = rnd.next(1, 4); while (random == lastrandom) { random = rnd.next(1, 4); } lastrandom = random; switch (random) { case 1: widgets.activewidgets.add(new viewmodel1() {text = "this text"}); break; case 2: widgets.activewidgets.add(new viewmodel2() { text2 = "this text" }); break; case 3: widgets.activewidgets.add(new viewmodel3() { text3 = "this yet text" }); break; } widgets.selectedwidget = widgets.activewidgets.lastordefault(); } private void remove_click(object sender, routedeventargs e) { widgets.activewidgets.remove(widgets.selectedwidget); widgets.selectedwidget = widgets.activewidgets.lastordefault(); } } public class widgetsviewmodel: viewmodelbase { public observablecollection<viewmodelbase> activewidgets { get; set; } private viewmodelbase _selectedwidget; public viewmodelbase selectedwidget { { homecoming _selectedwidget; } set { _selectedwidget = value; notifypropertychange(() => selectedwidget); } } public widgetsviewmodel() { activewidgets = new observablecollection<viewmodelbase>(); } } public class viewmodel1: viewmodelbase { public string text { get; set; } } public class viewmodel2: viewmodelbase { public string text2 { get; set; } } public class viewmodel3: viewmodelbase { public string text3 { get; set; } } }

just re-create , paste code in file - new - wpf application , see results yourself.

since grid places lastly ui element added topmost, see adding items observablecollection makes these "different widgets" appear on top of each other, topmost beingness lastly 1 added.

the bottom line is, when widgeta requests open widgetb, create new widgetbviewmodel , add together activewidgets collection. then, when widgetb no longer needed, remove it.

then, it's matter of putting usercontrols within proper datatemplate each viewmodel. suggest maintain separate viewmodel each of widgets, , if need share info between them, share info between viewmodels. don't effort things listbox itemssource="{binding whatever, relativesource={relativesource findancestor, ancestortype=window}" unless have reason to.

this way no longer have deal panel.zindex stuff. maybe can create couple of attached properties deal things focus , whatnot, approach dead simple, , far more performant visibility , resources approaches.

wpf xaml

No comments:

Post a Comment