views:

165

answers:

2

A little background: I am loading a WPF UI from a database which is stored in a table of properties (Control type, label, margin, etc.) which I load into a class I call ControlPresenter. Basically I set ControlPresenter as the DataContext of a ContentPresenter and use a TemplateSelector to choose which type of control to load. The DataTemplate(s) load their properties off the DependencyProperties exposed by the ControlPresenter.

This all works extremely well. The problem I'm running into comes when I try to bind data (e.g. TextBox's Text property to a presenter's Name property) in other presenters (which have normal properties that implement INotifyPropertyChanged) to these controls. The control's DataContext is the associated ControlPresenter so I can't bind directly to the other presenters and I can't set up two bindings on the same dependency property (I can't bind the control and the desired presenter property to the same DP).

Possible solutions:

  1. I could convert all of the other presenters to use DPs (extremely time consuming and prone to cause problems with inheritance)
  2. I could use two DPs for each property I want to pass through and try to tie them together through changed notifications

Both of these seem problematic and prone to breaking so I'm hoping someone else has come up with a better solution.

Edit: I came up with a solution that works fairly well (see below). Thanks to everyone that looked at this and if you come up with a better method than I did please let me know.

A: 

There are several ways of accessing out of scope DataContexts.

1) ElementName Binding

This is probably the least useful as, in most real world scenarios, what you are trying to bind to is out of namescope anyway. But it is a way to access a parallel or parent Data Context as long as the Framework Element is within namescope.

<TextBox Text="{Binding ElementName=ControlSomewhereElseBoundToSomeOtherControlPresenter, Path=DataContext.SomeTextPropertyOnTheControlPresenter}" />

2) RelativeSource Binding

This is similar to #1 but using RelativeSource to navigate to the appropriate visual element and grab the DataContext. Of course, this assumes that the DataContext you are trying to get at is ABOVE where you currently are in the Visual Tree.

3) Use a Static Relay

You could expose your alternative ControlPresenters within a static class that acts as a facade to your ViewModel. Then, within each ViewModel construct instance properties that pass through to the static methods / properties. This is a common way for achieving shared storage across multiple View Models. I realize this technique would require you to alter your pattern a bit, but adding a simple ViewModel wrapper around these "ControlPresenter" classes seems a lot more simple than the options you described.

markti
hmm, I'll have to see if I can get option number 3 working.
Bryan Anderson
A: 

I ended up setting the bindings in code by setting Binding.Source to the desired ViewModel and then using the path in the database for Binding.Path. I store the bindings in a Dictionary(Of DependencyProperty, BindingBase) and when the Control loads I use BindingOperations.SetBinding to set the bindings on the control. This seems to work pretty well except for occasional order of operations issues (e.g. If you set SelectedItem/SelectedValue prior to the ItemsSource it will be set but won't display in a ComboBox).

Bryan Anderson