in Silverlight

Silverlight Attached Properties: Binding.UpdateSourceTrigger.PropertyChanged

Note: It is one of my answers from Silverlight Forum.

Problems

UpdateSourceTrigger is not supported in Silverlight until version 3.o.

Workaround

You will need to set the focus on other controls and re-focus to the control that you are using. For example: You are typing in TextBoxA. You want to have UpdateSourceTrigger support for that textbox. So, what you have to do is that you need to change the focus on another control (e.g. TextboxB) and re-set the focus on TextboxA while typing.

Yes. you can do it very easily from code-behind. But ( genius? ) people from pattern world don’t like the idea of using code-behind file (so, creating a code-behind file for each and every xaml in Visual Stuid is a waste? What about changing the default pattern (MVC) to MVVM in Visual Stuido Template? ) Anyway, let me get back to the topic. So, we can probably create an attached property to workaround the UpdateSourceTrigger in Silverlight.

Note: This code is just giving you some idea how you can workaround the issue. You will need to do the modification based on your need.

Let’s name our attached property aS “UpdateSourceTriggerHelper”. We will add some codes for registering the property and getter/setter as below. I will tell you more about OnUpdateSourceTriggerChanged


public class UpdateSourceTriggerHelper
{
public static readonly DependencyProperty UpdateSourceTriggerProperty =
DependencyProperty.RegisterAttached("UpdateSourceTrigger", typeof(bool), typeof(UpdateSourceTriggerHelper),
new PropertyMetadata(OnUpdateSourceTriggerChanged));

public static bool GetUpdateSourceTrigger(DependencyObject d)
{
return (bool)d.GetValue(UpdateSourceTriggerProperty);
}

public static void SetUpdateSourceTrigger(DependencyObject d, bool value)
{
d.SetValue(UpdateSourceTriggerProperty, value);
}

In OnUpdateSourceTriggerChanged method, we are going to add the logic for focusing other controls and re-setting the focus back to the original control. So, we need to walk throught the Visual Tree to get the object of other control. Please take a look at findFocusableControl();


private static void OnUpdateSourceTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = d as TextBox;
if ((bool)e.OldValue)
{
textBox.TextChanged -= (s, arg) => {

};
}
if ((bool)e.NewValue)
{
textBox.TextChanged += (s, arg) => {
var c = findFocusableControl(textBox);
if (c != null)
{
c.Focus();
}
textBox.Focus();
};
}
}

Let’s take a look at what we did in findFocusableControl(). Based on the control that you are typing, you can get the parent of control by using VisualTreeHelper. But if you want to use this code in production, you will have to modify the code. (For example: if you can’t find any parent then you will need to find the child element. and you need to check whether that control that you get from Visual Tree Helper is focusable or not. yes. there are a lot of things to do it.)


private static Control findFocusableControl(Control control)
{
var ctl = VisualTreeHelper.GetParent(control);
if ((ctl as Control) != null)
{
return ctl as Control;
}
else
{
int childrenCount = VisualTreeHelper.GetChildrenCount(ctl);
for (int i = 0; i < childrenCount; i++)
{
var c = VisualTreeHelper.GetChild(ctl, i) as Control;
if ((c != null) &amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp; (c != control))
{
return c;
}
}
}
return null;
}
}

Here is how we can use our attached property to provide PropertyChanged event.

Usage (Example)


<UserControl x:Class="UpdateSourceTriggerExtDemo.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UpdateSourceTriggerExtDemo"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<TextBox x:Name="nameTextbox" Height="25" Width="100" Margin="5" Text="{Binding Name, Mode=TwoWay}"
local:UpdateSourceTriggerHelper.UpdateSourceTrigger="True" />
<TextBox x:Name="addressTextbox" Height="25" Width="100" Margin="5" Text="{Binding Address, Mode=TwoWay}"
local:UpdateSourceTriggerHelper.UpdateSourceTrigger="False" />
<TextBox x:Name="phoneTextbox" Height="25" Width="100" Margin="5" Text="{Binding Phone, Mode=TwoWay}"
/>
<Button Height="25" Width="100" Margin="5" Content="Save" />
</StackPanel>
</Grid>
</UserControl>

Hope it helps. Feel free to let me know if you have any better solution.. I’m always open to any suggestion from you guys. Thanks..

Happy Silverlighting!!!

Leave a Reply

  1. I was using this method for a minute (so thanks for getting me out of a jam!), but then I found a _cleaner_ way to implement, that is compatible with SL3.

    private static void OnUpdateSourceTriggerChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
    {
    var textBox = d as TextBox;

    if (textBox == null)
    return;

    KeyEventHandler textChanged = (s, arg) =>
    {
    BindingExpression bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);

    bindingExpression.UpdateSource();
    };

    if ((bool)e.NewValue)
    {
    textBox.KeyDown -= textChanged;
    }

    if ((bool)e.NewValue)
    {
    textBox.KeyDown += textChanged;
    }
    }

  2. Hi.
    Thanks for this post. It works fine for me, but I still feel a bit awkward knowing how it actually does.
    I’m currently working with Silverlighlt 3 and Prism and I can’t help notice the paradox: we use this separation paterns (MVVM for me)in order to keep the code clear and easy to test, modify, etc, but in order to make it actually work, there are a lot of dirty hacks that must be implemented, some with unpredictible side effects. The ideas are great, but the technology is still lacking…

  3. Hi,

    I am just curious? does XAML on silverlight doesnt have any
    UpdateSourceTrigger on binding? say UpdateSourceTrigger=PropertyChanged…

    Why do you need to get the parent control?
    and why do loop on visual tree to get the parent control?

    I think there is a simple way to get the parent control using
    XAML and no need for code behind..

  4. Thanks a lot for your article! This is exactly what I was looking for. I just replaced the part that changes the focus by a call to the UpdateSource method of the binding expression:

    if ((bool)e.NewValue)
    {
    textBox.TextChanged += (s, arg) =>
    {
    textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    };
    }

  5. Here it is as a behavior:

    public class PropertyChangedBindingBehavior : Behavior
    {
    protected override void OnAttached()
    {
    base.OnAttached();

    AssociatedObject.TextChanged += new TextChangedEventHandler(AssociatedObject_TextChanged);
    }

    protected override void OnDetaching()
    {
    base.OnDetaching();

    AssociatedObject.TextChanged -= new TextChangedEventHandler(AssociatedObject_TextChanged);
    }

    void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e)
    {
    AssociatedObject.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }
    }

    You need a reference to System.Windows.Interactivity.

    Then in XAML:


    i is the System.Windows.Interactivity, beh is your namepsace for the PropertyChangedBindingBehavior.

    I think it’s nicer than the attached property since you can unwire from the TextChanged.

  6. Sorry, error above due to trying to “fix” formatting. Should inherit from : Behavior not just Behavior.

  7. Ugh… I guess it’s the HTML killing it… Should inherit from generic Behavior where T is TextBox.