How to manage explicit update of (many) bounded controls in XAML windows

This is the result of a discussion that I had in a MSDN Forum devoted to WPF development.

I have a XAML page with many fields bounded to the fields of a typed DataRow (PersonDataSet.PersonRow). This PersonRow is generated by Visual Studio and have many fields like Name, Phone, Mobile, Address, SSN, etc.

In the page every field is defined with some XAML code similar to this snippet:

  <TextBox
     Name="Name"
     Text="{Binding Path=Name, UpdateSourceTrigger=Explicit}">
  </TextBox>

I have different types of controls like TextBox or ComboBox. I used explicit update because I want to save modifications only when the user press a Save button. In the code I defined

  PersonDataSet.PersonRow personRow;

and I used this variable as page DataContext object. The variable references to an instance contained in a PersonDataSet object (results of a query on a SQL Server db).

Basically I have tWo event handlers: SaveData e UndoData. The first is called when the user clicks a save button, the second when the user clicks an undo button.

 
public void SaveData(object sendere, RoutedEventArgs args)
{
  BindingExpression exprName =
           Name.GetBindingExpression(TextBox.TextProperty);
  exprName.UpdateSource();
  …
  … many lines of code similar to the two lines above
  … 
  // save the data into the db.
  using (PersonDataSetTableAdapters.PersonTableAdapter ta =
           new PersonDataSetTableAdapters.PersonTableAdapter())
  {
    ta.Update(personRow);
  }
}

public void UndoData(object sendere, RoutedEventArgs args)
{
  BindingExpression exprName =
        Name.GetBindingExpression(TextBox.TextProperty);
  exprName.UpdateTarget();
  …
  … many lines of code similar to the two lines above
  …
}

The code is working well but if I have 50 fields I need to write 200 lines of code. I can use some scripting or macros but I’m worried if there is a more efficient way to have the same behavior.

After a brief discussion I decided that a bit of recursive code that visits the object tree can solve the problem. The code below implements a more generic undo function.

public void RecurseUndoData(UIElement element)
{
  if (element is Panel)
  {
    foreach (UIElement childElement in ((Panel)element).Children)
    {
    RecurseUndoData(childElement);
    }
  }
  else
  {
    BindingExpression bindExpr = null;
    if (element is TextBox)
    {
      bindExpr = ((TextBox)element)
            
.GetBindingExpression(TextBox.TextProperty);
    }
    else if (element is ComboBox)
    {
      bindExpr = ((ComboBox)element)
            .GetBindingExpression(ComboBox.SelectedValueProperty);
    }
    else if (element is DatePicker)
    {
      bindExpr = ((DatePicker)element)
            .GetBindingExpression(DatePicker.ValueProperty);
    }
    if (bindExpr != null)
    {
      bindExpr.UpdateTarget();
    }
  }
}
 

The RecurseSaveData is similar. The problem with this approach is that you need to know which type of controls you are using. Note that I wrote 

  if (element is TextBox)

and the other similars statements because GetBindingExpression function require the bounded dependency property. If you introduce more control types, you need to expand these functions adding more if clauses. 

I decided to centralize the logic to better manage the changes and I build an helper class with (a more complex version of) the two functions. 

Have some better ideas?

5 comments
  1. Madhuri said:

    Hi There,

    I cannot find the SelectedValueProperty for the combox. Could you please help me how I can find this property.

    Many Thanks
    Madhuri

  2. hello,
    your article has motivated myself for the follow solution:

    Public Sub UpdateDataSource(ByRef element As UIElement)

    Dim objTC As TabControl = Nothing
    Dim objCC As ContentControl = Nothing
    Dim objPan As Panel = Nothing
    Dim objChild As UIElement = Nothing
    Dim objBind As BindingExpression = Nothing

    Try
    If TypeOf (element) Is TextBox Then
    objBind = CType(element, TextBox).GetBindingExpression(TextBox.TextProperty)

    ElseIf TypeOf (element) Is CheckBox Then
    objBind = CType(element, CheckBox).GetBindingExpression(CheckBox.IsCheckedProperty)

    ElseIf TypeOf (element) Is ComboBox Then
    objBind = CType(element, ComboBox).GetBindingExpression(ComboBox.SelectedValueProperty)

    ElseIf TypeOf (element) Is UserControl Then
    ‘usercontrols must implement the update-mechanisim itself
    Exit Try

    ElseIf TypeOf (element) Is TabControl Then
    objTC = CType(element, TabControl)
    For Each objChild In objTC.Items
    If TypeOf (objChild) Is UIElement Then
    UpdateDataSource(objChild)
    End If
    Next

    ElseIf TypeOf (element) Is ContentControl Then
    objCC = CType(element, ContentControl)
    If TypeOf (objCC.Content) Is UIElement Then
    objChild = objCC.Content
    UpdateDataSource(objChild)
    End If

    ElseIf TypeOf (element) Is Panel Then
    objPan = CType(element, Panel)
    For Each objChild In objPan.Children
    If TypeOf (objChild) Is UIElement Then
    UpdateDataSource(objChild)
    End If
    Next

    Else
    Err.Raise(10000, , “For UIElement ‘” & TypeName(element) & “‘ is not implement any update-mechanisim.”)

    End If

    ‘start update for current element
    If objBind IsNot Nothing Then objBind.UpdateSource()

    Catch ex As Exception
    Call modGlobal.ShowErrorMessage(ex, “modGlobal”, Reflection.MethodBase.GetCurrentMethod.Name)

    Finally
    objCC = Nothing
    objPan = Nothing
    objChild = Nothing
    objBind = Nothing

    End Try

    End Sub

  3. Oh my goodness! Impressive article dude! Many thanks, However I am having
    problems with your RSS. I don’t know the reason why I cannot join it. Is there anybody else having the same RSS issues? Anybody who knows the answer will you kindly respond? Thanks!!

  4. tedebus said:

    Well done!! Thank you!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: