Overriding a WPF binding value without bindings

Written by Varigence Blog on 3.11.2011

TAGS: MistDeveloperWPF

Share

First off, let me briefly introduce myself. My name is Craig and I'm the lead developer on Mist. My blog posts will typically be focused on developing Mist and developing BI solutions in Mist.

I recently encountered a situation where I needed to override a binding's value without changing the binding. If that statement seems confusing to you, allow me to back up and explain further.

Background:

All of the designers in Mist are implemented as WPF Pages. Within each designer is a variety of controls, e.g., ComboBoxes, DataGrids, CheckBoxes. Many of these controls have their IsEnabled bindings set in XAML.

A scenario arose where we wanted the user to be able to view an editor's content, but not edit it. Sort of a 'look but don't touch' behavior. At first glance, this seemed like a trivial problem; simply add or alter IsEnabled bindings to detect when to disable each control. The problem I faced was that each designer has lots of controls, all of which would need to implement this behavior. Furthermore, many of them already have IsEnabled bindings. I really didn't want to deal with the time consuming and cumbersome choice of adding or editing each control's IsEnabled binding. I also wanted to avoid the maintenance cost of ensuring that all future changes account for this new IsEnabled logic.

Solution:

Some of you may already be thinking that the right way to solve this is to use default styles. And you're right. (For those of you unfamiliar with default styles, check out MSDN Default (Theme) Styles and view a simple example at Inheriting Default Styles in WPF). But the problem remains of how to change the default style so we can override the control's IsEnabled value while still preserving its binding.

Your title here...The way I chose to solve this was by using an attached property:
public static class ControlExtension
{
    public static readonly DependencyProperty IsReadOnlyProperty =
        DependencyProperty.RegisterAttached(
            "IsReadOnly",
            typeof(bool),
            typeof(ControlExtension),
            new PropertyMetadata(false, new PropertyChangedCallback(OnIsReadOnlyChanged)));
 
    public static void SetIsReadOnly(DependencyObject element, string value)
    {
        element.SetValue(IsReadOnlyProperty, value);
    }
 
    public static object GetIsReadOnly(DependencyObject element)
    {
        return element.GetValue(IsReadOnlyProperty);
    }
 
    private static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool)
        {
            if ((bool)e.NewValue)
            {
                d.SetValue(UIElement.IsEnabledProperty, false);
            }
            else
            {
                d.ClearValue(UIElement.IsEnabledProperty);
 
                var bindingExpression = BindingOperations.GetBindingExpression(d, UIElement.IsEnabledProperty);
                if (bindingExpression != null)
                {
                    bindingExpression.UpdateTarget();
                }
            }
        }
    }
}

When IsReadOnly becomes true, we set the DependencyObject's IsEnabled property to false, thus forcibly disabling the control. Note that any IsEnabled bindings on that DependencyObject will be ignored, due to SetValue. If IsReadOnly returns to being false, we first clear the IsEnabled property's value and, if the control has an IsEnabled binding, we then call UpdateTarget to refresh the binding's value.

https://varigencecomstaging.blob.core.windows.net/blogimages/buttonIsReadOnlyStyle.png

To add this behavior in XAML, we can create or edit a default style for each relevant control, setting a binding to its IsReadOnly attached property. Our controls will then inherit their theme styles and use the new behavior.

Craig Lichtenstein

Comments