Watermark TextBox Behavior (Silverlight 4)
Introduction
Recently I implemented a behavior, which is adds to any TextBox control an ability of watermark. But before I going to implementation details, let’s explain what the watermark is. The “watermark” on TextBox control is a text, which is displayed inside while the TextBox is empty (sometimes it also has to be not focused). The idea of watermark text is to hint to user about the purpose of TextBox control. For example, when you logging into the Windows 7, if it password protected you will see a “Password” text inside it. Typing inside it any text will hide the watermark text. Additional example is a search textbox on the top of MSDN site (www.msdn.com). It behaves slightly different, but the idea is the same – inside the search textbox you may notice the “watermark” text: “Search MSDN with Bing”.
If you will think about how to do it in WPF, probably first you will consider to use adorners to render a watermark text above the TextBox control (I know, since it was my initial idea also). But – how unfortunate – there is no adorners in Silverlight (not in current fourth version, at least). Therefore, we have to do some kind of workaround. The common workaround is to create custom control, which template will be very similar to usual TextBox, but will have a Panel atop of it. This Panel is used then to display content above the TextBox, and this way to simulate adorner functionality. While this approach is working, it has some drawbacks:
- In every place you would like to have watermark functionality, you will need to use special custom control.
- You will have to maintain its style, in addition to regular TextBox style.
- What about existing behaviors/attached properties for TextBox control? They are not designed to work with something else.
- Due to my personal preference, I think functionality like this should be attached to existing TextBox control, rather than implementing new control from the scratch.
So, I decided to create special Behavior, which will add watermark functionality to any regular TextBox control. If there were adorners in Silverlight, the task would be trivial. I would add from my behavior an adorner to the attached element – and that’s it. However, like I said before, there are no adorners in Silverlight. Also, I cannot add a Panel above a TextBox, since it already has a parent (and generally this is a bad idea to manipulate logical tree like this). So I had to choose another path: something had to change on the TextBox control itself. I can’t play with the content of the TextBox – since setting it from the behavior could affect bound objects and/or kill a binding (Silverlight 4), in addition to other side effects. But there is something I could actually use – it is a TextBox’s background.
The concept
My idea is simple: create a TextBlock with a watermark text inside. Make it size and font you like. After that, render it to brush. Merge this brush with existing background brush (if any) – this step is essential, since I don’t want to override the exist background. Finally, set the resulting brush to the background of the attached TextBox. When I have to show watermark – I using rendered brush, when I don’t need to show it – I using the original background brush. Now, after I explained the basic idea, let’s dive into the implementation details.
Rendering the control to Brush
If I would do it in WPF, I would use VisualBrush – and case is solved. However, we are in Silverlight and life is not picnic. Lucky me, WriteableBitmap class together with ImageBrush do exist in SL, and we will use them to achieve the same result. The code rendering Control into the brush looks as the following:
WriteableBitmap bitmap = new WriteableBitmap(_watermarkHolder, null);
ImageBrush brush= new ImageBrush() { ImageSource = bitmap, Stretch = Stretch.None };
After that, the brush is ready to be set as a background of TextBox control.
Merging brushes
Since we want to keep the original background of TextBox and yet to show our watermark, we have somehow to merge these two brushes. The only way I know is to take two controls, set each of the brushes to each of the controls, and then put one control on top of the second one. You may give them some opacity if you want for even more “realistic” merge.
Therefore I decided to took a Canvas panel, set its background to background of TextBox, and as its content I set a TextBlock (with watermark text). After that I render all this to the brush – and whoa: I have an original background merged with the watermark text.
Questions:
Q1: Why I had to use a Panel to hold my TextBlock? Why not to take a regular ContentControl for the same purpose?
A1: The TextBlock holder is not part of any visual tree – it is used only to render a background together with TextBlock to brush. Some very simple elements, like Canvas and TextBlock able to render themselves correctly even in this scenario. However, ContentControl, Button, Grid and some other elements I tested cannot perform layout correctly without being part of some visual tree. Even calling explicitly Measure and Arrange overrides does not solves the problem.
Q2: Why I need any holder at all? Why not set a background directly to TextBlock?
A2: TextBlock is not control, and it has not Background property. Canvas is not control either, but Panel class has Background property of its own.
Q3: But if you will set yourself the background of TextBox, you may kill any bindings to the Background property. Furthermore, what if the background of TextBox will change (for example, due to new value from binding) – you need to catch somehow this change and re-render the new merged brush.
A3: This one is tricky, and deserves a separate topic –
Managing background
We have two typical scenarios: first one – the background property set directly; and the second – background property set from binding. I can test whether the property is bound:
BindingExpression expresion = textBox.GetBindingExpression(Control.BackgroundProperty);
If the expression is null – that means the first case – this property is not bound. This is the simplest case: all we need to do is to take the current background, create merged brush and set it back. However, if the expression is not null we have to handle this differently. Instead, we will hijack the binding from TextBox to Canvas. This way, every time the value of binding source will change, instead of setting it to background of TextBox, we will redirect it to background of Canvas. After that, we will recreate the merged brush and set it to background of TextBox:
BindingExpression expresion = AssociatedObject.GetBindingExpression(Control.BackgroundProperty); Binding originalBinding = expresion.ParentBinding; Binding clonedBinding = new Binding(); clonedBinding.Path = originalBinding.Path; clonedBinding.Source = expresion.DataItem; _watermarkHolder.SetBinding(Panel.BackgroundProperty, clonedBinding); AssociatedObject.ClearValue(Control.BackgroundProperty); //clear old binding
However, this is not enough. There are still two problems we have to solve: first – we need to know somehow when the Background of Canvas being changed – to recreate the merged brush. Second – truly tricky problem, which took me few days to solve: at the time when the Behavior being attached, the Binding is not fully initialized yet. If I create it during attachment of Behavior, the Binding will not work and I will not receive notifications. So, in order to make it right, I have to wait until the value from original Binding received, and that’s the right time to redirect it.
Both these problem have the same solution – I need to know somehow when the DependencyProperty is being changed. In WPF you have easy way to do that: by using DependencyPropertyDescriptor.AddValueChanged method you can subscribe to changes of dependency property. However, as you may guess, it is not supported in Silverlight. And again, there is a workaround for this problem: by using intermediate object you can bind to it, and then raise property changed from inside. There is plenty sources where you can read about this, for example here: http://www.amazedsaint.com/2009/12/silverlight-listening-to-dependency.html. Based on this idea, I created a helper with two extension methods:
public static void NotifyOn<T>(this T vmb,string path,Action<object,object> callback) public static void UnNotifyOn<T>(this T vmb,string path)
For clarity (and due to confidentiality), I do not providing here the implementation details of this helper – but it pretty much the same as I described.
Additional Point of Interest
Obviously, the size of Canvas should be the same as the size of TextBox. This is necessary in order to render watermark text correctly. So, my initial idea was to bind height and width of Canvas to the actual height and width of TextBox. However, in Silverlight, this property is buggy and the notifications about value changes are not raised. The workaround is to subscribe to SizeChanged event of TextBox, and then update the size of Canvas manually.
The code
Here the code of my behavior. It may look very long, but most of it is different dependency properties, allowing controlling the rendering of watermark text.
/// <summary>
/// Adds watermark behavior to any <see cref="TextBox"/> control.
/// </summary>
public class WatermarkBehavior : Behavior<TextBox>
{
#region Fields
private Canvas _watermarkHolder = null;
private TextBlock _watermark = null;
#endregion
#region Dependency Properties
#region WatermarkText
/// <summary>
/// Gets or sets the watermark text.
/// </summary>
public static readonly DependencyProperty WatermarkTextProperty =
DependencyProperty.Register(
"WatermarkText",
typeof(string),
typeof(WatermarkBehavior),
new PropertyMetadata(null));
/// <summary>
/// Gets or sets the watermark text.
/// </summary>
/// <value>
/// The watermark text.
/// </value>
public string WatermarkText
{
get { return (string)GetValue(WatermarkTextProperty); }
set { SetValue(WatermarkTextProperty, value); }
}
#endregion
#region WatermarkFontSize
/// <summary>
/// Gets or sets the size of the watermark font.
/// </summary>
public static readonly DependencyProperty WatermarkFontSizeProperty =
DependencyProperty.Register(
"WatermarkFontSize",
typeof(double),
typeof(WatermarkBehavior),
new PropertyMetadata(12.0));
/// <summary>
/// Gets or sets the size of the watermark font.
/// </summary>
/// <value>
/// The size of the watermark font.
/// </value>
public double WatermarkFontSize
{
get { return (double)GetValue(WatermarkFontSizeProperty); }
set { SetValue(WatermarkTextProperty, value); }
}
#endregion
#region WatermarkFontFamily
/// <summary>
/// Gets or sets the watermark font family.
/// </summary>
public static readonly DependencyProperty WatermarkFontFamilyProperty =
DependencyProperty.Register(
"WatermarkFontFamily",
typeof(FontFamily),
typeof(WatermarkBehavior),
new PropertyMetadata(new FontFamily("Century Gothic")));
/// <summary>
/// Gets or sets the watermark font family.
/// </summary>
/// <value>
/// The watermark font family.
/// </value>
public FontFamily WatermarkFontFamily
{
get { return (FontFamily)GetValue(WatermarkFontFamilyProperty); }
set { SetValue(WatermarkFontFamilyProperty, value); }
}
#endregion
#region WatermarkFontWeight
/// <summary>
/// Gets or sets the watermark font weight.
/// </summary>
public static readonly DependencyProperty WatermarkFontWeightProperty =
DependencyProperty.Register(
"WatermarkFontWeight",
typeof(FontWeight),
typeof(WatermarkBehavior),
new PropertyMetadata(FontWeights.Thin));
/// <summary>
/// Gets or sets the watermark font weight.
/// </summary>
/// <value>
/// The watermark font weight.
/// </value>
public FontWeight WatermarkFontWeight
{
get { return (FontWeight)GetValue(WatermarkFontWeightProperty); }
set { SetValue(WatermarkFontWeightProperty, value); }
}
#endregion
#region WatermarkFontStyle
/// <summary>
/// Gets or sets the watermark font style.
/// </summary>
public static readonly DependencyProperty WatermarkFontStyleProperty =
DependencyProperty.Register(
"WatermarkFontStyle",
typeof(FontStyle),
typeof(WatermarkBehavior),
new PropertyMetadata(FontStyles.Italic));
/// <summary>
/// Gets or sets the watermark font style.
/// </summary>
/// <value>
/// The watermark font style.
/// </value>
public FontStyle WatermarkFontStyle
{
get { return (FontStyle)GetValue(WatermarkFontStyleProperty); }
set { SetValue(WatermarkFontStyleProperty, value); }
}
#endregion WatermarkFontStyle
#region WatermarkHorizontalAlignment
/// <summary>
/// Gets or sets the watermark horizontal alignment.
/// </summary>
public static readonly DependencyProperty WatermarkHorizontalAlignmentProperty =
DependencyProperty.Register(
"WatermarHorizontalAlignment",
typeof(HorizontalAlignment),
typeof(WatermarkBehavior),
new PropertyMetadata(HorizontalAlignment.Stretch));
/// <summary>
/// Gets or sets the watermark horizontal alignment.
/// </summary>
/// <value>
/// The watermark horizontal alignment.
/// </value>
public HorizontalAlignment WatermarkHorizontalAlignment
{
get { return (HorizontalAlignment)GetValue(WatermarkHorizontalAlignmentProperty); }
set { SetValue(WatermarkHorizontalAlignmentProperty, value); }
}
#endregion WatermarkHorizontalAlignmentProperty
#region WatermarkFontStretch
/// <summary>
/// Gets or sets the watermark font stretch.
/// </summary>
public static readonly DependencyProperty WatermarkFontStretchProperty =
DependencyProperty.Register(
"WatermarkFontStretch",
typeof(FontStretch),
typeof(WatermarkBehavior),
new PropertyMetadata(FontStretches.Normal));
/// <summary>
/// Gets or sets the watermark font stretch.
/// </summary>
/// <value>
/// The watermark font stretch.
/// </value>
public FontStretch WatermarkFontStretch
{
get { return (FontStretch)GetValue(WatermarkFontStretchProperty); }
set { SetValue(WatermarkFontStretchProperty, value); }
}
#endregion WatermarkFontStretch
#region WatermarkForeground
/// <summary>
/// Gets or sets the watermark foreground.
/// </summary>
public static readonly DependencyProperty WatermarkForegroundProperty =
DependencyProperty.Register(
"WatermarkForeground",
typeof(Brush),
typeof(WatermarkBehavior),
new PropertyMetadata(new SolidColorBrush(Colors.Gray)));
/// <summary>
/// Gets or sets the watermark foreground.
/// </summary>
/// <value>
/// The watermark foreground.
/// </value>
public Brush WatermarkForeground
{
get { return (Brush)GetValue(WatermarkForegroundProperty); }
set { SetValue(WatermarkForegroundProperty, value); }
}
#endregion
#region WatermarkMargin
/// <summary>
/// Gets or sets the watermark margin.
/// </summary>
public static readonly DependencyProperty WatermarkMarginProperty =
DependencyProperty.Register(
"WatermarkMargin",
typeof(Thickness),
typeof(WatermarkBehavior),
new PropertyMetadata(default(Thickness)));
/// <summary>
/// Gets or sets the watermark margin.
/// </summary>
/// <value>
/// The watermark margin.
/// </value>
public Thickness WatermarkMargin
{
get { return (Thickness)GetValue(WatermarkMarginProperty); }
set { SetValue(WatermarkMarginProperty, value); }
}
#endregion
#endregion
#region Methods
/// <summary>
/// Called when behavior attached.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
TextBox textBox = this.AssociatedObject;
_watermarkHolder = new Canvas();
_watermark = new TextBlock();
Binding textBinding = new Binding("WatermarkText");
textBinding.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.TextProperty, textBinding);
Binding horizontalAlignmentBinding = new Binding("WatermarkHorizontalAlignment") { Source = this };
BindingOperations.SetBinding(_watermark, FrameworkElement.HorizontalAlignmentProperty, horizontalAlignmentBinding);
Binding watermarkFontSizeBinding = new Binding("WatermarkFontSize") { Source = this };
BindingOperations.SetBinding(_watermark, TextBlock.FontSizeProperty, watermarkFontSizeBinding);
Binding watermarkFontFamily = new Binding("WatermarkFontFamily");
watermarkFontFamily.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.FontFamilyProperty, watermarkFontFamily);
Binding watermarkFontWeight = new Binding("WatermarkFontWeight");
watermarkFontWeight.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.FontWeightProperty, watermarkFontWeight);
Binding watermarkFontStyle = new Binding("WatermarkFontStyle");
watermarkFontStyle.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.FontStyleProperty, watermarkFontStyle);
Binding watermarkFontStretch = new Binding("WatermarkFontStretch");
watermarkFontStretch.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.FontStretchProperty, watermarkFontStretch);
Binding watermarkForeground = new Binding("WatermarkForeground");
watermarkForeground.Source = this;
BindingOperations.SetBinding(_watermark, TextBlock.ForegroundProperty, watermarkForeground);
Binding watermarkMargin = new Binding("WatermarkMargin");
watermarkMargin.Source = this;
BindingOperations.SetBinding(_watermark, FrameworkElement.MarginProperty, watermarkMargin);
_watermarkHolder.Children.Add(_watermark);
textBox.SizeChanged += OnSizeChanged;
textBox.TextChanged += OnTextBoxTextChanged;
BindingExpression expresion = textBox.GetBindingExpression(Control.BackgroundProperty);
if (expresion != null) //we have something bound to background of textbox - move it to background of grid
{
textBox.NotifyOn("Background", OnOriginalBackgroundSet);
}
else
{
if (textBox.Background != null)
_watermarkHolder.Background = textBox.Background;
UpdateWatermark();
}
}
/// <summary>
/// Called when behavior detaching.
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
TextBox textBox = this.AssociatedObject;
textBox.TextChanged -= OnTextBoxTextChanged;
textBox.SizeChanged -= OnSizeChanged;
}
/// <summary>
/// Called when adorned textbox size changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
_watermarkHolder.Width = e.NewSize.Width;
_watermarkHolder.Height = e.NewSize.Height;
UpdateWatermark();
}
/// <summary>
/// Updates the watermark.
/// </summary>
private void UpdateWatermark()
{
TextBox textBox = this.AssociatedObject;
WriteableBitmap bitmap = new WriteableBitmap(_watermarkHolder, null);
textBox.Background = new ImageBrush() { ImageSource = bitmap, Stretch = Stretch.None };
}
/// <summary>
/// Called when background of adorner change.
/// </summary>
/// <param name="newValue">The new value.</param>
/// <param name="oldValue">The old value.</param>
private void OnBackgroundChange(object newValue, object oldValue)
{
UpdateWatermark();
}
/// <summary>
/// Called when original background set. Used as workaround to solve not initialized binding.
/// </summary>
/// <param name="newValue">The new value.</param>
/// <param name="oldValue">The old value.</param>
private void OnOriginalBackgroundSet(object newValue, object oldValue)
{
AssociatedObject.UnNotifyOn("Background");
_watermarkHolder.NotifyOn("Background", OnBackgroundChange);
BindingExpression expresion = AssociatedObject.GetBindingExpression(Control.BackgroundProperty);
Binding originalBinding = expresion.ParentBinding;
Binding clonedBinding = new Binding();
clonedBinding.Path = originalBinding.Path;
clonedBinding.Source = expresion.DataItem;
_watermarkHolder.SetBinding(Panel.BackgroundProperty, clonedBinding);
AssociatedObject.ClearValue(Control.BackgroundProperty); //clear old binding
}
/// <summary>
/// Called when text box text changed. This method handles the logic of watermark.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
bool watermarkVisible = AssociatedObject.Text.Length == 0;
if (watermarkVisible && _watermark.Visibility == Visibility.Collapsed)
{
_watermark.Visibility = Visibility.Visible;
UpdateWatermark();
}
if (!watermarkVisible && _watermark.Visibility == Visibility.Visible)
{
_watermark.Visibility = Visibility.Collapsed;
UpdateWatermark();
}
}
#endregion
}
}
Now, the WatermarkBehavior can be used on any regular TextBox control. Here is usage example:
<TextBox x:Name="filterBox"
Width="180"
VerticalAlignment="Center"
Text="{Binding Filter, Mode=TwoWay}"
TextAlignment="Left">
<i:Interaction.Behaviors>
<Behaviors:WatermarkBehavior
WatermarkText="Type to filter"
WatermarkMargin="5,5,0,0"
/>
</i:Interaction.Behaviors>
</TextBox>
Enjoy,
Evgeni
WPF 4 vs Silverlight 4
During last four years I was developing a Windows applications using WPF. From time to time, various people were asking me: “what the difference between a WPF and SL”? Well, until now I was never using SL (lucky me), but based on what I heard I was answered: “SL is a subset of WPF”.
Generally speaking, I was right – it is a subset. BUT I was never thinking that subset is so small… I developing using SL4 only a month, but I missing so much things from WPF… The strange thing is, the workarounds for most missing features I met until now in SL is existing – you can find it on Internet or implement yourself. So I really don’t get the reason why they are not implemented from the beginning.
So, I decided to build a list of missing features in SL4, compared to WPF. Next time I will be asked what the difference between them – I will be able to answer more specific. The post will be updated as I will discover something new. You welcome to write in comments what you are missing.
OK, lets start. The missing features in SL4, compared to WPF are:
- No style selectors
- No visuals
- No adorners
- Reflection cannot be used on private members
- DataContractSerializer requires “DataContract” and “DataMember” attributes declared explicitly
- No Binary formatter
- Visability enum has only “Collapsed” and “Visible” states (no hidden)
- No Visual brush (of cause, since no visuals)
- No MiltiBinding
- No FrameworkMetadata
- No SerializableAttribute
- Bugs (under the hood, implementations of libraries in WPF and SL are completely different. Things that work perfectly OK on WPF refuse to work normally on SL. For example, today I tried to call ItemContainerGenerator.ContainerFromItem method on ComboBox. Instead of returning expected container, it always returns null…)
- MeasureOverride and ArrangeOverride methods of panels are sealed
- UUrrrr… bugs are all around. Everything I touching is not working… I spent 4 hours, trying to make a ComboBox width to be as the maximal width of its children. Eventually I gave up, due to bug in ItemsGenerator inside ComboBox: calling to ContainerFromItem method returns null instead of container. So there is no way to know the size of rendered children… After that, I said: OK, lets do another task. I trying to implement kind of adorner for my textbox (there is no adorners in SL – right?). So I took a regular panel, to serve as a layer above textbox. Next thing I was need is to bind size of panel to actual size of textbox. Impossible, since there is a bug with ActualWidth (known issue)…
- There is no Tasks in SL4 (documentation says it will be supported in SL5, though)
- DependencyPropertyDescriptor is not supported
- Cannot use bindings inside styles (but workaround exist, for more information read here)
- …To be continued
Overcoming the limitation of having MultiBinding inside another MultiBinding
Right now, WPF 4.0 is not allowing to have one MultiBinding inside another. If you will try to do that, you will get an exception:
BindingCollection does not support items of type MultiBinding. Only Binding is allowed.
Here the workaround I found (it used from code, but I my case it was exactly what I needed): you can implement a dummy object in the middle, so child multibinding will bind to property on this dummy object, and then make another regular binding from that property to parent multibinding.
Here the code demonstrating the concept:
MultiBinding parentMultiBinding = new MultiBinding(); BindingAdapter adapter = new BindingAdapter (); BindingOperations.SetBinding(adapter, BindingAdapter.ValueProperty, childMultiBinding); Binding adapterBinding = new Binding(); adapterBinding.Source = adapter; adapterBinding.Path = new PropertyPath(BindingAdapter.ValueProperty); parentMultiBinding.Bindings.Add(adapterBinding);
Here example of BindingAdapter class:
private class BindingAdapter : DependencyObject
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(BindingAdapter), new PropertyMetadata(null));
}
Note: if you doing this, you must store the reference to BindingAdapter all the time while you using it, otherwise it will be garbage collected, and the binding will stop working!
Working with “resx” resources
Hello folks,
Recently I had a task to load dynamically resx (the WinForms) resources. The idea was to discover all embedded resources in any of product assemblies. So, I used “GetManifestResourceNames” method to get resources names from inspected assembly, and after that created a ResourceManager instance managing these resources:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly searchAssembly in assemblies)
{
string[] resourceNames = searchAssembly.GetManifestResourceNames();
foreach (var resourceName in resourceNames)
{
ResourceManager manager = new ResourceManager(resourceName , searchAssembly);
}
}
The initialization of ResourceManager passed successfully, however trying to get any string from it was resulting in following exception:
Could not find any resources appropriate for the specified culture or the neutral culture. Make sure “MySolution.g.resources.resources” was correctly embedded or linked into assembly “MySolution” at compile time, or that all the satellite assemblies required are loadable and fully signed.
It took me a while to discover, that the resource name as it returned by “GetManifestResourceNames” method is not good for ResourceManager.
In order to make it work, the postfix “.resources” has to be removed. The following line of code fixes the problem:
ResourceManager manager = new ResourceManager(Path.GetFileNameWithoutExtension(resourceName), searchAssembly);
Cheers,
Evgeni
Lecture 3: WPF architecture
This is a third lecture about WPF. In first one we discussed about the Windows graphical interfaces (namely GDI and DirectX), in the second we talked about different frameworks wrapping these interfaces (MFC, Winforms and WPF). I explained why WPF is so different from any previous technologies. In this lecture we will talk about WPF architecture.
WPF architecture
The following diagram shows the high level view of the WPF architecture:
- PresentationFramework.dll holds the top-level WPF types, such as buttons, windows and other controls. In WPF, all controls re-implemented from scratch, first time from 1985. They are not wrappers around user32 GDI controls anymore, instead they rendering by using DirectX.
- PresentationCore.dll holds base types, from which all shapes and controls derive.
- Millcore.dll is the core of WPF rendering system. This is where rendering thread runs, going over defined visual trees and composes all elements together. DWM uses milcore.dll to compose desktop from off-screen memory buffers. Milcore is written in unmanaged code in order to enable tight integration with DirectX.
In order to demonstrate composition in action, I took the sample WPF application (taken from http://www.codeproject.com/KB/WPF/GuidedTourWPF_1.aspx). Its main window description is very simple, and shown above:
<Grid>
<Grid.Background>
<ImageBrush ImageSource="Resources/Background.jpg" Opacity="0.25" />
</Grid.Background>
<Grid.RowDefinitions>
<!-- The top row is for the race track. -->
<RowDefinition Height="*" />
<!-- The bottom row is for the command strip. -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.Resources>
<ResourceDictionary>
<!-- Import the resource dictionary which contains the DataTemplate for the RaceHorse class. -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/RaceHorseDataTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- This RotateTransform is referenced by the ItemsControl (race track) and Slider. -->
<RotateTransform x:Key="RaceTrackRotateTrans" Angle="0" />
</ResourceDictionary>
</Grid.Resources>
<!-- The 'Race Track' area. -->
<ItemsControl x:Name="raceTrack"
Grid.Row="0"
HorizontalAlignment="Stretch"
LayoutTransform="{StaticResource RaceTrackRotateTrans}"
Margin="8"
VerticalAlignment="Center"
/>
<!-- The 'Command Strip' area -->
<Border Grid.Row="1" BorderBrush="Gray" BorderThickness="1" CornerRadius="8" Margin="12,4">
<!-- The background of the command strip area is set via an inline resource. -->
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1" Opacity="0.8">
<GradientStop Color="#FFFFFF" Offset="0" />
<GradientStop Color="#22AA22" Offset="0.7" />
<GradientStop Color="#448844" Offset="0.95" />
<GradientStop Color="#EEEEEE" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<StackPanel.Resources>
<!-- This Style is applied to all TextBlock elements in the command strip area. -->
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Foreground" Value="#EE000000" />
</Style>
<local:DoubleToIntegerConverter x:Key="RotationDisplayConv" />
</StackPanel.Resources>
<TextBlock Margin="10,4">Rotation: </TextBlock>
<!-- This Slider is bound to the angle of rotation applied to the ItemsControl 'race track.' -->
<Slider x:Name="rotationSlider"
Margin="2,0"
Minimum="-90" Maximum="90"
Width="120"
Value="{Binding Source={StaticResource RaceTrackRotateTrans}, Path=Angle}"
/>
<TextBlock Text="{Binding ElementName=rotationSlider, Path=Value, Converter={StaticResource RotationDisplayConv}}" />
<TextBlock xml:space="preserve"> degrees</TextBlock>
</StackPanel>
<TextBlock Margin="10,4" HorizontalAlignment="Right">
<Hyperlink x:Name="lnkStartNewRace">Start new race</Hyperlink>
</TextBlock>
</Grid>
</Border>
</Grid>
It describes a main grid, which contains two main parts: in first row it has a collection of racetracks, and in second row it has a stack panel, which in turn contains a slider and hyperlink controls with a text around. The main grid has a background picture.
The following picture demonstrates a snapshot of this application:
We can see a simple UI, with a picture in background and bars atop on it. In bottom it has a hyperlink and a slider controls.
As a next step I used a Snoop application, which allows viewing all elements separated (kind of Spy++ application for WPF). Also, it has a nice feature viewing the whole rendered tree in a 3D space. Since everything is retained, every visual item from the tree is available for manipulations, such as 3D projections.
Here is a snapshot of this application, slightly rotated to the side:
You can notice the difference in the z-order: bottom controls have higher order than top ones. This is exactly how rendering works: it goes over the visual tree, and renders element by element. Each next element in tree is rendered on top of the previous one (unless z order specified other than default).
Here is another snapshot of the same application, rotated even more:
Now it’s time to dig deeper into WPF architecture. The following diagram representing the hierarchy of WPF objects:
System.Threading.DispatcherObject
In WPF, Dispatcher object associated with any UI thread. Its job is to coordinate messages, such as keyboard and mouse events. Objects that derive from DispatcherObject have thread affinity, and can be accessed directly from thread that the Dispatcher was created on. DispatcherObject provide methods to determine whether it accessed on the right thread.
System.Windows.DependencyObject
One of the primary architectural philosophies used in WPF was a preference for properties over methods or events. Properties are declarative and allow you to more easily specify intent instead of action. This also supported a model driven, or data driven, system for displaying user interface content. In order to support this, WPF introduced concept of “dependency properties”, which are properties with change notifications, default values, callbacks and more. In order to declare dependency properties, class has to derive from DependencyObject class.
System.Windows.Media.Visual
With a system defined, the next step is getting pixels drawn to the screen. The Visual class provides for building a tree of visual objects, each optionally containing drawing instructions and metadata about how to render those instructions (clipping, transformation, etc.). Visual is designed to be extremely lightweight and flexible, so most of the features have no public API exposure and rely heavily on protected callback functions.
Visual is really the entry point to the WPF composition system. Visual is the point of connection between these two subsystems, the managed API and the unmanaged milcore.
WPF displays data by traversing the unmanaged data structures managed by the milcore. These structures, called composition nodes, represent a hierarchical display tree with rendering instructions at each node. This tree, illustrated on the right hand side of the figure below, is only accessible through a messaging protocol.
When programming WPF, you create Visual elements, and derived types, which internally communicate to the composition tree through this messaging protocol. Each Visual in WPF may create one, none, or several composition nodes.
System.Windows.UIElement
UIElement defines core subsystems including Layout, Input, and Events.
Layout is a core concept in WPF. In many systems there is either a fixed set of layout models (HTML supports three models for layout; flow, absolute, and tables) or no model for layout (User32 really only supports absolute positioning). WPF started with the assumption that developers and designers wanted a flexible, extensible layout model, which could be driven by property values rather than imperative logic. At the UIElement level, the basic contract for layout is introduced – a two phase model with Measure and Arrange passes.
Measure allows a component to determine how much size it would like to take. This is a separate phase from Arrange because there are many situations where a parent element will ask a child to measure several times to determine its optimal position and size. The fact that parent elements ask child elements to measure demonstrates another key philosophy of WPF – size to content. All controls in WPF support the ability to size to the natural size of their content. This makes localization much easier, and allows for dynamic layout of elements as things resize. The Arrange phase allows a parent to position and determine the final size of each child.
System.Windows.FrameworkElement
The two most critical things that FrameworkElement introduces are data binding and styles. The data binding subsystem in WPF should be relatively familiar to anyone that has used Windows Forms or ASP.NET for creating an application user interface (UI). In each of these systems, there is a simple way to express that you want one or more properties from a given element to be bound to a piece of data. WPF has full support for property binding, transformation, and list binding.
One of the most interesting features of data binding in WPF is the introduction of data templates. Data templates allow you to declaratively specify how a piece of data should be visualized. Instead of creating a custom user interface that can be bound to data, you can instead turn the problem around and let the data determine the display that will be created.
Styling is really a lightweight form of data binding. Using styling you can bind a set of properties from a shared definition to one or more instances of an element. Styles get applied to an element either by explicit reference (by setting the Style property) or implicitly by associating a style with the CLR type of the element.
System.Windows.Controls.Control
Control’s most significant feature is templating. If you think about WPF’s composition system as a retained mode rendering system, templating allows a control to describe its rendering in a parameterized, declarative manner. A ControlTemplate is a piece of XAML, describing look and feel of control. When designer alters presentation of control, it actually alters its template.
That’s all. I hope after these three lectures you will be more familiar with WPF. WPF is great technology which solves many problems (and creates a new ones). From my experience, developing using WPF (in avarage) takes more time than using WinForms. The concept of visual trees introducing time consuming problems, related to binding and routed events. In addition, WPF suffers from bad scalability (in terms of amount of objects). However, WPF brings many cool features to the table, such as truly separated presentation and functionality layers, skinning and customization abilities and much more.
Right now I have no planned future lectures, but I will do something based on FAQ and feedback.
Lecture 2- Windows UI Frameworks and introduction to WPF
This is a second lecture in the WPF series. In the previous one, we discussed about two Windows Graphical interfaces: GDI and DirectX. We compared these two technologies, and showed advantages and disadvantages of each of them.
In this lecture, we will talk about Microsoft Graphical Frameworks, under the hood using these interfaces. The first one we going to talk about is a Microsoft Foundation Classes (or MFC in short).
MFC
MFC was introduced in 1992, as an object-oriented C++ wrapper for the Windows API. Several classes in MFC (such as CButton) designed to wrap various user interface elements, which are GDI based and reside inside user32.dll.
Windows Forms
In early 2003 Microsoft introduced .NET framework, which is a managed wrapper for the Windows API. .NET introduced set of managed wrappers of user interface elements, together called “Windows Forms”. In addition, it exposed GDI functionality via Graphics class in System.Drawing namespace.
Both technologies are “just” wrappers around the Windows API, using the same GDI elements defined in 1985 in user.dll, which later became to be user32.dll. Both technologies using immediate rendering mode: the application repaints the area that becomes invalidated – just as the underlying GDI they use (this is partially true, because interface user elements do know to repaint themselves, when invalidated).
In order to alter the look and feel of user elements, the software developer have to override OnPaint method (in other words, to handle WM_PAINT message). Inside this method, the developer has to describe the required look and feel by calling to GDI API. For example, consider the following peace of code, which is drawing a custom button (taken from http://www.codeproject.com/KB/buttons/zhaocolorbutton.aspx):
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
LinearGradientBrush brush;
LinearGradientMode mode;
//
// set SmoothingMode
//
switch (_SmoothingQuality)
{
case SmoothingQualities.None:
e.Graphics.SmoothingMode = SmoothingMode.Default;
break;
case SmoothingQualities.HighSpeed:
e.Graphics.SmoothingMode = SmoothingMode.HighSpeed;
break;
case SmoothingQualities.AntiAlias:
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
break;
case SmoothingQualities.HighQuality:
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
break;
}
//
// set LinearGradientMode
//
switch (_GradientStyle)
{
case GradientStyles.Horizontal:
mode = LinearGradientMode.Horizontal;
break;
case GradientStyles.Vertical:
mode = LinearGradientMode.Vertical;
break;
case GradientStyles.ForwardDiagonal:
mode = LinearGradientMode.ForwardDiagonal;
break;
case GradientStyles.BackwardDiagonal:
mode = LinearGradientMode.BackwardDiagonal;
break;
default:
mode = LinearGradientMode.Vertical;
break;
}
SizeF textSize = e.Graphics.MeasureString(this.Text, base.Font);
int textX = (int)(base.Size.Width / 2) - (int)(textSize.Width / 2);
int textY = (int)(base.Size.Height / 2) - (int)(textSize.Height / 2);
Rectangle newRect = new Rectangle(ClientRectangle.X + 1, ClientRectangle.Y + 1,
ClientRectangle.Width - 3, ClientRectangle.Height - 3);
if (_Active)
{
switch (_State)
{
case _States.Normal:
brush = new LinearGradientBrush(newRect, _NormalColorA, _NormalColorB, mode);
switch (_ButtonStyle)
{
case ButtonStyles.Rectangle:
e.Graphics.FillRectangle(brush, newRect);
e.Graphics.DrawRectangle(new Pen(_NormalBorderColor, 1), newRect);
break;
case ButtonStyles.Ellipse:
e.Graphics.FillEllipse(brush, newRect);
e.Graphics.DrawEllipse(new Pen(_NormalBorderColor, 1), newRect);
break;
}
e.Graphics.DrawString(this.Text, base.Font, new SolidBrush(base.ForeColor), textX, textY);
break;
case _States.MouseOver:
brush = new LinearGradientBrush(newRect, _HoverColorA, _HoverColorB, mode);
switch (_ButtonStyle)
{
case ButtonStyles.Rectangle:
e.Graphics.FillRectangle(brush, newRect);
e.Graphics.DrawRectangle(new Pen(_HoverBorderColor, 1), newRect);
break;
case ButtonStyles.Ellipse:
e.Graphics.FillEllipse(brush, newRect);
e.Graphics.DrawEllipse(new Pen(_HoverBorderColor, 1), newRect);
break;
}
e.Graphics.DrawString(this.Text, base.Font, new SolidBrush(base.ForeColor), textX, textY);
break;
case _States.Clicked:
brush = new LinearGradientBrush(newRect, _HoverColorA, _HoverColorB, mode);
switch (_ButtonStyle)
…
The revolution
From 1985 and until 2006, the development lifecycle was very similar: the designer person drawing the picture of user interface by using its tools, such as Adobe Photoshop (probably in 1985 they used pen and paper). Next, this picture is shown to developer. By overriding OnPaint method, developer writes a code describing the look of the controls and doing his best to match as possible the pictures prepared by designer.
As you probably know from your own experience, rarely the produced UI by developer looks exactly as designer intended to be. First, the static picture of UI does not provide all necessary information about user experience: what happens when mouse over the button? When dragging an item? When selecting item in list view? How UI behave when less space is available? Secondly, even matching a color is not easy task – the developer has to receive exact numbers for every used color, or otherwise to guess it.
Obviously, during the process of translating from designer sketches to developer implementation there is an information loss. To prevent it, the designer has to be the one who creates the UI – in order to make it just as he imagined it. However, there is a problem: designers are not software developers! They have no idea about variables, messages, methods, loops and so on. They usually never have to type any text. The code presented above (demonstrating override of OnPaint) is a Chinese for an average designer. Instead, designers live in world of graphical tools, brushes and colors. So, Microsoft came with solution: let’s create a tool intended for designers, which will produce a data describing the UI. The tool named Blend, the data it producing named XAML, and the technology capable to translate this data to graphical output called WPF.
WPF
The idea behind WPF is to separate completely the functionality and the presentation. For me, this separation is perfectly natural: different persons with different skills making different application parts with different tools. The designers which are skilled to create visual staff draw presentation by using Blend/Illustrator/Designer/Photoshop tools. The software developers which are skilled in implementing algorithms and software architecture create a code behind by using Visual Studio.
Development in WPF is very similar to ASP.NET: you have a presentation layer described by some way (in ASP.NET that is HTML), and you have a code behind doing the actual logic. Designer drawing a button and the developer creates a logic handles the button click.
To describe presentation layer, Microsoft presented declarative object oriented language, called “XAML”. Its structure is very similar to XML or HTML: it consists of hierarchical nodes with attributes. Here is a XAML for example:
<Canvas> <Button Name="button" Background="Pink" BorderBrush="Black" BorderThickness="1" > Hello! </Button> </Canvas>
In this example, we have a node “Canvas” which contains a node “Button” with attributes describing it, and content inside it – text “ClickMe1”. The button itself can be described by hierarchy of visual elements: the border around, the rectangle, the text block inside, etc.
Since everything about presentation is described in XAML (and not in code), XAML provides everything needed to describe any possible UI: different controls, borders, geometries, brushes, images, animation and so on and so on. So, with help of XAML, it is possible to describe any desirable UI as a tree of visual elements.
Consider the following example, which is describes Glass-like button (taken from http://www.jadeskaggs.com/2009/05/04/creating-custom-glass-buttons-with-xaml-in-wpf/):
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ButtonBorder"
CornerRadius="25,25,25,25"
BorderThickness="4,4,4,4"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="1.7*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" CornerRadius="23,23,0,0">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#08FFFFFF" Offset="0"/>
<GradientStop Color="#88FFFFFF" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<ContentPresenter x:Name="ButtonContentPresenter"
VerticalAlignment="Center"
Grid.RowSpan="2"
HorizontalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform" TargetName="ButtonBorder">
<Setter.Value>
<TransformGroup>
<ScaleTransform ScaleX="0.9" ScaleY="0.9"/>
</TransformGroup>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
The resulting button will look as the following:
You may notice that very important part is missing: where is the famous “OnPaint” method? There is none. Since designers are not software developers, they have no idea about shared memory, and WM_PAINT message and so on. They drawing the UI once and expect it to render correctly when it have to. So under the hood, the WPF itself handles the rendering of XAML. After designer describes UI in XAML (which is hierarchical), during runtime every node in XAML is translates to .NET object, and the whole XAML translates to a tree of objects. WPF is responsible to create and initialize these objects (this is why every WPF objects have a public constructor without arguments). From that moment, all these objects live in WPF system, and WPF responsible to render them when necessary. In other words, everything in WPF is retained. WPF have special rendering thread, which is runs in the background and renders those visual trees when necessary. In GDI, to draw a line developer was needed to create a code maintaining that line during invalidations inside of “OnPaint” method. In WPF, designer creates a line object (by describing it in XAML), and that object lives in memory and holding all necessary data inside (such as color and coordinates) in order to render it as desired.
So, we have two separated parts: the presentation and functionality. WPF provides mechanism for connecting these parts without introducing dependencies in code behind – Binding. On every property of UI element it is possible to set binding to property on code behind. The bindings set in XAML part, so if everything done correctly – the code behind have no idea how it is rendered, since everything about presentation located in the presentation layer – just as it should be.
In WPF, binding plays very important role – actually this is the only possible way to access code behind data from XAML part. It is one of the core concepts of WPF. The binding concept itself is not new, and introduced a time ago in Windows Forms. However, in WPF binding is much more powerful: it is possible to bind to properties, nested classes, methods by using different modes and convertors in the middle. The source and target of binding can be anything in visual tree or code behind.
The following drawing demonstrates the WPF concept:
In addition to retained mode and separation between presentation and functionality, WPF brings additional features:
- Hardware acceleration – all WPF rendering is performed using DirectX.
- Resolution independence – since WPF rendering all user interface elements itself, it not suffers from resolution problem as GDI/GDI+ does.
- Advanced support for text rendering: WPF gives ability to display rich, styled text anywhere in a user interface.
- Improved support for handling RTL.
- Ability to define animations with declarative tags.
- Support for audio and video media – it allows to play any audio or video file supported by Windows Media player.
Styles and templates – allows changing the way any element is rendered – even on the fly.
WPF Limitations
Due to retained rendering mode, WPF is very sensitive for complexity of the rendered objects and the number of objects. Rendering thread has to go over every visual tree and render each element inside it (using measure and layout calls) – complex elements will require longer rendering times. In addition, since all elements are held in memory (retained scene) – the scaling of WPF applications in terms of number of objects is bad. From my experience, when I tried to use about 1000 geometry drawings on pretty strong machine, the performance degradation was very noticeable. In WPF 4, the performance can be improved my using bitmap cache.
Although these limitations, I think WPF suit well for most Windows applications. In case extra performance is needed – you should consider using Direct2D/Direct3D.
Technology Compare
| MFC | Windows Forms | WPF | Direct2D | |
| Year of release | 1992 | 2003 | 2006 | 2009 |
| Underlying interface | GDI | GDI | Direct3D | Direct3D |
| Is managed? | No | Yes | Yes | No |
| Is hardware accelerated? | Only BitBlt | Only BitBlt | Yes | Yes |
| Rendering mode | Immediate | Immediate | Retained | Immediate |
| Unit of measure | Hardware Pixel | Hardware Pixel | Device-independent unit (1/96-inch) | Device-independent |
Coming next: Lecture 3: WPF architecture
Sometimes Dependency Properties piss me off
Generally speaking, I like the concept of dependency properties – the ability to attach data to something is very handly. However, I don’t like how this implemented. Lets put a side their syntax – even after writing 1000 of them, I can’t write 1001 time without looking to previous examples. The most worst thing is a combination of dependency properties and items control. As you know (and maybe not), items control generates containers for every (non UIElement) item in Items collection. Now, assume that item (it’s UIElement representation) has any attached properties defined (such as Canvas properties). The canvas will test its direct children, which are containers – which doesn’t have these properties defined.
Huston, we have a problem! How we suppose to define dependency properties on item, if we don’t know how these items will be used? If we use just a panel it will work, if we will have ItemsControl in the middle – it will not. So we have only two ugly options: first is in style of items container define these properties with binding to content; and second to generate our own containers – if possible – and during that process create necessary bindings.
I think dependency properties should expose ability to make them visible to whole visual tree. I mean, if parent doesn’t have a required property – test its children, and so on.
Lecture 1: Windows graphical interfaces
Recently I was asked to lecture in my company, Netformx, about WPF. My goal was to make this lecture different from what you usually may see in WPF courses and events – instead of saying good sounding buzzwords and showing nice graphics, I intended to explain about how WPF is different from other available Microsoft technologies, and why it looks as it looks. So I started to gather materials to base some technical background before digging deep into WPF, and very soon I realized it can be a standalone topic, justifying a separate lecture. Therefore, my first lecture will be called:
Windows graphical interfaces
Since 1985, when first version of Microsoft version released and up today, Microsoft released two main graphical interfaces: GDI and DirectX. Different frameworks such as a WPF are using these interfaces under the hood. In this lecture, we will discuss about each of them, and learn about appliances and limitations.
GDI
Graphic Device Interface (or GDI in short) was introduced back in 1985 with the first version of Microsoft Windows. The idea behind GDI is to create an abstraction of physical device, so calling application doesn’t have to adjust its code for specific device. In other words, the same code that using GDI will be able to draw graphics on any GDI compatible device – and it will look the same each time. The device can be even a printer – and the calling application will not notice any difference. Microsoft called to that abstraction as “Device Context” – or DC in short.
So, GDI has two distinct worlds: the physical and the logical. Applications know about logical world, and telling to GDI what they want to draw, using logical GDI objects (such as pens, brushes, fonts, palettes, and bitmaps). Internally, Windows using GDI to draw its windows (frames) and common controls, such as message box. Any control based on GDI, consuming GDI objects. For example, when I opened a new empty window in GDI application I created, it consumed 24 GDI objects. Adding a button with text inside that window consumed additional 4 GDI objects. You can monitor how much GDI objects consumed by each process by opening Task Manager (by default, GDI Objects column is hidden). There is a limit of GDI objects, available for each process (the limit is various for different Windows versions). After the limit is reached, no more windows or controls can be created.
Next step after GDI receiving a call from application with logical objects, logical objects been converted to device specific physical objects, using device driver. This process called “rasterizing”. During rasterizing, requested (logical) color is mapped to available colors, fonts are matched to the best available fonts, and patterns are prepared for output. Some parts of realization process handled by device driver, other handled by GDI itself. The video driver is free to use any combination of CPU and GPU processing, however today only BitBlt API is hardware accelerated.
Early during boot (and Windows install) when no manufacturer-specific driver is available, the GDI does perform all rendering in software and writes to the framebuffer, which is just the memory area which feeds the GPU RAMDAC and mapped into the CPU address space. The framebuffer is stored in one of several well-known formats (defined by VESA). During this phase, you can notice that graphics is extremely slow (in selecting a region, try to open and scroll list of countries – it barely moving). This is because there is no video driver yet, and everything is done without any hardware acceleration.
GDI uses immediate rendering mode: the application repaints the area that becomes invalidated. On every invalidation, the WM_PAINT message is received by window. It is an application responsibility to handle that message, and to draw its state using GDI.
By maintaining the two separate but linked spaces, logical for the applications and physical for the devices, GDI creates a device-independent interface. Applications that make full use of the logical space and avoid device-specific assumptions can expect to operate successfully on any output device that comes down the turnpike. However, this advantage has a drawback: since GDI interface is a minimal subset of operations, which is common across different devices (video cards and printers), it does not expose advanced abilities of modern hardware such as visual effects, transforms, light sources, physics and more. As a result, any operation such this has to be calculated manually by application on CPU, before sending the result to GDI for rasterizing. This limitation (and inability to rasterize3D) makes GDI not suitable well for animations and rich visual effects.
GDI+
GDI+ presented in 2001 with release of Windows XP, as a successor of GDI. It resides in gdiplus.dll. GDI+ expanding GDI by adding the following abilities:
- 2D vector graphics. Vector graphics is based on graphic primitives like: lines, curves and shapes, all defined as a set of points on a coordinates system. Everything is relative regarding the origin of the coordinate system. Considering this, vector-based graphics can be scaled without losing details, which is a good thing, but rendering vector-based graphics demands a lot of CPU calculation (it is not hardware accelerated).
- Imaging. GDI+ can handle different types of bitmaps, including icons, PNG and JPEG formats and perform different image operations like: stretching and skewing.
- 2D anti-aliasing
- Floating point coordinates (needed for making resolution independence)
- Alpha blend for colors (ARGB)
Overall, GDI+ is a little bit slower than the old GDI but provides better rendering quality and clean code.
Despite all the improvements of GDI, the same advantages (device abstraction) and disadvantages (device abstraction) are remaining. GDI+, just as GDI is good in drawing windows and buttons – but when it comes to animations or visual effects it very CPU consuming, since it very limited and (mostly) not hardware accelerated.
Note, that from 1985 and up today, during something like 25 years, GDI received only 1 update. I believe the reason for that is due the purpose of GDI to be a simple cross device graphical interface, Microsoft changes (almost) nothing in it, to keep it simple and generic.
DirectX
Back in 1994, while preparing for release of Windows 95, Microsoft realized that GDI is not suitable for high performance multimedia, such as games. Graphic cards (with 3D acceleration) was optimized to perform complex vector calculations, visual effects and more – but GDI didn’t provided access to these functions, in order to keep its interface simple and generic, common between video cards and printers. Game developers needed direct access to video card, sound device and mouse without any abstraction layers in the middle. Therefore in 1995, as part of Windows 95, Microsoft released a “bypass” – a set of interfaces for direct access, together called as “DirectX”. In scope of graphics, two most important interfaces are “Direct3D” – for rendering 3D graphics and DirectDraw (replaced by Direct2D in 2009) – for rendering 2D graphics.
DirectX (namely Direct3D) providing developers low level access to all functionality, the video cards capable to perform. In order to be updated, it keeps evolving together with graphical cards. Microsoft is all the time in touch with leading video cards manufacturers and constantly adjusting interfaces of DirectX, in order to expose newest functionality. From 1995 and until today, more than 25 versions of DirectX have been released.
Everything in Direct3D is hardware accelerated – many complex calculations performed by GPU, thus reducing the load on CPU. This comes at a price, however. When using DirectX, developers have to test whether current graphic card supports specific operation. If it not, they have to plan some fallback (like disabling animations, reducing quality and so on). So, developers have to work harder, but they can do much more compared to GDI – and much faster.
Direct3D works in immediate mode: it follows the notation of “paint” calls to draw each frame.
DirectDraw is another graphical interface from a bundle called “DirectX”. It designed to render 2D hardware accelerated graphics (also in immediate mode). DirectDraw allows direct access to video memory, hardware overlays, hardware blitters, and page flipping. Its video memory manager can manipulate video memory with ease, taking full advantage of the blitting and color decompression capabilities of different types of display adapters. Today it is deprecated, in favor of its successor Direct2D (D2D).
Direct2D is a latest graphic interface / technology released by Microsoft. It presented in 2009, together with DirectWrite (for a text layout) in Windows 7. According to Microsoft, the goal of D2D is to replace both DirectDraw and GDI+. In this MSDN page (http://msdn.microsoft.com/en-us/library/ff729480%28v=vs.85%29.aspx), they recommend to consider writing new graphics code using Direct2D.
While GDI supports hardware acceleration for certain operations (BitBlt API), many GDI operations are bound to the CPU. Direct2D is layered on top of Direct3D, and takes full advantage of hardware acceleration provided by the GPU. If the GPU does not support the features needed for Direct2D, then Direct2D falls back to software rendering. Overall, Direct2D outperforms GDI and GDI+ in most situations.
Summary so far – Windows graphic APIs:
Desktop Window Manager
Before Windows Vista, the rendering was an easy thing: there was one shared memory buffer (which represented a desktop), and everybody wrote into it. Consider the example, where you have two open windows, one over other. Moving upper window to new position will draw it at the new position, but since it shares a memory buffer with the second window, the second window will remain overwritten, as shown in the picture below:
In order to fix this issue, the bottom window has to handle a WM_PAINT message and to redraw itself. If the upper window will move constantly, the bottom window will have to redraw itself in high rate, and this will produce a “flicker” phenomenon. This is where the BitBlt API is very useful: using it, it is possible to implement “double buffer” technique and thus avoid “flicker” issue. In my previous article (http://evgenya.wordpress.com/2008/01/05/fast-rendering-images-in-c/), I demonstrated another uses for BitBlt for fast image rendering in C#.
Windows Vista presented several new features and these features caused to changing the way the graphics is rendered:
- Aero Glass – the semi-transparent look that Aero provides, with the blurry content behind the window frames, designed to allow the user to focus on the window itself, and not on what lies behind:
- Live thumbnails – previews provided on the taskbar:
- Flip3D – the ability to scroll open windows in the 3D space, for navigating between and selecting windows:
The component which handling all this staff called Desktop Window Manager, or DWM in short. Starting from Windows Vista, instead of writing into one shared memory buffer, each application writes to its own memory buffer. The application is not aware of that, and “thinks” that it writes to main buffer as usual. DWM intercepts these calls, and redirects them to off-screen buffer. This process called redirection. After that, DWM composes all this together in one single composed desktop. This change was necessary to support all features presented in Vista:
- Since every window is stored in different buffer, during composition the DWM can determine the color of each pixel. This is how frame transparency is achieved.
- Since the off-screen buffer is constantly updated by the application, the embedded rendering will be a dynamic representation of the application window and not a static rendering. This is how the live thumbnail previews, Windows Flip and Windows Flip 3D work in Windows Vista.
Moreover, since every window is retained and there is no shared memory buffer anymore, moving one window over other will not cause to the redraw of bottom window.
The DWM uses DirectX 9 to perform the function of compositing and rendering in the GPU, freeing the CPU of the task of managing the rendering from the off-screen buffers to the display. However, it does not affect applications painting to the off-screen buffers; depending on the technologies used for that, it might still be CPU-bound. DWM-agnostic rendering techniques like GDI are redirected to the buffers by rendering the UI as bitmaps. DWM-aware rendering technologies like WPF directly make the internal data structures available in a DWM-compatible format. The window contents in the buffers are then converted to DirectX textures.
The redirection process works different for different technologies:
GDI – rendered windows
- A system memory (in Win 7 – an aperture memory) surface the size of the window is allocated and associated with that window.
- A video memory surface, in the target DirectX pixel format, is allocated, also the size of the window.
- When an application retrieves the GDI DC of an HWND, it no longer is the DC of the primary video buffer, as it is in the non-composited, pre-DWM desktop. Instead, the DC is a DC onto the allocated system memory surface.
- GDI operations on that DC then populate the system memory surface.
- The system, based on a number of variables, decides to update the video memory surface from the system memory surface at the “right times”.
- The video memory surface is now up-to-date with the application, and the compositor comes around and uses the video memory surface to composite the desktop from.
In GDI based windows, for each window allocated memory buffer (in Vista it is a system memory, in Win 7 – aperture memory). Since GDI uses its own format (GDI objects, etc), it renders as usual to its DC, which is a memory buffer. After that, the whole buffer moved as a single bitmap to video buffer of DWM (which is using DirectX pixel format).
DirectX – rendered windows
DirectX applications can natively render into the DirectX pixel format that the DWM expects. As such, DirectX applications only need a single window buffer to manage their redirection. DirectX window redirection is handled by having the DirectX system, when it’s determining what surface to provide the app with to render to, make calls to the DWM in order to share a surface between the DirectX client application process, and the DWM process. WPF applications are DirectX applications, so the rendering process for them is the same.
GDI limitations under Windows Vista
At time of developing of Windows Vista, Microsoft found too difficult to combine the new graphical engine with GDI. As a result, under Windows Vista there is no at all hardware acceleration for GDI. This is why under the Vista, GDI uses system memory to store a buffer: GDI is being rendered utilizing the CPU completely in the operating system without any assistance or “acceleration” by the graphics hardware, and it requires an easily accessible cacheable copy of memory.
In addition, the design of GDI synchronization mechanism in Vista allows only a single application to hold a system-wide exclusive global lock:
In other words, if there are several GDI applications running at the same time on machine, one application will wait for other to perform rendering.
Both of these problems were fixed in Windows 7, through according to different sources GDI performance under Windows 7 is lower than under Windows XP.
I found this video, comparing performance of GDI under XP and Windows 7:
The following tables summarizes this lecture:
| CriteriaTech\ | Mode | Is hardware accelerated | DWM compatible |
| GDI | Immediate | Partly (non under Vista) | No |
| GDI+ | Immediate | Partly (non under Vista) | No |
| D2D | Immediate | Yes | Yes |
| D3D | Immediate | Yes | Yes |
| CriteriaTech\ | Uses system memory | Is resolution independent | Is allow direct access to video card | Supported in Windows XP? |
| GDI | Yes (GDI objects) | No | No | Yes |
| GDI+ | Yes (GDI objects) | No | No | Yes |
| D2D | No | Yes | ? | No |
| D3D | No | ? | Yes | Yes |
(as “?” I marked fields where I not 100% sure)
From the table above, we can learn that DirectX technology is far superior to a GDI one. Unlike GDI, DirectX is being constantly updated to reflect latest video hardware abilities. Under windows Vista, GDI has no hardware acceleration at all and suffers from GDI graphics stack bottleneck. Under rest of the operating systems, GDI has only BitBlt API hardware acceleration.
Coming Next:
Lecture 2- Windows UI Frameworks and introduction to WPF
References
- http://stackoverflow.com/questions/6591227/how-gdi-gdi-works-without-opengl-or-directx
- http://dotnetfacts.blogspot.com/2008/04/gdi-windows-rendering-api.html
- http://msdn.microsoft.com/en-us/library/ff684176%28v=vs.85%29.aspx http://en.wikipedia.org/wiki/Microsoft_Direct3D
- http://blogs.msdn.com/b/directx/archive/2009/05/12/2d-drawing-apis-in-windows.aspx
- http://msdn.microsoft.com/en-us/library/ms536380%28v=vs.85%29.aspx
- http://blogs.msdn.com/b/e7/archive/2009/04/25/engineering-windows-7-for-graphics-performance.aspx
- http://msdn.microsoft.com/en-us/library/ff729480%28v=vs.85%29.aspx
- http://blogs.msdn.com/b/directx/archive/2009/09/29/comparing-direct2d-and-gdi.aspx
- http://www.youtube.com/watch?v=ay-gqx18UTM
- http://www.tomshardware.com/reviews/2d-windows-gdi,2539-11.html http://blogs.msdn.com/b/greg_schechter/archive/2006/05/02/588934.aspx
- http://en.wikipedia.org/wiki/Desktop_Window_Manager
- http://blogs.msdn.com/b/greg_schechter/archive/2006/03/05/544314.aspx
- http://blogs.msdn.com/b/greg_schechter/archive/2006/04/02/566767.aspx
WPF Thumb control capturing mouse!
In this post I would like to point to some peculiarity of the WPF thumb control, knowing which may save you hours of debugging. I was trying to make a custom control, which template was a thumb primitive. As part of control behavior, I wanted to change mouse cursor when the mouse hovering over the control. However, setting the cursor property of my control didn’t have any effect. The reason is, that internally Thumb control calling to CaptureMouse() method on mouse down and thus preventing from setting mouse cursor of template parent. There are 3 solutions to overcome this problem (I tested first two, but I pretty sure the third will work also):
a. Inside template, bind thumb’s cursor property to parent control’s cursor property.
b. Instead of deriving from control, derive from Thumb.
c. Instead o setting cursor inside control’s style, set inside thumb’s style (inside template).
Clipboard and OutOfMemoryException
During my professional career as software developer, I noticed many times that even the most trivial and simple things are able to hide a pitfalls, which will consume unexpected amount of time. I had to admit, .NET compared to C++ provide much less “surprises” of this kind, but still there is a plenty (especially in WPF). Recently I considered to use Clipboard in some application, in copy and paste scenario. The API was looking very straightforward: to store object: Clipboard.SetData(“DataOwnerKey”, dataToStore); and to retrieve: object myData = Clipboard.GetData(“DataOwnerKey”); What can be simplier than that? Well, this is exactly the example of how simple thing can turn into pain in the ass. After I implemented my initial version, during the call to “GetData” method… I crashed on “OutOfMemoryException”. Trust me, on that machine was enough memory to store thousands of objects like the one I tried to store. After something like a hour of googling and trying to figure out what the f.. is going here, I discovered that my object was missing Serializable attribute on it. I have to admit, its my fault that I released an object without Serializable attribute (that against best practices) – but why OutOfMemoryException?? However, the story doesn’t ends here. After I added the attribute, I was still crashing on the same exception. I spent another hour, but eventually I did found the problem: my object exposed event (PropertyChanged), and someone subscribed for it. I not sure how exactly that preventing from object to be serialized, but that’s the fact. Maybe that’s because delegates are not serializeable? Anyway, the default serialization was failing, and to overcome this problem I had to implement my own serialization, implementing ISerializable interface. I not going into implementation details, nothing interesting there. After I implemented my own serialization, the staff finally started to work. Horay! Only two hours for storing – retrieving data from clipboard…














