пятница, 6 июня 2008 г.

How Josh saved my day or the power of the Model side

May be I missed something but it was a challenge for me to maintain a following situation.

Imagine, for example, that you have an object with some property that can be chosen from some external list of values. How would you solve it? You'll, probably, put in UI some Selector control, say it would be a ComboBox, bound with list of available values. You would bind its SelectedItem property with underlying object's target property by means of OneWayToSource binding. Quite easy so far, isn't it? Just write ViewModel for this view that will publish this object and will keep all logic. Now let's make this example a little trickier. Imagine that you have to edit a list of said objects and you have to choose a unique value for every particular object. You see now that it is not so easy anymore? And to puzzle you completely picture yourself a Configuration Dialog that hosts your editing UI. This dialog has OK and cancel button and your objects' state must stay sync with what you do. Something like the following:

Don't be scared this UI is just for tests =).

I've written a number of posts about M-V-VM pattern, but in spite of was completely cumbered with this scenario. I do have an infrastructure to edit objects (I've written about it here), but in this case I was baffled. The problem was with this list of available values. To make it easier for understanding I'll introduce some classes.

The scenario is as follows: I have a number of visual addins (powered by Managed Add-Ins Framework) (I really should write about it, but I'll do it later) that I want to host in my UI. But they can be hosted only in the named Workspaces (a CAB's notion, it is not a WPF restriction it is my decision). You can see the model now. I've got AddInDescriptor objects that keep a name of containing workspace and addin token. This name can be set in Configuration Dialog by browsing and selecting one from available list. This dialog can commit or rollback changes and at next start it must show appropriate content.

descriptor class. just a DTO.

descriptor data template.

listbox item style.

The problem
looks like this: when I first set the name of containing workspace and then rollback and start again, combobox updates current item binding (AddInDescriptor.ContainingWorkspaceName property) with a valid empty workspace name and then explicitly sets the first item as current without update. This strange behavior violates all the logic written in a style's triggers. I tried to fix this broken behavior in a whole bunch of ways but neither of them worked.

And this is whe
n an idea dawned at me! I remembered a beautiful article written by WPF-rock star (of course it was Josh, who else?) about the simplest way to manage a TreeView in WPF. Here it is: http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx. I say this article is a MUST read.

And then I thought: "What is the difference? I have to do the same with ComboBox in my case.", and everything became clear.

I've created a AddInDescriptorPresenter class that held all the UI related logic. It is nothing more then a UI-proxy to the object and is intended to publish object- and UI-related properties. This class accepts a CollectionView (a built-in VM for collection) which a Combo is bound with.

AddIn descriptor presenter.

A list of these proxies is created and exposed by UI ViewModel that keeps a list of AddInDescriptors.

You might be interested what the TransactionalTune is.
TransactionalTune is a special data container, a volatile transaction manager based on Juval Lowy's fantastic article. You can read more here.
All this UI-proxy does is controlling the state of CollectionView that is synchronized with ComboBox.

AddInDescriptorPresenter code.

Template to render this presenter became a little bit easier.

AddInDescriptorPresenter data template.

I've spent a couple of days trying to solve this problem writing tons of different xamls and code-behinds, and it took me about an hour to make everything work correctly with the ViewModel!
You see now that it is powerful?

Good luck!

Комментариев нет: