CodeProject, WPF

Lookless Controls / Themes

One of the great things about WPF is that it separates the functionality of a control from the way it looks, this has become known as “lookless controls”. Which is great, but how can we ensure that our custom controls behave and also have a default look in the first place. This mini article will try and show you what happens with lookless controls.

Ensuring Control Does What We Want It To Do

I will firstly talk a bit about how you can create a split designer/developer project that should work correctly.

The first things that a developer should do is create a TemplatePartAttribute such that this is captured in the metadata which can be used by an documentation tool. By using this TemplatePartAttribute the developer is able to tell the designer what was intended for a correct control operation.

Here is an example for a small control that I have made

   1:      [TemplatePart(Name = "PART_DropDown",
   2:        Type = typeof(ComboBox))]
   3:      public class DemoControl : Control
   4:      {
   5:      }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

This should be an alert to the designer that they need to create a part of the control Template that should be a ComboBox and should be called “PART_DropDown”. So that is part1, next the developer should override the OnApplyTemplate and look for any expected parts that are required to make the control work properly and wire up the events required. Here is an example.

   1:          public override void OnApplyTemplate()
   2:          {
   3:              base.OnApplyTemplate();
   4:  
   5:              //Obtain the dropdown and create the items
   6:              dropDown =
   7:                  base.GetTemplateChild(
   8:                  "PART_DropDown") as ComboBox;
   9:              if (dropDown != null)
  10:                  dropDown.SelectionChanged +=
  11:                      new SelectionChangedEventHandler(
  12:                          dropDown_SelectionChanged);
  13:  
  14:  
  15:          }
  16:  
  17:          void dropDown_SelectionChanged(object sender,
  18:              SelectionChangedEventArgs e)
  19:          {
  20:  
  21:  
  22:          }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Another method is to rely on RoutedCommands that should be used by the designer in the XAML control Template. These can then used as follows:

   1:  
   2:              // Make sure the command is bound, so that it will work when called to
   3:              CommandBindings.Add(new
   4:                  CommandBinding(DemoCommands.SayHello,
   5:                  //The actual command handler code
   6:                  (s, e) => {
   7:                      MessageBox.Show("Hello");
   8:                  }));

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Lookless Controls

In order to create a truly lookless control, we should do the following:

Override the default Style associated with a control, this is done by changing the metadata. An example of which is as follows:

 

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

   1:          static DemoControl()
   2:          {
   3:              //Provide a default set of visuals for a custom control
   4:              DefaultStyleKeyProperty.OverrideMetadata(
   5:                  typeof(DemoControl),
   6:                  new FrameworkPropertyMetadata(
   7:                      typeof(DemoControl)));
   8:          }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }Next we need to understand a few things about how Themes work in WPF. There is an assembly level attribute that is called ThemeInfoAttribute, which is typically created as follows:

   1:  [assembly: ThemeInfo(
   2:      ResourceDictionaryLocation.None,
   3:      //where theme specific resource dictionaries are located
   4:      //(used if a resource is not found in the page, 
   5:      // or application resource dictionaries)
   6:      ResourceDictionaryLocation.SourceAssembly
   7:      //where the generic resource dictionary is located
   8:      //(used if a resource is not found in the page, 
   9:      // app, or any theme specific resource dictionaries)
  10:  )]

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }This could be used to indicate a location for a Style for a control. More often than not this is created as I have just shown. If you do not specify an external Dll to look in, the next place that is examined is Themesgeneric.xaml, so this is where you should put your default Style/Template for your custom control.

So typically you would create a generic.xaml file that held the default control Style/Template.

For the attached demo project my generic.xaml simply contains a bunch of merged resource dictionary objects as follows:

   1:  <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   3:  
   4:      <!-- Merge in all the available themes -->
   5:      <ResourceDictionary.MergedDictionaries>
   6:          <ResourceDictionary
   7:              Source="/CustomControls;component/Themes/Default.xaml" />
   8:          <ResourceDictionary
   9:              Source="/CustomControls;component/Themes/Blue.xaml" />
  10:          <ResourceDictionary
  11:              Source="/CustomControls;component/Themes/Red.xaml" />
  12:      </ResourceDictionary.MergedDictionaries>
  13:  
  14:  
  15:  
  16:  </ResourceDictionary>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }If we study one of these, a little more closely, say the “Blue” one, we can see that is also uses a ComponentResourceKey markup extension.

   1:  <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   3:      xmlns:local="clr-namespace:CustomControls">
   4:  
   5:  <Style x:Key="{ComponentResourceKey {x:Type local:DemoControl}, Blue }"
   6:         TargetType="{x:Type local:DemoControl}">
   7:      <Setter Property="Background" Value="Blue"/>
   8:      <Setter Property="Margin" Value="10"/>
   9:      <Setter Property="Template">
  10:          <Setter.Value>
  11:              <ControlTemplate TargetType="{x:Type local:DemoControl}" >
  12:                  <Border Background="{TemplateBinding Background}"
  13:                          CornerRadius="5" BorderBrush="Cyan"
  14:                          BorderThickness="2">
  15:                      <StackPanel Orientation="Vertical"
  16:                                  Margin="{TemplateBinding Margin}">
  17:                          <Button x:Name="btnSayHello"
  18:                                  Margin="{TemplateBinding Margin}"
  19:                                  Background="LightBlue"
  20:                                  Foreground="Black"
  21:                                  Command="{x:Static 
  22:                                  local:DemoCommands.SayHello}"
  23:                                  Content="Say Hello" Height="Auto"
  24:                                  Width="Auto" />
  25:                          <ComboBox x:Name="PART_DropDown"
  26:                                    Margin="{TemplateBinding Margin}"
  27:                                    Background="LightBlue"
  28:                                    Foreground="Black">
  29:                              <ComboBoxItem Content="Blue"/>
  30:                              <ComboBoxItem Content="Red"/>
  31:                          </ComboBox>
  32:                      </StackPanel>
  33:                      <Border.LayoutTransform>
  34:                          <ScaleTransform CenterX="0.5"
  35:                                          CenterY="0.5"
  36:                                          ScaleX="3.0"
  37:                                          ScaleY="3.0"/>
  38:                      </Border.LayoutTransform>
  39:                  </Border>
  40:              </ControlTemplate>
  41:          </Setter.Value>
  42:      </Setter>
  43:  </Style>
  44:  
  45:  
  46:  </ResourceDictionary>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }So lets get to the bottom of that. What does that do for us. Well quite simply it allows us another way to select a resource, by using a Type/Id to lookup the resource. Here is an example

   1:              Style style = (Style)TryFindResource(
   2:                  new ComponentResourceKey(
   3:                      typeof(DemoControl),
   4:                      styleToUseName));
   5:  
   6:              if (style != null)
   7:                  this.Style = style;

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

The working app simply allows users to toggle between 3 different Styles for the lookless control. You can download it and play with using the demo project, theming.zip

Enjoy.

Advertisements
C#

Snippet Designer For VS2008…Hooray!!!!!

A great tool has just come to my attention, hooray. Snippet Designer for VS2008 is available on codeplex. Thank goodness, at last. You can get it using the following link

http://www.codeplex.com/SnippetDesigner/Release/ProjectReleases.aspx?ReleaseId=17407

 

And here is sneaky peak at what you get

snip1

snip2

snip3

Enjoy it.

C#, WPF

Crack .NET

If you use WPF you have probably heard of Mole and Snoop, which are great tools, well today a member of team Mole, Mr Josh Smith, released into the wild another great tool, that like Mole allowed you to inspect objects in the managed heap. It also allowed you to alter properties like Snoop, but it also allows you to use .NETs dynamic support using IronPython scripts which can be dynamically injected at run time.

You can read more about this crazy tool over at Josh Smiths blog, using the Crack .NET homepage where you will find a full run down of what this does by the master himself. Don’t take my word for it, go check it out for yourself.

 

Well done Josh, much respect, don’t know how you do it.

 

PS : This also works for Winforms as well as WPF

WPF

.NET 3.5 SP1 StringFormat Binding Property Weirdness (A Rant)

There are some great things in the .NET 3.5 SP1 for sure, one of them is very useful which is the new StringFormat Binding property, which means that you do not need to write ValueConverters any more to create a formatted value for the Binding. I really like this little addition to WPF.

 

The only thing is that I had a need to use it the other day within a ToolTip and it plain did not want to know. Consider the following XAML where I am using 3 separate TextBoxes with custom Bindings.

 

  1. The 1st one uses the StringFormat Binding property to create a Binding on the TextBox.Text property. This is all cool and works as expected.
  2. The 2nd one uses a custom ToolTip for a TextBox just to prove we can do it. This is all cool and works as expected.
  3. The 2nd one uses a custom ToolTip for a TextBox, using the same Binding we used in Number 1, but we instead use the ToolTip property instead of Text. And this doesn’t use the string in the StringFormat property at all. It does use some value, but only the raw value, it seems to abandon the rest of the Format String. Very strange.

 

Now I have no answer to this apart from to say you may have to resort to ValueConverters if you want a formatted TooTip based on a bound value. I just thought this little issue may be interesting to someone reading this post. Basically it is a little rant that I decided to document.

Here is the code and screen shot to back up my points.

   1:  <Window x:Class="StringFormatBinding.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="StringFormat Binding Weirdness" Height="300" Width="300" 
   5:      SizeToContent="WidthAndHeight"
   6:      WindowStartupLocation="CenterScreen">
   7:      <StackPanel Orientation="Vertical">
   8:          
   9:          <!-- This binding using StringFormat works just fine-->
  10:          <StackPanel Orientation="Horizontal" Margin="5" 
  11:                      Background="WhiteSmoke">
  12:              <Label Content="This Custom Binding Works Fine"/>
  13:              <TextBox x:Name="txt1" Width="180" Height="25"
  14:                   Text="{Binding Path=Width, 
  15:                          ElementName=txt1, Mode=Default,
  16:                          StringFormat='Element is {0} Wide'}"/>
  17:          </StackPanel>
  18:   
  19:          <!-- Are here is a custom tooltip for a TextBox-->
  20:          <StackPanel Orientation="Horizontal" Margin="5" 
  21:                      Background="WhiteSmoke">
  22:              <Label Content="This Custom ToolTip is ok"/>
  23:              <TextBox x:Name="txt2" Width="180" Height="25"
  24:                   ToolTip="CUSTOM TOOL TIP"/>
  25:          </StackPanel>
  26:   
  27:          <!-- So why doesnt the  binding using StringFormat 
  28:               work for ToolTip -->
  29:          <StackPanel Orientation="Horizontal" Margin="5" 
  30:                      Background="WhiteSmoke">
  31:              <Label Content="This Custom ToolTip is ok"/>
  32:              <TextBox x:Name="txt3" Width="180" Height="25"
  33:                   ToolTip="{Binding Path=Width, 
  34:                          ElementName=txt3, Mode=Default,
  35:                          StringFormat='Tooltip : Element is {0} Wide'}"/>
  36:          </StackPanel>
  37:   
  38:      </StackPanel>
  39:  </Window>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is a screen shot of what I am talking about

The top TextBox works, as does the 2nd

Works

But this little blighter, just will not have it. Grrrr

DontWork

I am assuming this is some issue with WPF that will get resolved within a further release.

WPF

WPF Screen Saver

I had a little bit of hands on my time today, so I thought about creating a WPF screen saver. A couple of hours later and I had a screen saver that does the following

  • Allows user to pick image folders
  • randomly picks a working set of images from all available images, and displays one of these every n-seconds on a 3d Cube.

 

Here is a screen shot of the finished article

saver

 

This uses a nice WPF screen saver template which is available at http://scorbs.com/2006/12/21/wpf-screen-saver-template

I recommend having a go at creating your own WPF screen saver it really is quite fun. If you want to know more about this one that I have made, you can read about it over at the codeproject.com link, which is http://www.codeproject.com/KB/WPF/WPFScreenSaver.aspx

Enjoy

WPF

Notes ListBox

A while ago one of my collegues from WPF Disciples showed me a video about a WPF app that Billy Hollis had put together.

This app was written in VB .NETand had a very nice notes ListBox. There was supposed to be some source code
published somewhere, but I couldn’t find it. As such I tried the task myself and am pretty pleased with the results. This article respresents the fruits of my investigation/trials.

It is a re-usable Notes ListBox that can be applied to your own project with not to much bother (I hope).
itrunning.png

If you want to find out more about it you can read the full article over at www.codeproject.com, using the following url:

http://www.codeproject.com/KB/WPF/StickyNotes.aspx

The nice thing about this control is that it is hosted in the AdornerLayer, so doesn’t impact your current Layout UI Elements.