Lecture 2- Windows UI Frameworks and introduction to WPF

August 14, 2011 at 10:30 am Leave a comment

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

Advertisement

Entry filed under: WPF. Tags: .

Sometimes Dependency Properties piss me off Lecture 3: WPF architecture

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Categories


Follow

Get every new post delivered to your Inbox.