Detecting dependency property changes in WinRT

Very poorPoorAverageGoodExcellent (No Ratings Yet) 
Loading ... Loading ...

Today I’d like to share a trick I used while developing my first Windows Store application. I’m very new to this technology and it’s my first article about it, so I hope I won’t make a fool of myself…

It’s often useful to be notified when the value of a dependency property changes; many controls expose events for that purpose, but it’s not always the case. For instance, recently I was trying to detect when the Content property of a ContentControl changed. In WPF, I would have used the DependencyPropertyDescriptor class, but it’s not available in WinRT.

Fortunately, there is a mechanism which is available on all XAML platforms, and can solve this problem: binding. So, the solution is just to create a class with a dummy property that is bound to the property we want to watch, and call a handler when the value of the dummy property changes. To make it cleaner and hide the actual implementation, I wrapped it as an extension method that returns an IDisposable:

    public static class DependencyObjectExtensions
    {
        public static IDisposable WatchProperty(this DependencyObject target,
                                                string propertyPath,
                                                DependencyPropertyChangedEventHandler handler)
        {
            return new DependencyPropertyWatcher(target, propertyPath, handler);
        }

        class DependencyPropertyWatcher : DependencyObject, IDisposable
        {
            private DependencyPropertyChangedEventHandler _handler;

            public DependencyPropertyWatcher(DependencyObject target,
                                             string propertyPath,
                                             DependencyPropertyChangedEventHandler handler)
            {
                if (target == null) throw new ArgumentNullException("target");
                if (propertyPath == null) throw new ArgumentNullException("propertyPath");
                if (handler == null) throw new ArgumentNullException("handler");

                _handler = handler;

                var binding = new Binding
                {
                    Source = target,
                    Path = new PropertyPath(propertyPath),
                    Mode = BindingMode.OneWay
                };
                BindingOperations.SetBinding(this, ValueProperty, binding);
            }

            private static readonly DependencyProperty ValueProperty =
                DependencyProperty.Register(
                    "Value",
                    typeof(object),
                    typeof(DependencyPropertyWatcher),
                    new PropertyMetadata(null, ValuePropertyChanged));

            private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var watcher = d as DependencyPropertyWatcher;
                if (watcher == null)
                    return;

                watcher.OnValueChanged(e);
            }

            private void OnValueChanged(DependencyPropertyChangedEventArgs e)
            {
                var handler = _handler;
                if (handler != null)
                    handler(this, e);
            }

            public void Dispose()
            {
                _handler = null;
                // There is no ClearBinding method, so set a dummy binding instead
                BindingOperations.SetBinding(this, ValueProperty, new Binding());
            }
        }
    }

It can be used like this:

// Subscribe
watcher = myControl.WatchProperty("Content", myControl_ContentChanged);

// Unsubscribe
watcher.Dispose();

I hope you will find this useful!

Using C# 5 caller info attributes when targeting earlier versions of the .NET framework

Very poorPoorAverageGoodExcellent (2 votes) 
Loading ... Loading ...

Caller info attributes are one of the new features of C# 5. They’re attributes applied to optional method parameters that enable you to pass caller information implicitly to a method. I’m not sure that description is very clear, so an example will help you understand:

        static void Log(
            string message,
            [CallerMemberName] string memberName = null,
            [CallerFilePath] string filePath = null,
            [CallerLineNumber] int lineNumber = 0)
        {
            Console.WriteLine(
                "[{0:g} - {1} - {2} - line {3}] {4}",
                DateTime.UtcNow,
                memberName,
                filePath,
                lineNumber,
                message);
        }

The method above takes several parameters intended to pass information about the caller: calling member name, source file path and line number. The Caller* attributes make the compiler pass the appropriate values automatically, so you don’t have to specify the values for these parameters:

        static void Foo()
        {
            Log("Hello world");
            // Equivalent to:
            // Log("Hello world", "Foo", @"C:\x\y\z\Program.cs", 18);
        }

This is of course especially useful for logging methods…

Notice that the Caller* attributes are defined in the .NET Framework 4.5. Now, suppose we use Visual Studio 2012 to target an earlier framework version (e.g. 4.0): the caller info attributes don’t exist in 4.0, so we can’t use them… But wait! What if we could trick the compiler into thinking the attributes exist? Let’s define our own attributes, taking care to put them in the namespace where the compiler expects them:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerMemberNameAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerFilePathAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class CallerLineNumberAttribute : Attribute
    {
    }
}

If we compile and run the program, we can see that our custom attributes are taken into account by the compiler. So they don’t have to be defined in mscorlib.dll like the “real” ones, they just have to be in the right namespace, and the compiler accepts them. This enables us to use this cool feature when targeting .NET 4.0, 3.5 or even 2.0!

Note that a similar trick enabled the creation of extension methods when targeting .NET 2.0 with the C# 3 compiler: you just had to create an ExtensionAttribute class in the System.Runtime.CompilerServices namespace, and the compiler would pick it up. This is also what enabled LinqBridge to work.

kick it on DotNetKicks.com

Posted in Tricks. Tags: , . 9 Comments »

[WPF] Creating parameterized styles with attached properties

Very poorPoorAverageGoodExcellent (3 votes) 
Loading ... Loading ...

Today I’d like to share a trick that I used quite often in the past few months. Let’s assume that in order to improve the look of your application, you created custom styles for the standard controls:

OK, I’m not a designer… but it will serve the purpose well enough to illustrate my point ;) . These styles are very simple, they’re just the default styles of CheckBox and RadioButton in which I only changed the templates to replace the BulletChromes with these awesome blue tick marks. Here’s the code:

        <Style x:Key="{x:Type CheckBox}" TargetType="{x:Type CheckBox}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="{StaticResource CheckBoxFillNormal}"/>
            <Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource EmptyCheckBoxFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}">
                        <BulletDecorator Background="Transparent"
                                         SnapsToDevicePixels="true">
                            <BulletDecorator.Bullet>
                                <Border BorderBrush="{TemplateBinding BorderBrush}"
                                        Background="{TemplateBinding Background}"
                                        BorderThickness="1"
                                        Width="11" Height="11" Margin="0,1,0,0">
                                    <Grid>
                                        <Path Name="TickMark"
                                              Fill="Blue"
                                              Data="M0,4 5,9 9,0 4,5"
                                              Visibility="Hidden" />
                                        <Rectangle Name="IndeterminateMark"
                                                   Fill="Blue"
                                                   Width="7" Height="7"
                                                   HorizontalAlignment="Center"
                                                   VerticalAlignment="Center"
                                                   Visibility="Hidden" />
                                    </Grid>
                                </Border>
                            </BulletDecorator.Bullet>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              Margin="{TemplateBinding Padding}"
                                              RecognizesAccessKey="True"
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </BulletDecorator>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
                                <Setter Property="Padding" Value="4,0,0,0"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="TickMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="{x:Type RadioButton}" TargetType="{x:Type RadioButton}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="#F4F4F4"/>
            <Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RadioButton}">
                        <BulletDecorator Background="Transparent">
                            <BulletDecorator.Bullet>
                                <Grid VerticalAlignment="Center" Margin="0,1,0,0">
                                    <Ellipse Width="11" Height="11"
                                             Stroke="{TemplateBinding BorderBrush}"
                                             StrokeThickness="1"
                                             Fill="{TemplateBinding Background}" />
                                    <Ellipse Name="TickMark"
                                             Width="7" Height="7"
                                             Fill="Blue"
                                             Visibility="Hidden" />
                                    <Ellipse Name="IndeterminateMark"
                                             Width="3" Height="3"
                                             Fill="Blue"
                                             Visibility="Hidden" />
                                </Grid>
                            </BulletDecorator.Bullet>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              Margin="{TemplateBinding Padding}"
                                              RecognizesAccessKey="True"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </BulletDecorator>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
                                <Setter Property="Padding" Value="4,0,0,0"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="TickMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

OK, so you now have beautiful controls that are going to make the app a big success, management is happy, everything is for the best… until you realize that in another view of the application, the controls need to have the same style, but with green tick marks!

The first solution that comes to mind is to duplicate the style, and replace blue with green in the copy. But since you’re a good developer who cares about best practices, you know that duplicate code is evil: if you ever need to make changes to the style of the blue CheckBox, you will also have to modify the green one… and perhaps the red one, and the black one, etc. Clearly it would soon become unmanageable. So we need to refactor, but how? Ideally we would pass parameters to the style, but a style is not a method that you can call with various parameters…

What we need is an extra property that controls the color of the tick marks, so we can bind to this property in the template. A possible approach is to create custom controls that inherit CheckBox and RadioButton, with an extra TickBrush property… but personnally I don’t really like this approach, I always prefer to use the built-in controls when they can fit the bill.

Anyway, there is an easier solution: we just need to create a ThemeProperties class with an attached property of type Brush:

    public static class ThemeProperties
    {
        public static Brush GetTickBrush(DependencyObject obj)
        {
            return (Brush)obj.GetValue(TickBrushProperty);
        }

        public static void SetTickBrush(DependencyObject obj, Brush value)
        {
            obj.SetValue(TickBrushProperty, value);
        }

        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                "TickBrush",
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black));
    }

We change the templates a bit to replace the hard-coded color with a binding to this property:

                                ...

                                <!-- CheckBox -->
                                        <Path Name="TickMark"
                                              Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                              Data="M0,4 5,9 9,0 4,5"
                                              Visibility="Hidden" />
                                        <Rectangle Name="IndeterminateMark"
                                                   Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                                   Width="7" Height="7"
                                                   HorizontalAlignment="Center"
                                                   VerticalAlignment="Center"
                                                   Visibility="Hidden" />

                                ...

                                <!-- RadioButton -->
                                    <Ellipse Name="TickMark"
                                             Width="7" Height="7"
                                             Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                             Visibility="Hidden" />
                                    <Ellipse Name="IndeterminateMark"
                                             Width="3" Height="3"
                                             Fill="{TemplateBinding my:ThemeProperties.TickBrush}"
                                             Visibility="Hidden" />

And when we use the controls, we set the property to the desired tick color:

<CheckBox Content="Checked" IsChecked="True" my:ThemeProperties.TickBrush="Blue" />

So we can now have controls that share the same style, but have different colors for the tick mark:

Isn’t it great? However there is a small problem left: since controls on the same view all use the same tick color, it’s not very convenient to repeat the color on each one. It would be nice to be able to specify the color just once, on the root of the view… Well, as it happens, dependency properties have a nice feature that allows to do exactly that: value inheritance. We just need to specify the Inherits flag in the declaration of the TickBrush property:

        public static readonly DependencyProperty TickBrushProperty =
            DependencyProperty.RegisterAttached(
                "TickBrush",
                typeof(Brush),
                typeof(ThemeProperties),
                new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.Inherits));

With this flag, the property becomes “ambient”: we only need to specify its value on a parent control, and all descendant controls will automatically inherit the value. So if you need a view where all the checkboxes and radiobuttons are red, just set the TickBrush property to Red on the root element of the view.

Obviously this concept can be extended to other cases: actually, every time an element of the template must change based on arbitrary criteria, this technique can be used. It can be a good alternative to duplicating a template when you only need to change a small part of it.

kick it on DotNetKicks.com

[WPF] How to bind to data when the DataContext is not inherited

Very poorPoorAverageGoodExcellent (21 votes) 
Loading ... Loading ...

The DataContext property in WPF is extremely handy, because it is automatically inherited by all children of the element where you assign it; therefore you don’t need to set it again on each element you want to bind. However, in some cases the DataContext is not accessible: it happens for elements that are not part of the visual or logical tree. It can be very difficult then to bind a property on those elements…

Let’s illustrate with a simple example: we want to display a list of products in a DataGrid. In the grid, we want to be able to show or hide the Price column, based on the value of a ShowPrice property exposed by the ViewModel. The obvious approach is to bind the Visibility of the column to the ShowPrice property:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding ShowPrice,
                        Converter={StaticResource visibilityConverter}}"/>

Unfortunately, changing the value of ShowPrice has no effect, and the column is always visible… why? If we look at the Output window in Visual Studio, we notice the following line:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’)

The message is rather cryptic, but the meaning is actually quite simple: WPF doesn’t know which FrameworkElement to use to get the DataContext, because the column doesn’t belong to the visual or logical tree of the DataGrid.

We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding DataContext.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

Or we can add a CheckBox bound to ShowPrice, and try to bind the column visibility to the IsChecked property by specifying the element name:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding IsChecked,
                        Converter={StaticResource visibilityConverter},
                        ElementName=chkShowPrice}"/>

But none of these workarounds seems to work, we always get the same result…

At this point, it seems that the only viable approach would be to change the column visibility in code-behind, which we usually prefer to avoid when using the MVVM pattern… But I’m not going to give up so soon, at least not while there are other options to consider ;)

The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree. I don’t know the exact mechanism that enables this behavior, but we’re going to take advantage of it to make our binding work…

The idea is to create a class (I called it BindingProxy for reasons that should become obvious very soon) that inherits Freezable and declares a Data dependency property:

    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }

We can then declare an instance of this class in the resources of the DataGrid, and bind the Data property to the current DataContext:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

The last step is to specify this BindingProxy object (easily accessible with StaticResource) as the Source for the binding:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                    Visibility="{Binding Data.ShowPrice,
                        Converter={StaticResource visibilityConverter},
                        Source={StaticResource proxy}}"/>

Note that the binding path has been prefixed with “Data”, since the path is now relative to the BindingProxy object.

The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.

[Visual Studio] Trick : make a project item a child item of another

Very poorPoorAverageGoodExcellent (No Ratings Yet) 
Loading ... Loading ...

You probably noticed that, in a C# project tree, some items are placed “under” a parent item : it is the case, for instance, for files generated by a designer or wizard :

Solution Explorer

Model1.Designer.cs is a child item of Model1.edmx

The following trick shows how to apply the same behavior to your own files.

Let’s assume that you want to customize the classes generated by the EDM designer. You can’t modify the Model1.designer.cs file, because you changes would be overwritten by the designer. So you create a new file, say Model1.Custom.cs, where you will write your custom code for the entity classes (using the partial keyword). By default, this file is placed at the root of the project :

Solution Explorer

Model1.Custom.cs is at the root of the project

In order to show clearly the association with Model1.edmx, we would like to make Model1.Custom.cs a child item of Model1.edmx, at the same level as Model1.designer.cs… Even though the Visual Studio IDE doesn’t offer that option, it is possible : you just need to edit the .csproj file manually. The easiest way to do that is to unload the project (right click on the project, “Unload project“), and edit it directly in Visual Studio (right click, “Edit FooBar.csproj“). Find the <Compile> element corresponding to Model1.Custom.cs, and add a <DependentUpon> child element, as show below :

    <Compile Include="Model1.Custom.cs">
        <DependentUpon>Model1.edmx</DependentUpon>
    </Compile>

Reload the project : Model1.Custom.cs now appears as a child item of Model1.edmx.

Solution Explorer

Model1.Custom.cs is now a child item of Model1.edmx

This trick enables you to organize your project better and make its structure clearer.

Posted in Tricks. Tags: , . No Comments »