May 09, 2007

How to update an explicit binding within a template

38UpdateExplicit

Mix07 in Vegas was awesome! I always get pumped up when I meet new customers that are using WPF and loving it! It gives me fuel to come back to rainy Seattle with a smile and extra energized to do my absolute best for WPF!

In today’s sample, I have a ListBox with editable data. When I change the data of one of the items, I want that to be reflected in the source, as usual. However, this time I set my Binding’s UpdateSourceTrigger to Explicit, which means I need to make that update by calling the UpdateSource() method on BindingExpression through code. Unfortunately, getting a handle to the BindingExpression in a ListBox scenario is a little tricky with the current bits of WPF, which is why I decided to write this post.

I will start with a quick explanation of two basic concepts, which are core to the understanding of this problem: Binding Mode and UpdateSourceTrigger. If this is too basic for you, you can skip the next couple of sections and safely jump to “Updating Explicit Bindings.”

Binding Mode

WPF Data Binding supports five binding modes (which you can set by using the Mode property of Binding):

  • One way - The data flows from the source to the target only. If you add a Binding to a TextBlock’s Text property and don’t specify the Mode, it will be one way by default.
  • Two way - The data flows from the source data to the target UI, and the other way around. A Binding on a TextBox’s Text property without the Mode specified is two way by default: changes in the source are reflected in the TextBox, and changes typed into the TextBox are also propagated back to the source.
  • One time - Like one way, but the UI doesn’t listen to change notifications in the source. You may want to consider using this mode if your source doesn’t support property change notifications. If you don’t care about changes in the source, setting your binding to one time will make it a little more performant.
  • One way to source - The opposite of one way: the data flows only from the target UI to the source. I have yet to see a good use of this binding mode - the scenarios that require it are quite rare.
  • Default - This is the same as not setting the Mode property at all. The Binding engine will look at the default mode specified at the time the DependencyProperty was registered, and will use that. Therefore, setting the Mode to Default will not mean the same thing for all DependencyProperties. For example, as I mentioned before, TextBlock’s Text has a default mode of one way, while the TextBox’s Text has a default of two way.

But how do we know the default Binding Mode of a DependencyProperty? How can we set a default Binding Mode when we define a new DependencyProperty?

.NET Reflector is your friend. With reflector, search for TextBox and look at the source for the static constructor (.cctor()). Here, you will be able to find the code used for registering the TextProperty DP:

    TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(TextBox.OnTextPropertyChanged), new CoerceValueCallback(TextBox.CoerceText), true, UpdateSourceTrigger.LostFocus));

Notice that a parameter is passed to the Register method indicating the default Binding Mode: FrameworkPropertyMetadataOptions.BindsTwoWayByDefault. If you use reflector to look at the registration for TextBlock’s Text DP, you will see that no such value is passed, in which case we assume the binding is one way by default.

It is not possible to make the binding one way to source or one time by default (we couldn’t think of any compelling user scenarios that would justify increasing the complixity of the API). However, it is possible to say that you don’t want the DP to support data binding at all - to do this, simply pass FrameworkPropertyMetadataOptions.NotDataBindable as a parameter.

Binding UpdateSourceTrigger

In the case of a binding that propagates values from the target to the source (Mode = TwoWay or OneWayToSource), we allow you to specify how you want that update to be triggered. There are three ways to cause the changes to propagate to the source (which you can set by using the UpdateSourceTrigger property of Binding):

  • LostFocus - The value will be updated when the element loses focus. This is the default behavior for TextBox (notice that UpdateSourceTrigger.LostFocus is specified when the Text dependency property was registered). When you type something in a TextBox, that value will be updated to the source when you change focus to some other element.
  • PropertyChanged - The value is updated every time it changes. In the TextBox scenario, the value will be updated every time you type a new character.
  • Explicit - The target value if not updated until you explicitly call “UpdateSource()” on the BindingExpression.

Updating Explicit Bindings

In today’s sample, I started by defining a source object (MySource) with a property Employees of type ObservableCollection<Employee>. Employee is a class that contains two properties: Name and Title, both of type string. The code for defining this source is straight forward, so I won’t show it here.

Then I added a ListBox to my XAML file that is bound to the Employees collection, and I added a Style for its items:

    <Window.Resources>
        <DataTemplate x:Key="nonEditableEmployee">
            <StackPanel Margin="2">
                <TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
                <TextBlock Text="{Binding Path=Title}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="editableEmployee">
            <StackPanel Margin="2" >
                <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
                <TextBox Text="{Binding Path=Title, UpdateSourceTrigger=Explicit}" Width="130" x:Name="tb"/>
            </StackPanel>
        </DataTemplate>

        <Style TargetType="ListBoxItem" x:Key="lbiStyle">
            <Setter Property="Height" Value="40" />
            <Setter Property="ContentTemplate" Value="{StaticResource nonEditableEmployee}" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="ContentTemplate" Value="{StaticResource editableEmployee}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <ListBox ItemsSource="{Binding Path=Employees}" ItemContainerStyle="{StaticResource lbiStyle}" IsSynchronizedWithCurrentItem="True" (…) Name="lb"/>
    <Button Content="Submit" Click="Submit" (…) />

In the Style for ListBoxItems, I am specifying that I want the DataTemplate applied to each of the items to be “nonEditableEmployee” except when the item is selected, in which case I want to use “editableEmployee”. Both DataTemplates display the Name and Title of the Employee, the only difference is that “editableEmployee” displays the Title using a TextBox instead of a TextBlock. So, when the user selects an employee in the ListBox, that employee’s Title becomes editable.

Below the ListBox I have a Submit Button. If I click on this Button after editing some data in the ListBox, I want that data to be submitted to the source. However, if I change the data in the ListBox and don’t click on the Button, I don’t want the source to be modified. Notice that I set the UpdateSourceTrigger of editable Title’s Binding to Explicit.

To achieve this behavior, I need to call the UpdateSource() on the BindingExpression’s instance. Getting this instance is a little tricky, so let’s think about it for a minute. The best way to think about this is by starting from the end, and walking backwards. Utimately, this is the code I want to write (assuming “be” is the BindingExpression instance, and “tb” is the TextBox’s instance):

    BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
    be.UpdateSource();

As you can see, in order to get the BindingExpression for the Binding in the TextBox’s Text property, I need to have a handle to that TextBox. Traversing the DataTemplate to get to the TextBox won’t give me the actual instance, it will only give me the FrameworkElementFactory used to create the TextBox instance. Fortunately, DataTemplate has a FindName method that, given the instance of the object the template is applied to, will give you a particular named part within its visual tree. So, assuming “cp” is the object the DataTemplate is applied to, this is the code I would like to write to get the actual TextBox:

    DataTemplate dt = (DataTemplate)(this.Resources["editableEmployee"]);
    TextBox tb = (TextBox)(dt.FindName("tb", cp));

We’re getting close. The next step is to figure out how to get to the element the DataTemplate is applied to. A DataTemplate is always applied to a ContentPresenter, which is an element present in the visual tree of every ContentControl. Since every item in the ListBox is wrapped with a ListBoxItem (which is a ContentControl), I simply have to get to the ContentPresenter instance in the ControlTemplate for the currently selected ListBoxItem. Here is what I did:

    Employee currentEmployee = (Employee)(lb.Items.CurrentItem);
    ListBoxItem lbi = (ListBoxItem)(lb.ItemContainerGenerator.ContainerFromItem(currentEmployee));
    ContentPresenter cp = GetObjectOfTypeInVisualTree<ContentPresenter>(lbi);

“GetObjectOfTypeInVisualTree” is a simple recursive method that walks the visual tree of an object (in this case, the current ListBoxItem) and returns the first element matching the type specified by the generic parameter (in this case ContentPresenter). Here is the complete code:

    private void Submit(object sender, RoutedEventArgs e)
    {
        Employee currentEmployee = (Employee)(lb.Items.CurrentItem);
        ListBoxItem lbi = (ListBoxItem)(lb.ItemContainerGenerator.ContainerFromItem(currentEmployee));
        ContentPresenter cp = GetObjectOfTypeInVisualTree<ContentPresenter>(lbi);

        DataTemplate dt = (DataTemplate)(this.Resources["editableEmployee"]);
        TextBox tb = (TextBox)(dt.FindName("tb", cp));

        BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
        be.UpdateSource();
    }

    private T GetObjectOfTypeInVisualTree<T>(DependencyObject dpob) where T : DependencyObject
    {
        int count = VisualTreeHelper.GetChildrenCount(dpob);
        for (int i = 0; i < count; i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(dpob, i);
            T childAsT = child as T;
            if (childAsT != null)
            {
                return childAsT;
            }
            childAsT = GetObjectOfTypeInVisualTree<T>(child);
            if(childAsT != null)
            {
                return childAsT;
            }
        }
        return null;
    }

This code is not only useful for submitting explicit bindings; it is useful for any scenario where you need to get to the instance of some element in your DataTemplate. It should be pretty easy to tweak this sample to suit your needs.

We do realize that this scenario is quite hard to implement, and we know it’s not that uncommon. We would like to make it easier, but unfortunately we will not be able to make that happen in the next release. At least for now you have a sample solution that should help you solve most scenarios. Hopefully we will have a chance to fix this in the platform in the near future.

Below is a screenshot of the final application. The ListBox on the right is bound to the same source. I added it to this sample so you can see when the source changes.

Here you can find the VS project with this sample code. This works with RTM WPF bits.

Update September 18, 2007: Here you can find this project working with Orcast Beta 2 bits.


25 Comments
  1. /sid

    Regarding I have yet to see a good use of this binding mode, to me it seems that Josh Smith found good use for it in http://www.codeproject.com/useritems/AttachingVirtualBranches

    • Bea

      It’s a cool trick. Thanks for pointing it out.
      I’m glad that OneWayToSource is useful after all.

      Bea

  2. RN

    Regarding accessing elements within your datatemplate from code-behind, you can do it in various ways. 3 different ways are shown below. Could you explain which is the best way and why?

    ___________________________________
    1st method:

    ContentPresenter cp = (ContentPresenter)(VisualTreeHelper.GetChild(TenantDetails, 0));

    GroupBox gb = (GroupBox) VisualTreeHelper.GetChild(cp, 0);

    TextBox tb = (TextBox) gb.FindName(“TenantNameTextBox”);

    //this works fine
    ___________________________________
    2nd method:

    ContentPresenter cp = (ContentPresenter)(VisualTreeHelper.GetChild(TenantDetails, 0));

    DataTemplate dt = (DataTemplate)(Application.Current.Resources["detailsTenantTemplate"]);

    TextBox tb = (TextBox)(dt.FindName(“TenantNameTextBox”, cp));

    // this works fine
    ___________________________________
    3rd method:

    TextBox tb = (TextBox)this.TenantDetails.Template.FindName(“TenantNameTextBox”, TenantDetails);

    // does not work in this case, but
    // have seen it work in some other
    // cases
    ___________________________________

    Info on the elements I have used:

    TenantDetails: ContentControl on my page

    detailsTenantTemplate: DataTemplate used by TenantDetails and this resides in a ResourceDictionary

    TenantNameTextBox: Name of the textBox element that I’m trying to access. This resides inside my datatemplate.

    - RN

    • Bea

      Hi RN,

      At first sight, both the first and second methods seem to be equally good. The first method has the slight advantage that you don’t need to know the name of the DataTemplate. The third method won’t work anytime you’re looking for an element within the DataTemplate, like in this sample. If you’re looking for something within the ControlTemplate, the third method is the best, quickest way to get a handle to the element.

      Thanks,
      Bea

  3. Buzz

    I want to FindName of a TextBox that is in a DataTemplate in my ListView. (Not ListBox)

    As is typical, my ListView’s View is a GridView, containing a GridViewColumn whose CellTemplate is assigned the DataTemplate resource I want. (sorry… trouble putting XAML in comments)

    My DataTemplate simply contains a TextBox.

    I’m having trouble acquiring the ContentPresenter for a ListViewItem (like you do with ListBoxItem). (Actually, it looks like a ListViewItem subclasses a ListBoxItem… but that didn’t work either)
    I’m using your GetObjectOfTypeInVisualTree method, starting from the selected ListViewItem. But I never get the ContentPresenter.
    ListViewItem is a ContentControl, and you said “A DataTemplate is always applied to a ContentPresenter, which is an element present in the visual tree of every ContentControl.” Is this really true?
    Can you help me find the ContentPresenter for a ListViewItem?

    Thanks for any help.
    Buzz

    • Buzz

      After poking around some more, I found your own example:
      http://www.beacosta.com/Forum/ListViewVisualTree.zip

      I was doing the equivalent of this, but this gave me the confidence to find my real problem…
      In my GridViewColumn I was setting BOTH my DisplayMemberBinding AND my CellTemplate… whose DataTemplate bound its TextBox’s Text to the same binding.
      My mistake.

      By the way, thanks for taking the time to blog this kind of stuff. Some of the stuff you talk about is very hard to find anywhere else.

      Buzz

      • Bea

        Hi Buzz,

        I’m glad your figured it out. Thanks for your kind words.

        Bea

  4. Anonymous

    The method GetObjectOfTypeInVisualTree has a small recursion issue. If the first child is not a T, it returns the recursive call of the first child. It will never look at any other children of besides the first.

    Here is my change:
    private T GetObjectOfTypeInVisualTree(DependencyObject dpob) where T : DependencyObject
    {
    int count = VisualTreeHelper.GetChildrenCount(dpob);
    for (int i = 0; i < count; i++)
    {
    DependencyObject child = VisualTreeHelper.GetChild(dpob, i);
    T childAsT = child as T;
    if (childAsT != null)
    {
    return childAsT;
    }
    childAsT = GetObjectOfTypeInVisualTree(child);
    if (childAsT != null)
    {
    return childAsT;
    }
    }
    return null;
    }

    • Bea

      Yes, you’re completely right. I changed the code in the sample attached and in the blog post.
      Great catch, and thanks a lot for letting me know!

      Bea

  5. John

    In regard to this example, is it possible to have changes on multiple bindings be held until an explicit update is called that then pushes all changes to the source? If so, do you have any pointers on how to do this?

    Thanks,

    John

    • Bea

      Hi John,

      Please see my reply to Tony B, below. I modified this sample to behave the way you guys want - you can find my code here.

      Thanks,
      Bea

  6. Florian (fkruesch)

    Loving your stuff, Beatrice.
    So, the reason there’s no datagrid in WPF is you don’t need it, right? :)
    I’m curious why you’re not planning to make it easier in the next version and when the next version is gonna come. Is there a new version of WPF shipping with Orcas?

    cheers
    Florian

    • Bea

      Hi Florian,

      We are definitely aware of the fact that people want a DataGrid in WPF, and I agree with you that we should have already shipped one.
      Yes, there is a new WPF version comming out with Orcas, but don’t count on DataGrid being out by then. My team has been looking into it though, so hopefully you will see one coming from Microsoft in a future release.

      If need a DataGrid today, you have a few options:

      - You can use Xceed’s DataGrid.

      - You can use Infragistics DataGrid.

      - You can use interop to take advantage of WinForm’s DataGrid.

      - You can write your own. This may sound unrealistic, but I’ve noticed that most customers only need about 10% of the features of a typical DataGrid. If that’s your case, you can take the time to build the 10% you need for your specific scenario.

      Thanks,
      Bea

  7. Ryan Cromwell

    Beatriz,
    Great blog. Love all the samples. I have a question regarding binding to a method. I have applied a RotateTransform to an Image in a DataTemplate. I’ve also created a an ObjectDataProvider which allows me to successfully bind to a method which simply returns Random.Next( -40, 40 ). I was doing this in the hopes of generating a random transform Angle. Unfortunately, regardless of how many items are in the ItemsSource for the ItemsControl, the RandomAngleHelper.GetAngle() method is only called once by the DataTemplate binding. It seems that once the Angle property of the RotateTransform is bound, it’s not rebound (for lack of a better term) with each DataTemplate applied.

    Hopefully this makes sense.

    • Bea

      Hi Ryan,

      Yes, it does make sense. Unfortunately that is the current behavior of ObjectDataProvider and I can’t think of a simple workaround to call the method for each item. A similar question has been answered in the WPF forum recently.

      Thanks,
      Bea

  8. Rob

    I have an unrelated question and I’m asking it here because I was unaware of any other way to contact you:

    I am trying to figure out how some WPF internals work and I was wondering if you could shed some light. Let’s say that you have a UserControl with a RelativeBinding on one of its child controls. When the UserControl is instantiated its InitializeComponent method is called. If the relative binding is pointing to something up the tree, but the UserControl has not yet been added to the tree, how does InitializeComponent keep from throwing and exception? Furthermore, how does it bind properly when one of its relatives is changed? I would appreciate any info you can give in this area.

    Thanks!

    • Bea

      Hi Rob,

      RelativeSource bindings will be re-evaluated every time there is a change in layout. More specifically, RelativeSource bindings are evaluated 1) when SetBinding is called, 2) by a method that is queued in the dispatcher at a priority lower than layout, and 3) every time we get a layout changed event. The relative binding source may not exist while layout is still happening, but once layout settles, the source will be there. We will retry evaluating the binding until that happens. You can find the code for this in the AttachToContext method of BindingExpression.

      Let me know if this helps.

      Bea

  9. PeterG

    I am finding problems in using Explicit source updates in a master detail scenario.

    If a detail user control is bound on a form by its datacontext (a custom class implementing INotifyPropertyChanged) and standard controls inside the user control are bound to sub-properties of this data context, is there no way to set the explicit binding at the user control binding? I am trying to find a solution where I do not need to know/update the individual bindings within the user control or at least keep the behavior in XAML.

    In addition the master form has a listbox to select the current detail and should discard any changes when the selection is changed.

    • Bea

      Hi PeterG,

      If I understand correctly, you are asking for a way to call UpdateSource on all bindings below a certain element in the tree. We’ve talked about that on the data binding team, but never got around to doing it. Our idea was to add a Bindings collection to certain elements, which would contain all the Bindings associated with child elements in the same namescope. To update all bindings of a particular user control, you would simply have to get the Bindings collection for that control, enumerate through all BindingExpressions and call UpdateSource() on them.

      Unfortunately I very much doubt that this feature will be included in our next release, given our current planning - unless of course, there is a lot of customer demand. So scream if you would like to see us implement that :)

      Until then, we need to continue doing that manually… :(

      Thanks a lot for your feedback!!

      Bea

  10. Tony B

    I don’t think this works as one would expect. I’d expect to be able to edit a FEW peopl in the listbox and THEN hit the submit button. Unless I’m missing something, you would have to hit submit after each edit and if you select someone else in the listbox, your changes will be lost. Is this true? Can you fix that?

    It seems you’d need to get to a binding on a textbox that no longer exists since the template was switched out when IsSelected became false, right?

    • Bea

      Hi Tony B,

      I changed this sample to behave the way you describe.
      I first factored the code that updates an employee into a method called “UpdateOneEmployeeToSource”. This method is able to update the binding associated with the data item that you pass as a parameter.

      private void UpdateOneEmployeeToSource(Employee employee)
      {
      ListBoxItem lbi = (ListBoxItem)(lb.ItemContainerGenerator.ContainerFromItem(employee));
      ContentPresenter cp = GetObjectOfTypeInVisualTree(lbi);
      DataTemplate dt = (DataTemplate)(this.Resources["editableEmployee"]);
      TextBox tb = (TextBox)(dt.FindName(“tb”, cp));
      BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
      be.UpdateSource();
      }

      Then I wrote code in the handler for the “Submit All” button that calls the previous method for each employee displayed.

      private void Submit_All(object sender, RoutedEventArgs e)
      {
      MySource source = root.DataContext as MySource;
      foreach (Employee employee in source.Employees)
      {
      UpdateOneEmployeeToSource(employee);
      }
      }

      I also had to make some slight changes to the styles - I needed to make sure that the editable style is always used, even when the employee is not selected.
      You can find the project with this code here.

      One problem with this solution is that it attempts to update all employees, even if only a couple have been changed in the UI. Unfortunately there is no public way to know whether the binding needs updating (the binding knows that internally, but we don’t expose that publicly).

      I hope this helps.

      Bea

  11. Alex (geekcoder)

    Hi Beatriz, I have a WPF question related to a previous post of yours - asychronous display of data in WPF.

    I have a list box that displays search results.

    I want to spawn a worker thread to do the search, and say its going to return 120,000 results, over 10 seconds, is there a good way to fill the listview?

    I’ve tried using Dispatcher.CheckAccess and BeginInvoke, but this reall slows things down.

    I’ve tried putting the results in a thread safe queue and having the UI thread pull them off the queue, but I am having trouble getting WPF to update the listview - it doesn’t redraw it ever?

    Thanks,

    - Alex

    • Bea

      Hi Alex,

      It should be possible to do what you’re asking by using BeginInvoke. There are a few things that you will want to keep in mind:

      1. Don’t do a BeginInvoke with a priority higher than what is necessary. This may cause your app to slow down.
      2. Don’t forget to lock your data.
      3. Don’t call BeginInvoke before the previous BeginInvoke call has been process from the queue - this can also cause delays. You can write some code like the following:

      // this code should be called by the worker thread
      lock(this)
      {
      …store data items for later use by the UI thread
      if(this.dispatcherOperation == null)
      {
      this.dispatcherOperation = Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, …this.MyFunction…);
      }
      }

      // this will be called in the UI thread
      private void MyFunction(…)
      {
      lock(this)
      {
      …add items to the ListView
      this.dispatcherOperation = null;
      }
      }

      Thanks,
      Bea

  12. KD

    Hi, your post is two years old and I was wondering if something changed in the latest version of WPF to simplify the explicit issues described here (you wrote “We would like to make it easier, but unfortunately we will not be able to make that happen in the next release”), just wondering what are the latest news.

    • Bea

      Hi KD,

      Yes, there have been some improvements in this scenario. There are is now the “Binding Groups” feature which allows the user to more easily submit a group of explicit bindings. You can read more about this feature in Vincent’s blog.

      Bea

Comments are closed.