DCSIMG
October 2010 - Posts - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

October 2010 - Posts

WPF Tip: Overriding Metadata

Published at Oct 19 2010, 10:23 PM by pavely

Every dependency property is associated with some metadata, specifying (for instance) the property’s default value, various flags (affects render, inheritance down the visual tree and much more). Sometimes when creating custom elements, we need to change aspects of an inherited dependency property. For example, in my last post I showed a custom Shape called TickCircle, that uses lines to draw itself. These lines use the Stroke property (a Brush) inherited from Shape to draw those lines. The problem is that the default value of Stroke set by Shape is null, which means users of my TickCircle have to supply a non-null Stroke to see anything. As this is the essence of the control, I would have liked a different default.

At first glance, the solution seems easy: in the constructor add a simple assignment:

Stroke = Brushes.Black;

and be done with it. This, however, has a major drawback: it sets a local value for the property, wasting memory and “disrupting” the dependency property system. A stronger example is a property such as FontSize that supports visual inheritance; setting a local value is stronger than inheritance and will kill the inheritance capability.

What’s needed is changing of the default value itself. As the property has already been registered, we can override some of its metadata by calling the DependencyProperty.OverrideMetadata method like so:

static TickCircle() {

   StrokeProperty.OverrideMetadata(typeof(TickCircle), new FrameworkPropertyMetadata(Brushes.Black));

   StretchProperty.OverrideMetadata(typeof(TickCircle), new FrameworkPropertyMetadata(Stretch.Uniform));

}

 

Similarly done for the Stretch property (which defaults to None in Shape).

Curiously enough, changing the code to the following causes an exception:

StrokeProperty.OverrideMetadata(typeof(TickCircle), new PropertyMetadata(Brushes.Black));

 

Can you guess why?

Property metadata is represented by 3 classes, PropertyMetadata, UIPropertyMetadata (derives from it) and FrameworkPropertyMetadata (derives from UIPropertyMetadata). Each type adds more metadata info that can be specified. There is no real reason that I can see for this inheritance hierarchy – the inheritance is superficial. It would have been simpler to create just one class and provide convenient constructors. Nevertheless – we have three.

The exception stems from the fact that calling OverrideMetadata must be done with the same type (or a more derived type) as the original register. How would we know which of the three was used? No simple way I could find. The only way is to look with Reflector how the original property is registered. In our case, the Stroke property is registered with FrameworkPropertyMetadata. This is checked inside a private method (SetupOverrideMetadata) that is called by OverrideMetadata.

So the moral of the story is this: always use the most derived type (FrameworkPropertyMetadata) and you will be happier.

Creating a Custom WPF Shape

Published at Oct 17 2010, 01:23 PM by pavely

The WPF Shape class is the abstract base of various simple shapes, namely Line, Ellipse, Rectangle, Polyline, Polygon and Path. Path is not really simple, as it can show any given Geometry.

There are multiple ways to create custom visual objects in WPF and selecting the best base class is not always easy. The Shape class derives from FrameworkElement, meaning it supports layout, transformations, styles, etc., so it’s a convenient starting point for new shapes. Shape exposes properties such as Fill, Stroke, StrokeThickness and Stretch. A custom Shape only needs to override the protected DefiningGeometry property, returning a Geometry-derived type (such as PathGeometry) that describes the shape. The rest is done by WPF.

Here’s an example. Suppose we want to create a shape that shows circular tick marks like this:

image

Here’s how the code can be written:

public class TickCircle : Shape { 

   protected override Geometry DefiningGeometry {

      get {

         var g = new PathGeometry();

         double radx = 100, rady = 100;

         var figure = new PathFigure();

         double offset = Offset;

         figure.StartPoint = new Point(radx + radx * Math.Cos(offset), rady + rady * Math.Sin(offset));

         for(double angle = offset; angle < offset + 360; angle += 1) {

            double len = (int)(angle + .5) % 5 == 0 ? LongLineLength : ShortLineLength;

            figure.Segments.Add(new LineSegment(new Point(radx + (radx - len) * Math.Cos(angle * Math.PI / 180), rady + (rady - len) * Math.Sin(angle * Math.PI / 180)), true));

            figure.Segments.Add(new LineSegment(new Point(radx + radx * Math.Cos((angle + 1) * Math.PI / 180), rady + rady * Math.Sin((angle + 1) * Math.PI / 180)), false));

         }

         figure.IsClosed = figure.IsFilled = false;

         g.Figures.Add(figure);

         return g;

      }

   }

   // more properties

}

 

The class assumes the existence of three custom dependency properties (ShortLineLength, LongLineLength and Offset). They are not shown for brevity, but are defined with the usual DependencyProperty.Register static method and a property, calling GetValue and SetValue in the getter and setter, respectively.

Note that to create the geometry I’m using fixed numbers: the radii are 100 units and seem to be independent of the actual space allocated for the shape. This is fairly typical, as the actual size of the shape is determined externally, e.g. with the Width and Height properties or the Stretch property. Although the shape can try to adapt, it’s unnecessary and cumbersome (for example, the WIdth and Height may be Double.Nan – their default values, which is useless for calculation purposes. Using ActualWidth and ActualHeight is better, although they may change asynchronously). This also lets me treat other properties (such as ShortLineLength as relative to my fixed radius of 100 instead of being absolute. In this case I think it’s an advantage, but in other cases it may be a drawback).

Windows Platform Developers User Group meeting

Published at Oct 06 2010, 10:45 PM by pavely

This evening was a WPDUG meeting on CLR hosting and CLR profiling. Thank you all for coming! It was a pleasure to see so many of you there. My session was on CLR hosting, using the new CLR 4 hosting API. This is done using native code (C++) that uses COM for communication between the CLR and the host, both ways.

I showed how to enumerate the installed runtimes, how to load a specific CLR (a new feature of CLR 4 – the ability to host multiple CLRs in a single process), how to create an application domain (all from native code, of course). The interesting parts are the interfaces used to call on the CLR and the host implementations for CLR usage.

 

Picture1

After a ICLRRuntimeInfo interface is obtained, a call to GetInterface allows getting some functionality aspect from the CLR. The most interesting interfaces are ICLRRuntimeHost (new in CLR 4) and ICorRuntimeHost (the old one, but has functionality not available in the new one).

From the ICLRRuntimeHost, you can call GetCLRControl and from there get a management aspect object from the CLR. You can browse the presentation attached to the end of this post and the source code examples. In the other direction, calling ICLRRuntimeHost::SetHostControl allows the host to supply its objects for controlling various aspects of the execution, such as notifications for GC, memory management and more.

 image

In the code sample, one can choose a CLR to work with, and execute some managed executable in some app domain, while at the same time get indications on GC and memory related activities. This is, of course, just the tip of the iceberg.

WPF Menu Customization

Published at Oct 01 2010, 11:26 AM by pavely

Menus are the most well known of all user interface elements. Although their popularity has declined in recent years, in lieu of toolbar and the famous office style ribbon, they are still useful.

In WPF, a menu is constructed by a Menu class that mostly hosts MenuItem objects, that can nest to any needed level. Customizing the look of WPF menus (and context menus) can be surprisingly difficult.

The first, simplest, and most obvious way of customizing a WPF menu is to tweak its properties, such as Background, Foreground and FontSize. This, however, leaves much to be desired.

Here’s a simple try with an implicit style to be applied on all MenuItem objects:

<Style TargetType="MenuItem">
<Setter Property="Background" Value="Yellow" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
</Style>

We can change the Menu background brush as well. Here’s how this looks (with a classic Windows theme):

image 

A few issues: The highlighted item’s background is always blue (or whatever is configured via the control panel), and the foreground white. And there’s the separator, looking completely out of place.

One way to address the selection is add a style trigger:

<Style.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" Value="Green" />
  </Trigger>
</Style.Triggers>

This should work, but doesn’t work as expected. The trigger works only on the top level menu items, but not others:

image

The reason for the failed trigger is quite subtle, and relates to the way the MenuItem’s template is built.

So, what can we do about that? Do we have to write a completely new MenuItem control template? Fortunately, no.

Browsing through the properties of the MenuItem class, we come across a few static properties called SubmenuItemTemplateKey, TopLevelHeaderTemplateKey, TopLevelItemTemplateKey and SubmenuHeaderTemplateKey. These static properties are keys (pun intended) to a deeper customization of menu item.

The documentation states that these are keys of styles that are applied to menu items in various roles. This idea is pretty common in WPF, e.g. The ItemContainerStyle of ItemsControl and the FocusVisualStyle property of FrameworkElement. However, in this case the documentation is wrong. These keys are actually keys to a control template – not a style.

Armed with these insights, customization can be performed on a much more granular level. Unfortunately, this also means a control template must be supplied – a simple property change is not allowed. Here’s an example:

<ControlTemplate x:Key="{x:Static MenuItem.SubmenuItemTemplateKey}" TargetType="MenuItem">
<Border Background="Yellow" x:Name="_grid" Padding="2" BorderThickness="1" BorderBrush="Yellow" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="24" SharedSizeGroup="IconGroup"/>
<ColumnDefinition Width="Auto" MinWidth="30" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<ContentPresenter ContentSource="Icon" Margin="{TemplateBinding Padding}" />
<ContentPresenter Grid.Column="1" VerticalAlignment="Center" ContentSource="Header" RecognizesAccessKey="True" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" Value="Green" TargetName="_grid"/>
<Setter Property="BorderBrush" Value="Black" TargetName="_grid"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

This looks like this:

image

Definitely better. The “Recent Files” item is still in its default state because it’s defined by a different template key (SubmenuHeaderTemplateKey). It’s a bit more difficult, as we must add a popup element (or a StackPanel set as items host) to host the subitem of this header menu item:

<ControlTemplate x:Key="{x:Static MenuItem.SubmenuHeaderTemplateKey}" TargetType="MenuItem">
<Border Background="Yellow" Padding="2" x:Name="_grid" BorderThickness="1" BorderBrush="Yellow" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="24" SharedSizeGroup="IconGroup"/>
<ColumnDefinition Width="Auto" MinWidth="30"/>
<ColumnDefinition Width="30" MinWidth="0" />
</Grid.ColumnDefinitions>
<Path Data="M 0,2 L 10,10 L 0,18 Z" Fill="Red" Grid.Column="2" Margin="20,0,0,0" />
<ContentPresenter ContentSource="Icon" Margin="{TemplateBinding Padding}" />
<ContentPresenter Grid.Column="1" ContentSource="Header" RecognizesAccessKey="True" Margin="{TemplateBinding Padding}" />
<Popup x:Name="popup" PlacementTarget="{Binding ElementName=_grid}" Placement="Right" HorizontalOffset="0">
<ItemsPresenter x:Name="_items" />
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True" >
<Setter Property="Visibility" Value="Visible" TargetName="_items" />
<Setter Property="IsOpen" Value="True" TargetName="popup" />
<Setter Property="Background" Value="Green" TargetName="_grid"/>
<Setter Property="BorderBrush" Value="Black" TargetName="_grid"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

Note the popup control with an ItemsPresenter. Using this approach we can customize all four possible menu item roles.

What about the dear separator, which seems completely out of place? It turns out the separator has a static property for a resource key named SeparatorStyleKey. In this case, it actually is a style, but ironically, we’ll replace its control template:

<Style TargetType="Separator" x:Key="{x:Static MenuItem.SeparatorStyleKey}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Separator">
<Border Background="Blue" Height="3" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Here’s the final look:

image