Click here to Skip to main content
Click here to Skip to main content

Attaching a Virtual Branch to the Logical Tree in WPF

By , 6 May 2007
 

Introduction

This article demonstrates how to create a "virtual branch" of the logical tree in a WPF user interface. By "virtual branch" I mean a set of elements which is not physically in a logical tree, but can make use of the DataContext which propagates down. Elements in a virtual branch are not recognized by the LogicalTreeHelper, nor do they participate in the routed event and command systems. The impetus for attaching a virtual branch to the logical tree is that it allows for data binding scenarios that are otherwise impossible.

Instead of just diving straight into the code and markup which creates a virtual branch, we will first review a problem that can be solved by using one. Once we have ensconced the rather abstruse notion of "virtual branches" in a more approachable context we will then review what general problem they solve, and the details of their implementation.

The problem

Suppose that we create a simple application which allows us to select a number on a Slider, and then enter a multiple of that number into a TextBox. The application should provide a visual cue when the number in the TextBox is not a multiple of the number selected on the Slider. Perhaps the user interface might look like this:

The two numbers entered by the user are stored in an instance of a simple class named DivisionNumbers, which implements this interface:

interface IDivisionNumbers : INotifyPropertyChanged
{
 int Dividend { get; set; }
 int Divisor { get; set; }
}

In the Window's constructor we configure an instance of DivisionNumbers and set it as the Window's DataContext so that all elements in the application can bind to it, like so:

public Window1()
{
 InitializeComponent();

 DivisionNumbers nums = new DivisionNumbers();
 nums.Divisor = 1;
 nums.Dividend = 1;
 this.DataContext = nums;
}

The logic which validates the number in the TextBox was put into a ValidationRule subclass. An instance of that class is added to the ValidationRules property of the Binding associated with the TextBox's Text property. When the user types a number into the TextBox our ValidationRule checks to see if that number is valid. This XAML looks something similar to:

<TextBox x:Name="dividendTextBox">
  <TextBox.Text>
    <Binding Path="Dividend" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <local:IsMultipleOfValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

An incomplete version of our custom ValidationRule is implemented like this:

public class IsMultipleOfValidationRule : ValidationRule
{
 public override ValidationResult Validate(
    object value, CultureInfo cultureInfo )
 {
  try
  {
   int dividend = (int)Convert.ChangeType( value, typeof( int ) );

   int divisor = // Get the divisor somehow...

   if( dividend % divisor == 0 )
    return ValidationResult.ValidResult;

   return new ValidationResult(
    false,
    "The number is not a multiple of " + divisor );
  }
  catch
  {
   return new ValidationResult( false, "Not a number." );
  }
 }
}

A problem now reveals itself. How can we get the value of the divisor selected by the user from within the Validate method?

We cannot add an integer property called Divisor to IsMultipleOfValidationRule and bind to it, because only dependency properties can be bound. We cannot add a dependency property to the class either because they can only exist in classes which derive from DependencyObject. ValidationRule does not derive from that class.

Even if we did somehow add an integer dependency property named Divisor to IsMultipleOfValidationRule it would not help. The ValidationRule objects owned by a Binding are not added to the logical tree. Since DataContext is inherited only by elements in the logical tree, our custom ValidationRule would have no way to access that value. To top it all off, ValidationRule does not even have a DataContext property since it does not derive from FrameworkElement!

Similarly, ValidationRules are never added to a namescope. Since the ValidationRule is not in a namescope you would not be able to bind to the Slider via the ElementName property of Binding. The lookup process used to resolve ElementName to an element requires that the Binding is tied to an instance of a DependencyObject subclass which was added to the same namescope as the target element.

How can we elegantly overcome this seemingly impossible technical barrier to achieve our modest goal?

The solution

Attaching a virtual branch to a logical tree can provide objects that are not in the tree with data they need from it. Conceptually speaking, attaching a virtual branch to a logical tree is akin to tapping your neighbor's telephone line so that you can listen to their phone conversations about topics which interest you. We will, instead, tap into a user interface's logical tree and make use of the DataContext value which propagates down. The only "wire splicing" involved with our technique is to add a touch of XAML to a certain element in the logical tree.

Simply put, elements in a virtual branch can bind against the DataContext of the logical tree to which the branch is attached. It is possible to attach a virtual branch to any element in the logical tree, in case certain subsections of the tree are assigned a different DataContext to bind against. You can apply more than one virtual branch to the same logical tree, if necessary.

Without getting into implementation details just yet, let's review what a virtual branch is and what it is not. The next section in this article shows how to implement the pattern.

The term "virtual branch" is neither an official WPF term nor is it represented by any programmatic construct (i.e. there is no VirtualBranch class). It is a term we can use to refer to a reusable pattern which I designed to overcome the aforementioned technical limitations imposed by the WPF data binding model.

What I refer to as a "physical branch" is a hierarchical grouping of elements which actually exists in a logical tree. Elements in a physical branch are returned by methods of LogicalTreeHelper. Elements in a virtual branch are not. Elements in a physical branch participate in the routed event and command systems. Elements in a virtual branch do not. Elements in a virtual branch are not actually in the logical tree to which the branch is attached.

Implementing a virtual branch

It is very easy to create a virtual branch. There are only three pieces to the puzzle. For a visual explanation of the technique we are about to examine, refer to the diagram below:

Step 1 – Go build a bridge

Add a FrameworkElement to the Resources collection of the element to which you want to attach the virtual branch. This element acts a bridge between the logical tree and the virtual branch. The logical tree's DataContext is pushed across the bridge via data bindings. The bridge element becomes the root node of the virtual branch. In this article's demo application, the bridge element is added to the Window's Resources, as seen below:

<Window.Resources>
  <!-- This is the "root node" in the virtual branch
       attached to the logical tree. It has its
       DataContext set by the Binding applied to the
       Window's DataContext property. -->
  <FrameworkElement x:Key="DataContextBridge" />
</Window.Resources>

Step 2 – Push the DataContext across the bridge

At this point we have an element in place ready to expose the logical tree's DataContext to elements in the virtual branch. Now we need to make sure that the bridge element's DataContext always has the same value as one particular element in the logical tree. We will accomplish this by putting the rarely used 'OneWayToSource' binding mode to use, as seen below:

<Window.DataContext>
  <!-- This Binding sets the DataContext on the "root node"
       of the virtual logical tree branch.  This Binding
       must be applied to the DataContext of the element
       which is actually assigned the data context value. -->
  <Binding
    Mode="OneWayToSource"
    Path="DataContext"
    Source="{StaticResource DataContextBridge}"
    />
</Window.DataContext>

The Binding seen above ensures that whenever the Window's DataContext property is set, the new value will be pushed into the bridge element's DataContext as well. Ideally the Window would not need to have this Binding applied to its DataContext. It would be cleaner if the bridge element could contain that Binding (for the sake of encapsulation). However it is necessary to establish this Binding on the Window's DataContext because the bridge element is a resource living in a ResourceDictionary, and thus cannot bind to elements in the element tree.

It is important to note that the Binding seen above must be applied to the element in the logical tree to which you actually assign the DataContext property a value, as opposed to some other element which just happens to inherit that value. In the demo application the DataContext is set on the Window, so the Binding is applied to the Window's DataContext property.

Step 3 – Pull the DataContext off the bridge

The last step is to make use of the DataContext which was smuggled over to the virtual branch. Here is our opportunity to finally get the value of the divisor from within the IsMultipleOfValidationRule, as discussed earlier. This step can be broken into three tasks:

Step 3a – Create a data container class

All nodes in a virtual branch should derive from FrameworkElement, even if they are only meant to hold a simple value. Deriving from FrameworkElement gives the object a DataContext dependency property, and enables us to create our own dependency properties (FrameworkElement derives from DependencyObject).

Below is the class which will hold the divisor to be used by our validation rule:

/// <summary>
/// Stores an integer in the Value property.  Derives from
/// FrameworkElement so that it gets the DataContext property.
/// </summary>
public class IntegerContainer : FrameworkElement
{
 public int Value
 {
  get { return (int)GetValue( ValueProperty ); }
  set { SetValue( ValueProperty, value ); }
 }

 public static readonly DependencyProperty ValueProperty =
  DependencyProperty.Register(
  "Value",
  typeof( int ),
  typeof( IntegerContainer ),
  new UIPropertyMetadata( 0 ) );
}

Step 3b – Use the data container

Now it is time to put IntegerContainer to use in the IsMultipleOfValidationRule class. We will expose an instance of that class as a public read-write property. Here is the complete version of our validation rule:

public class IsMultipleOfValidationRule : ValidationRule
{
 private IntegerContainer divisorContainer;
 public IntegerContainer DivisorContainer
 {
  get { return divisorContainer; }
  set { divisorContainer = value; }
 }

 public override ValidationResult Validate(
    object value, CultureInfo cultureInfo )
 {
  try
  {
   int dividend = (int)Convert.ChangeType( value, typeof( int ) );

   int divisor = this.DivisorContainer.Value;

   if( dividend % divisor == 0 )
    return ValidationResult.ValidResult;

   return new ValidationResult(
    false,
    "The number is not a multiple of " + divisor );
  }
  catch
  {
   return new ValidationResult( false, "Not a number." );
  }
 }
}

Step 3c – Bind to the bridge

The final task is to bind the IntegerContainer's DataContext to the bridge element's DataContext. Once the IntegerContainer's DataContext is bound it will reference the same DivisionNumbers object as all of the elements in the logical tree. At that point we can bind to its Divisor property with ease. The following XAML configures the TextBox and our custom validation rule:

<TextBox x:Name="dividendTextBox">
  <TextBox.Text>
    <Binding Path="Dividend" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <local:IsMultipleOfValidationRule>
          <local:IsMultipleOfValidationRule.DivisorContainer>
            <!-- This IntegerContainer is the "child node" of
                 the DataContextBridge element, in the virtual
                 branch attached to the Window's logical tree. -->
            <local:IntegerContainer
              DataContext="{Binding
                            Source={StaticResource DataContextBridge},
                            Path=DataContext}"
              Value="{Binding Divisor}"
              />
          </local:IsMultipleOfValidationRule.DivisorContainer>
        </local:IsMultipleOfValidationRule>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

Parting words

I believe that this pattern will be put to use in ways I never imagined. There are most likely ways to use virtual branches that are detrimental or destabilizing to an application, so be careful with them. If you use a virtual branch to solve a problem unlike the one shown here, please leave a comment on this article's message board explaining the situation.

History

  • May 6, 2007 - Created article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Josh Smith
Software Developer (Senior) Cynergy Systems
United States United States
Member
Josh creates software, for iOS and Windows.
 
He works at Cynergy Systems as a Senior Experience Developer.
 
Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.
 
Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.
 
Check out his Advanced MVVM[^] book.
 
Visit his WPF blog[^] or stop by his iOS blog[^].

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionException thrown by XAML codememberaloifolia26 Jul '12 - 4:23 
I just tried to implement a user control which should act as a numeric kind of which textbox. For validation purposes I used your approach as I wanted to parameterize the validation rule. I strictly sticked to your code, nevertheless the VS compiler throws an exception that is caused by the container line:
<local:IntegerContainer DataContext="{Binding
                                     Source={StaticResource DataContextBridge},
                                     Path=DataContext}"
                        Value="{Binding Divisor}">
It seems as if IntegerContainer can not be instantiated. Ever came across such behaviour?
SuggestionIncredible shortcut if you're using MVVM Light or already have DataContext available in XAML.. Look withinmembergrahamoneale16 May '12 - 14:34 
Firstly, thanks Josh for a fantastic article, without it I wouldn't have even discovered what I believe to be an awesome shortcut.
 
I know this will sound profound guys, but I believe I have found a way to cut down a lot of this required code if you're using MVVM Light, or for anyone who can statically access their data context.
 
I did however only test this in a 'dynamic bindable validation scenario' Josh discusses in the article.
 
Firstly, I implemented all of your code within this article Josh and it worked great, then I went 'hold on', I already have a reference to my data context I can fetch via a universal binding call of {Binding Main, Source={StaticResource Locator}} anyway, so I wonder if I can skip going via the Bridge as that's all it's serving up to me anyway, just bunny-hopping through a different Binding call. So I tried short-curcuiting the DataContextBridge all together and just set my already available DataContext directly on my IntegerContainer validation attribute, and it worked fine! I was quite stunned.
 
Take a look guys, in essence, the only custom code required to hook up dynamic validation for my textbox was this:
 
<Binding.ValidationRules>
  <v:RangeRule>
    <v:RangeRule.RangeContainer>
      <v:IntegerContainer DataContext="{Binding Main, Source={StaticResource Locator}}" Value="{Binding ValueToTest}" />
    </v:RangeRule.RangeContainer>
  </v:RangeRule>
</Binding.ValidationRules>
 
So no need for a DataContextBridge FrameworkElement, binding tag in lt;code>Window.DataContext, or
this.DataContext assignment in constructor (it's already set in the <Window /> declaration of the xaml file.)
 
Not to undermine the use of the DataContextBridge, I believe it would still be required if you are setting your data context from code behind and don't have access to your data context typically in a neat binding call, but maybe Josh can elaborate on this?? Also without this article I wouldn't have known how I could have applied my binding rule to my IntegerContainer, so that's why most of this technique described in the article is still required (with the ValidationRule and use of Dependency Properties).
 
Tell me what you think guys, and Josh, and hopefully it works for you too Smile | :)
GeneralRe: Incredible shortcut if you're using MVVM Light or already have DataContext available in XAML.. Look withinmembergrahamoneale16 May '12 - 14:57 
Omg! I just discovered the IDataErrorInfo interface *RAGE*
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/[^]
 
This is what I wanted all along to support my dynamic validation!! lol, oh well. So Josh why would we do it this way when we can simply test dynamic properties of our model using an IDataErrorInfo implementation?
 
Is it just to support generic style validation xaml attributes that can be put anywhere?
QuestionValidation with binding to parent DataContext [solved] [modified]memberm.gnu.20 Mar '12 - 22:25 
I'm trying to use this for validation, but can't quite get it to work as I want.
 
I've got a DataGrid with two columns displaying a name and a value. The values are of different types, so I have a CellTemplateSelector which applies a template based on which viewmodel the current item uses. One of the templates displays a simple TextBox (with some extra stuff not included here) and I've setup my validation like this:
 
<DataTemplate x:Key="TextPropertyTemplate">
  <TextBox IsReadOnly="{Binding IsReadOnly}">
    <TextBox.Text>
      <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
          <local:TestValidationRule>
            <local:TestValidationRule.IsMandatoryContainer>
              <local:BooleanContainer Value="{Binding IsMandatory}"/> *insert magic here*
            </local:TestValidationRule.IsMandatoryContainer>
          </local:TestValidationRule>
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>
  </TextBox>
</DataTemplate>
 
I've got a ViewModel with the properties Value, IsReadOnly and IsMandatory (and then some). The properties Value and IsReadOnly works, but I also want to bind IsMandatory to the validator. I've realized that the validator doesn't inherit the DataContext so I'm clueless in how to setup the binding.
 
-------
 
Update: solved it using Artificial Inheritance Contexts in WPF[^]

modified 21 Mar '12 - 5:37.

QuestionFrameworkContentElement?memberfrank3326 Jan '12 - 11:39 
Hi Josh,
 
Thanks for the article, quick question :-
 
Is there any reason to inherit from FrameworkElement instead of FrameworkContentElement which seems more lightweight?
 
Thanks,
Frank
Frank

QuestionThis is the futurememberNaixandladders19 Jan '12 - 20:29 
Hey josh. Its 5 years in the future and as predicted, this pattern has become very usefull (at least for me).
 
I'm creating my own controls with built in validation so that the validation is easily reused, and this means I need to bind to the validation rules.
 
Just wanted to say thanks for the pattern
AnswerRe: This is the futurememberJosh Smith20 Jan '12 - 5:56 
That's great to hear. Thanks for letting me know.
 
I can't believe I wrote this article 5 years ago. OMG | :OMG: Time flies when you're having fun!
:josh:
Mole 2010 - debugging made easier

QuestionHow to use in Item within item control?memberxr280xr11 Jan '12 - 10:53 
Hi Josh,
 
I don't know if you're even connected to this anymore, but thanks so much for posting your idea. I followed the thread in the WPF forum to this example and tried it out, but it's not working for what I'm trying to do. I have an items control where each item has a CheckBox, a TextBox, and the Validator. If the CheckBox is checked, the validator should require the Text to be > 0. If it's not checked, the validator should allow the value to be 0. It should be so simple... I've implemented your idea, where the DependencyProperty is a property that tells my validator whether or not to allow zeros. I tried binding it by name to the CheckBox but it couldn't find it. So I tried binding it to the same property as the checkbox. That property uses INotifyPropertyChanged and ItemsControl is bound to a BindingList so it shoudl get notification when the item changes. When the ItemsControl is creating its items I can see my validator's DependencyProperty being set in the debugger, but after that it never changes even though the property its supposed to be bound to does. I can provide code in a forum thread if you'd be willing to check it out. Thanks again!
QuestionValidationRule Failures vs WarningsmemberRasko12310 Mar '11 - 4:52 
Hi Josh,
 
This was a great post and I have implemented it in my solution and works as expected.
 
However, I have an extra need that so far I could not find any posts that talk about it.
 
I need to separate invalid input vs "questionable-are you sure?" input.
 
For example if I have "Age" property which is an int, than if I receive "123abc" or "-10" as input I want to disregard input and I dont want to pass it down to the view model and model. And this works fine with current implementation.
However, let say that my expected range for the Age property is between 10 and 105. If I receive 8 or 110 as input I still want to pass that value to the VM and Model (since its valid) but I want to display a warning in the view. (that value is outside of expected range but still acceptable)
 
Ideally, ValidateResult instead of returning true/false (pass/fail) it could return Enum (Pass/Fail/Warning/Information ...) and than based on the return value I could style my view appropriately.
 
Do you, or anyone, have any suggestions how I can achieve this? Ideally, I would like to use already existing WPF infrastructure (ValidationRule or IDataErrorInfo) vs implementing my own stuff from scratch.
 
Thanks,
 
Rasko
GeneralPerformance Implications of Virtual BranchingmemberMember 420288512 Aug '10 - 8:40 
Hey Josh (or anyone else in the know),
 
I'm wondering what your thoughts are in terms of the performance implications of Virtual Branching? I've run through some tests and I can't see anything major, but I'm considering implementing this approach on a fairly large scale, and I'm just wondering what if any performance hits you could hypothesize?
 
Thanks,
Chris
GeneralRe: Performance Implications of Virtual BranchingmvpJosh Smith12 Aug '10 - 8:43 
Hey Chris,
 
The performance implications are not bad. Sure, this technique introduces some extra overhead, but it's negligible. Several people have told me that they use this approach extensively in their apps, and it works like a charm.
:josh:
Advanced MVVM[^]
Advance your MVVM skills

GeneralRe: Performance Implications of Virtual BranchingmemberMember 420288513 Aug '10 - 8:18 
Thanks Josh, I thought that was the case from my testing, but limited testing and real world are still worlds apart.
 
Thanks again for getting back to me so quickly.
 
Chris
QuestionCan you help me please with my sample?memberDudi Avramov1 Jun '10 - 1:07 
I have a class Bitmap inherited from UIElement.
This class has a dependency property, called Source, of type BitmapSource.
In the window.xaml i wrote
<local:Bitmap Source="{Binding Path=TheImage, ElementName=_this, Mode=OneWay}"/>
 
TheImage is another dependecy property in the window class.
I get "Cannot find governing FrameworkElement or FrameworkContentElement for target element".
Could you please help me how to change the code so it will work?
I failed to do the same with your sample because of wrong merging with your code.
 

Thanks,
Dudi
GeneralUsing virtual branch in a listviewmemberjoostvd18 Mar '10 - 5:10 
Hi,
 
My example contains a listview in which one of the columns must be validated against a maximum value. The maximum value can be entered in a textbox which is seperate from the listview. Using the virtual branch i am able to use the entered maximum value inside the validation rule (a dependency propery is used).
So far so good.
But the maximum value to be used is actually another column of the listview. So I though I do the OneWayToSource binding from the XAML item . When starting my application I get an 'InvalidOperationException' with message "The itemcollection should be empty before you use ItemsSource" (translated from the dutch message).
 
My question is: how must I do the OneWayToSource binding to be able to use the maximum value, defined in one column of the listview, inside the validation rule of another column of the listview.
 

Note: I have also tried to use an ElementSpy. In this case I basically face the same problem: where to define the ElementSpy?
 
Hopefully someone can help, since this puzzles me for some day now.
 
Joostvd
QuestionMaster slave with the DataContext bridgememberJony12312 Mar '10 - 1:35 
Hi,
 
I use the solution above for my master data and that is working fine.
 
My Master Binding data has an Observable collection that is shown in a list view.
 
I have created a set of Data, BindingData, DataValidator, DataValidatorContainer, and a DataValidationRule.
I use a data validator because I also want to validate the data out of the GUI.
 
this part is working without any problem.
 
I have created a same set for the "SlaveData"
and I have created a SlaveDataValidator in the master data.
 
but in the rule the container is ok but the validator object is a null reference.
 
I have placed breakpoints in the get/set in both containers and in validators but I never get the moment a valid value is set.
 
The "master" container has a Validator but my "Slave" Container not.
My "Master" binding data and my Slave Binding data both have a "SlaveValidator" and they are both set.
 
Every Slave element has a SlaveBindingData connected to it.
 
I have also looked into the ElementSpy and Artificial Inheritance Contexts in wpf both by Josh Smith but I still can't find the answer to my problems.
 
My data is binding perfect for the maste and for the slave but the validation rule is not working for the slave.
 
I have tried
<local:SlaveDataValidatorContainer
DataContext="{Binding Source={StaticResource DataContextBridge},
Path=DataContext.AdressesBindingData}"
SlaveValidator="{Binding MySValidator}" />

 
and this:
<ListView.Resources>
<local:ElementSpy x:Key="spy2"/>
</ListView.Resources>
.
.
.
<local:SlaveDataValidatorContainer
SlaveValidator="{Binding Source={StaticResource spy2}, Path=MySlaveValidator}"/>

 
and some more different once between those 2 using DataContextSpy Elementspy and BindingBridge unti now with no results.
 
is it impossible what I'm try to achieve?
 
Do I make a theoretical error in the way to reach my goal?
 
Jvdn
QuestionHow to use this in a DataTemplate?memberpiotri8512 Jan '10 - 7:48 
Hi,
 
I have a TextBox in a UserControl, which is designed to switch by code between different data types allowing users to type in either a "Path", a "Name" or a "Date". The current mode of the TextBox is represented by a defined string.
For this reason, I need to change the ValidationRules dependending on this string.
 
I wrote some code and tested it (without bindings) passing the string "manually" in xaml into the ValidationRule-class and it is working fine.
 
In summary, what I did is pretty the same what you did with "divisor", except that my TextBox is (and has to be) in a DataTemplate - which seems to lead to my following problem.
 
Trying to bind the string to a property in my ValidationRuleClass like in your solution causes an xaml-parse-exception with the information that the resource DataContextBridge is not found.
 
I'm sitting here for hours without getting along and now I'm not even sure if this problem is caused by me, a typo or if it is simply not possible what I want to do.
 
Is there any hint?
AnswerRe: How to use this in a DataTemplate?memberpiotri8520 Jan '10 - 11:56 
Hi,
 
I tryed DataContextSpy too, but it didn't work.
 
Finally, I got it to work with ElementSpy[^]
 
Regards,
piotri
GeneralRe: How to use this in a DataTemplate?mvpJosh Smith20 Jan '10 - 12:00 
Great! Thanks for the tip. Smile | :)
 
:josh:
Try Crack![^]
Sleep is overrated.

QuestionBrief explanation, pleasememberdavid freer16 Nov '09 - 10:29 
Thanks for the great insight. While I've used this succesfully in my own code, there is one part of this approach that escapes me. Could you explain what is happening in your snippet at the bottom.   I am accustomed to thinking of the 'property element' syntax as simply a way to set a property to some class or resource.   For example,
 
<Button>
   <Button.Background>
      <SolidColorBrush Color="Blue"/>
   </Button.Background>
<Button>
 
In the snippet below, not only is Window.DataContext not being set but, in fact, hi-jacked.   I cannot find any documentation that discusses such a possibility, which means there are probably other subtleties I don't know about. So, why does this work? Thanks in advance.
 
<Window.DataContext>
   <Binding
      Mode="OneWayToSource"
      Path="DataContext"
      Source="{StaticResource DataContextBridge}"
      />
</Window.DataContext>
AnswerRe: Brief explanation, pleasemvpJosh Smith16 Nov '09 - 10:50 
Great question. The property-element syntax is being used in that XAML to set a Binding on the window's DataContext property. Since the Binding's Mode=OneWayToSource it will never update the DataContext property, it merely listens for changes to it. WPF under the hood special-cases a dependency property set when the value is a Binding. It sets a binding on the property instead of setting a local value.
 
Does that help?
 
:josh:
Try Crack![^]
Sleep is overrated.

GeneralRe: Brief explanation, pleasememberDavidF16 Nov '09 - 18:21 
Thanks for the prompt reply and yes, it does; however, I have come across a scenario that does not work as expected. It deals with trying this trick within an ItemTemplate. I am attempting to pass the dataContextBridge as a ConverterParameter. With the lines commented below, dataContextBridge is passed to the ValueConverter via the ConverterParameter but, of course, dataContextBridge's DataContext is null.   If the lines are uncommented then, ValueConverter ceases to be called and the TextBlock's DataContext becomes null. I know this because of watching (via Mole) the AttachedProperty on the TextBlock. In that function the TextBlock's DataContext (as well as it's Grid parent) is null, whereas it wasn't before the lines where uncommented. I surmise that TextBlock.DataContext becomes null because it's parent's DataContext became null when the lines were uncommented. Should I expect this to work in a DataTemplate? Thanks in advance.
 
<DataTemplate x:Key="ParamSummaryTemplate">
      <Grid>
            <Grid.Resources>
           <FrameworkElement x:Key="dataContextBridge"/>
     </Grid.Resources>
     <!-- *****   uncomment results in null DataContext *****
     <Grid.DataContext>
           <Binding Mode="OneWayToSource" Path="DataContext"
                        Source="{StaticResource dataContextBridge}"/>
     </Grid.DataContext>
     -->
     <TextBlock
                  v:SummaryBindingUpdateBehavior.RegisterForForcedBindingUpdate="True"
           Text="{Binding Path=Value, Converter={cnvt:ParamToValueConverter},
              ConverterParameter={StaticResource dataContextBridge}}"/>
      </Grid>
</DataTemplate>
 
<DataTemplate x:Key="ModuleSummaryDataTemplate" >
      <Grid>
            <ItemsControl ItemTemplate="{StaticResource ParamSummaryTemplate}"
                  ItemsSource="{Binding QeParams}" />
      </Grid>
</DataTemplate>
GeneralRe: Brief explanation, pleasemvpJosh Smith19 Nov '09 - 5:06 
I don't think it would work in a DataTemplate used as an ItemTemplate. The problem, I believe, is that the DataContext is set on the item container, not on the root visual created by your template. If you're using an ItemsControl, the item container is a ContentPresenter, if you're using a ListBox, it's a ListBoxItem, etc. Perhaps try creating an ItemContainerStyle that has a Setter which applies a OneWayToSource binding on the item container's DataContext.
 
:josh:
Try Crack![^]
Sleep is overrated.

GeneralRe: Brief explanation, pleasemvpJosh Smith19 Nov '09 - 5:08 
Also try using DataContextSpy[^]
 
:josh:
Try Crack![^]
Sleep is overrated.

GeneralRe: Brief explanation, pleasememberdavid freer19 Nov '09 - 9:14 
Thank you very much for both replies. I will pursue the option you suggest.
GeneralAnother usage [modified]memberScottGBR5 Jun '09 - 5:37 
Josh,
 
First up, great article. I believe this is an elegant solution to the problem of using MVVM Commanding with InputBindings. My specific example is that I would like to bind my ViewModel ICommands to Mouse and Keyboard inputs on a TreeView, TreeViewItem, ListBox and ListBoxItem.
 
I'll let you know how I get on.
 
Scott
 
EDIT:
 
SUCCESS, you sir are a genius! It took me a little while to get my head around the container business, but eventually worked out that for my purposes the container just needs to implement ICommand.
 
Here's my command container (though still need to sort out the CanExecuteChanged event)...
internal class CommandContainer : FrameworkElement, ICommand
{       
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }
 
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandContainer));
 
    public bool CanExecute(object parameter)
    {
        return Command.CanExecute(parameter);
    }
 
    public event EventHandler CanExecuteChanged;
 
    public void Execute(object parameter)
    {
        Command.Execute(parameter);
    }
}
And a section of the relevant xaml...
<TreeView.InputBindings>
  <KeyBinding Key="Enter" >
    <KeyBinding.Command>
      <my:CommandContainer 
        DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}"
        Command="{Binding Path=AddRootNodeCommand}" 
        />
      </KeyBinding.Command>
  </KeyBinding>
</TreeView.InputBindings>
Proper UI-Logic separation and no code-behind, many thanks!
 
modified on Friday, June 5, 2009 12:19 PM

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 6 May 2007
Article Copyright 2007 by Josh Smith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid