Tag Archives: WPF

Binding WPF Style

This may be old news but it is exciting to me so I want to document it. Often times when working with the MVVM/Presentation Model Pattern you want to apply a style based on the state of your Model/Business Object. For example if a Business Object ‘IsNew’ you may want to apply a editable style to a TextBox control so the user can supply some data. Yet when the same Business Object ‘IsOld’ you may want disable a field preventing any data entry. My first thought was to use a converter to determine the style in a binding statement similar to this.

<TextBox Style={Binding Path=ViewModel.Customer, Converter={StaticResource StyleConverter}}/>

When data binding does its thing the Convert method will be passed the Customer Business Object. We can then inspect the Business Object to determine what style to apply to the TextBox. The rub however comes when we try to locate the style we want to apply. See code below.

        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var customer = value as Customer;
            if(customer != null && customer.IsNew)
            {
                //Locate and return the EditableTextboxStyle
                //Wait!!!! How?!!?
                return null;
            }
            return null;
        }

The crux of the problem is that most often styles exist as resources of your UserControl/Window/Page. In order to resolve these resources the converter will need a reference to the FrameworkElement that contains said resources. So how about this?.

<TextBox Style={Binding Path=ViewModel.Customer, Converter={StaticResource StyleConverter}}, ConverterParameter={Binding Path=DataContext}/>

And the converter….

           /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var customer = (Customer)value;
            var viewModel = (ViewModel)parameter;
            var view = viewModel.View;
            var fe = (FrameworkElement)view;

            if (customer != null && customer.IsNew)
            {
                return fe.TryFindResource("EditableTextboxStyle");
            }

            return fe.TryFindResource("NonEditableTextboxStyle");
        }

Though this seems like it may work(not really) there is one major issue that we run into. The ConverterParameter property in the binding statement is a CLR property that is it is not a DependencyProperty and because of this fact it cannot be data bound!

So just to restate the problem because most of the time I forget what problem I am actually trying to solve. I want a specific style to be applied to a TextBox based on the state of my Customer Business Object. For a converter to do this it needs a reference to the both the Business Object(to interrogate its state) as well as the View(To locate resources i.e. styles). So how can we pass two data bound objects to a converter? Well you cant! Not without some craziness.

Thankfully we have the Multibinding

I am not going to explain what you can read for yourself in the above article. I will however give you the code that did what I wanted to do ūüôā I am Felling a bit lazy tonight!

 <TextBox>
        <TextBox.Style>
            <MultiBinding Converter="{StaticResource StyleConverter}">
                <Binding Path="Customer"/>
                <Binding Path="DataContext"/>
            </MultiBinding>
        </TextBox.Style>
    </TextBox>

And the converter… When we multibinding we also have to use a multiconverter. So basically your converter has to implement IMultiConverter instead of IValueConverter. The only difference is that the first parameter of the Convert method is an array of objects. And in my case the first element in the array is a Customer object and the second a ViewModel! See below.

           /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object [] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var customer = (Customer)value;
            var viewModel = (ViewModel)parameter;
            var view = viewModel.View;
            var fe = (FrameworkElement)view;

            if (customer != null && customer.IsNew)
            {
                return fe.TryFindResource("EditableTextboxStyle");
            }

            return fe.TryFindResource("NonEditableTextboxStyle");
        }

Like I said in the beginning this is far from groundbreaking but I thought it was worth a short post!

Advertisements
Tagged

GetIsInDesignMode(this)

Often times when trying to view a Window or UserControl in the WPF designer, the designer chokes and pukes all over your screen. Though your first instinct may be to abuse the Cider designers please refrain. Belive it or not it may be possible that it is not their fault! As it turns out, when Cider attempts to render your Window or UserControl it runs through a somewhat abbreviated version of its lifecyle. If you have code in any of these framework methods that, say, calls a web service or hits a database, Cider may not be able to resolve all of the resources needed. Sadly like many things in WPF the error messages are not always that helpful.

Thankfully there is a method in the System.ComponentModel namespace that can help.

private void Control_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    if (DesignerProperties.GetIsInDesignMode(this))
    {
        return;
    }

    LoadModel();
}

Calling this method will return a boolean indicating weather or not the current control is being rendered in the designer or not. You can use this to wrap code that could potentially choke up the designer. Though this is not a perfect solution in terms of keeping your code clean. It will allow you to view your Windows or UserControls in the designer. And in many cases that is a must.

Tagged ,
Advertisements