Archives

Archives / 2009 / December
  • Centering Text on a WPF Shape

    In a WPF application I am building right now, I had a need to create different sets of shapes and put some text within those shapes. The various shapes I needed were things like rectangles, circles, ellipses and triangles. WPF can create these shapes; however, shapes in WPF are not containers, so you cannot add any text inside of them. This article will show you how to put text into each of these shapes in a couple of different ways.

    Using a Border Control

    If you wish to put text into an Ellipse, you can simulate this using a Border control and play with the Width, Padding and CornerRadius properties of this control. Below is some sample code that draws text like that shown in Figure 1.

    <Border CornerRadius="50"
            Width="60"
            Margin="10"
            Padding="4"
            Background="Aquamarine"
            BorderBrush="Black"
            BorderThickness="1">
       <TextBlock HorizontalAlignment="Center">Test</TextBlock>
    </Border>

    Figure 1 
    Figure 1: Using a Border control to simulate an ellipse.

    You can set the width and height of the Border control to create a circle as well. Below is some XAML that creates a circle with text in the middle as shown in Figure 2.

    <Border CornerRadius="50"
            Width="60"
            Height="60"
            Margin="10"
            Padding="0,20,0,0"
            Background="Aquamarine"
            BorderBrush="Black"
            BorderThickness="1">
      <TextBlock HorizontalAlignment="Center">Test
      </TextBlock>
    </Border>

     

    Figure 2: Using a Border control to simulate a circle.

    Using a VisualBrush

    If you wish to use a Triangle and put some text within that triangle, you will need to use a Polygon control to create that triangle. For example, here is code to create a triangle without any words in it.

    <Polygon Points="25,0 5,30 45,30"
             Fill="LightBlue"
             Stroke="Black"
             StrokeThickness="2" />

    To add words, like that shown in Figure 3, you need to use this Polygon control as the background of a TextBlock control. The XAML shown below is what is used to create Figure 3.

    <TextBlock Text="Test"
               Width="50"
               Height="50"
               Padding="13,28,0,0">
       <TextBlock.Background>
         <VisualBrush>
           <VisualBrush.Visual>
              <Polygon Points="25,0 5,30 45,30"
                       Fill="LightBlue"
                       Stroke="Black"
                       StrokeThickness="2" />
           </VisualBrush.Visual>
         </VisualBrush>
       </TextBlock.Background>
    </TextBlock>

    There are two keys to this XAML. First, you use the VisualBrush object of the Background of the TextBlock object to give you a place to put the Polygon control. Secondly, depending on the Text that you fill into the TextBlock control, and the width and height of the TextBlock control, you will need to play with the Padding property to align the text to the correct location. Take the above XAML and put it in a new window and then adjust the height, width and text values. You will see that you need to adjust the Padding property to make the text fall into the correct location.
     

    Figure 3: Using a Visual Brush.

    Here is another example of a Visual Brush, this time using an Ellipse control. The results of this XAML can be seen in Figure 4.

    <TextBlock Text="Test"
               Height="40"
               Width="40"
               Padding="8,10,0,0">
      <TextBlock.Background>
        <VisualBrush>
          <VisualBrush.Visual>
             <Ellipse Height="20"
                      Width="20"
                      Fill="LightBlue" />
          </VisualBrush.Visual>
        </VisualBrush>
      </TextBlock.Background>
    </TextBlock>

    Notice the Padding is different from the Padding used when using the Polygon control. You will need to make minor adjustments based on the shape that you are using and the height and width of the control, and the text you are putting into the TextBlock.

     
    Figure 4: Using an Ellipse as the background for a TextBlock

    Summary

    This article presented a couple of different methods of placing text in the middle of shape objects in WPF. You do have to do some tweaking of properties to get the text to appear in the middle of the shape, but for many uses, these techniques work well. In my next blog post, I will show you how to use a Canvas, a Shape, a TextBlock and a multi-binding value converter to center the text automatically.

     

    NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF Text and Shapes" from the drop-down.

    Good Luck With Your Coding,
    Paul Sheriff

    ** SPECIAL OFFER FOR MY BLOG READERS **
    Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

    Read more...

  • Get RGB Values from WPF Color

    Recently, I have been setting up a lot of WPF styles for an application I am working on. During this project, I needed to set a lot of colors. Instead of hard-coding colors, I use resources. For example, I am using the <DropShadowEffect> on many of my controls. This <DropShadowEffect> element has a “Color” attribute that you can set as shown in the code below:

    <Border.Effect>
      <DropShadowEffect
           BlurRadius="10"
           Color="Gray" />
    </Border.Effect>

    Instead of hard-coding the color “Gray”, I would like to use a resource. I can’t just create a string resource and put that into the Color attribute. You must use a <Color> resource. Thus, I need to create a Color resource that looks like the following:

    <Color x:Key="pdsaDropShadowEffectColor"
           R="128"
           B="128"
           G="128" />

    Well, I don’t know about you, but I do not remember all of the (R)ed, (G)reen, and (B)lue color numbers off the top of my head. Thus, I needed a little utility that would help me get these numbers for the color I was looking for. In Figure 1 you can see a utility I wrote to help me do this.
     
    Color to RGB

    Figure 1: Utility to display all the WPF colors and retrieve the RGB values

    Loading a List Box with WPF Colors

    The first thing this utility needs to do is to load all the WPF colors into a list box. You can get the complete list of Colors using an ObjectDataProvider object, however setting up the ObjectDataProvider can be a little tricky. One thing that helps me immensely – and I hope will work for you -- is to write a little code that will return a collection of items. Once I have that, I usually find it is very easy to convert that code to use an ObjectDataProvider.

    To retrieve the WPF Colors is also a little tricky, as the Colors class contains individual static/Shared properties, one property to represent each color. So you need to use reflection to get the collection of properties. Each property of the Colors class is a Color object. So once you have each Color object, you then need all of its properties so you can retrieve the  Name property. Once you have the Name property you can bind that property to the list box shown in Figure 1. Below is some sample code you might write to retrieve and print out each individual color name from the Colors class. NOTE: This code is not used in this project, it is just here as an example of how to use reflection to get the list of properties of a Color object.

    C#
    using System.Reflection;
    using System.Diagnostics;

    private void GetColorEnumsAsString()
    {
      PropertyInfo[] clrs = Type.GetType(
        "System.Windows.Media.Colors, PresentationCore,
           Version=3.0.0.0, Culture=neutral,
           PublicKeyToken=31bf3856ad364e35").GetProperties();

      foreach (PropertyInfo item in clrs)
      {
        Debug.WriteLine(item.Name);
      }
    }

    Visual Basic

    Imports System.Reflection
    Imports System.Diagnostics

    Private Sub GetColorEnumsAsString()
      Dim clrs As PropertyInfo() = Type.GetType( _
        "System.Windows.Media.Colors, PresentationCore,
         Version=3.0.0.0, Culture=neutral,
         PublicKeyToken=31bf3856ad364e35").GetProperties()

      For Each item As PropertyInfo In clrs
        Debug.WriteLine(item.Name)
      Next
    End Sub

    In the above code, you first use the  GetType method of the Type class. You pass to this method the fully qualified class name including the namespace and the assembly information. For any class that is not in the System assembly, you need to use the full assembly qualification. Once you retrieve the Type, you call the GetProperites method on type to get a collection of PropertyInfo objects. It is in the PropertyInfo object that you can get the Name property. Since the name of the property is the name of the color, you bind the Name property to the list box.

    Using the ObjectDataProvider

    From the above code you can then figure out how to lay out your ObjectDataProvider, or in this case, two ObjectDataProvider objects. As you can see in the above code, you need to perform two operations to get the final collection. First, you call the GetType method, then you call GetProperties method. This means you need to use two ObjectDataProviders. The first ObjectDataProvider you create looks like the following:

    <ObjectDataProvider MethodName="GetType"
                        ObjectType="{x:Type sys:Type}"
                        x:Key="odpColors">
      <ObjectDataProvider.MethodParameters>
        <sys:String>
             System.Windows.Media.Colors, PresentationCore,
             Version=3.0.0.0, Culture=neutral,
             PublicKeyToken=31bf3856ad364e35
        </sys:String>
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

    In the above ObjectDataProvider you are creating an instance of the Type object located in the “sys” namespace. You need to add the “sys” namespace to your Window object. You do this by adding the following XAML within your Window declaration:

    <Window x:Class="PDSAConvertColorToRGB.winMain"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:sys="clr-namespace:System;assembly=mscorlib"
      ...
    The ObjectDataProvider has the ability to pass a parameter to the object you instantiate through the <ObjectDataProvider.MethodParameters>. The parameter in the MethodParameters is the exact string used in the code shown earlier. The next ObjectDataProvider uses the first ObjectDataProvider as its source of data and then calls the GetProperties method on this souce.
    <ObjectDataProvider ObjectInstance="{StaticResource odpColors}"
                        MethodName="GetProperties"
                        x:Key="odpColorNames">
    </ObjectDataProvider>

    For this second ObjectDataProvider the data is the instance of the data of the first ObjectDataProvider. You call the GetProperties method on the data type in the first ObjectDataProvider to give you Color object collection to which you can bind to the ListBox control on the Window.

    Defining the ListBox

    Once you have the collection of Color objects you now bind them to the list box control. For this list box, you want to not only have the name of the color displayed, but also a display of the actual color. To accomplish this you use a DataTemplate and layout the display of each list item. Each list item will use a label for the name of the color, and will set the Fill attribute of a Rectangle control to display the actual color. A thin black border will be used around the Rectangle control to clearly define the color swatch.

    <ListBox Name="lstColors"
             Height="200"
             TextSearch.TextPath="Name"
             IsTextSearchEnabled="True"
             ItemsSource="{Binding
                   Source={StaticResource odpColorNames}}"
             SelectedValuePath="Name">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Horizontal">
            <Label Width="140"
                   Margin="4"
                   Content="{Binding Path=Name}" />
            <Border BorderBrush="Black"
                    BorderThickness="1"
                    Margin="4">
              <Rectangle Width="140"
                         Fill="{Binding Path=Name}" />
            </Border>
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>

    The final piece of the list box control is the setting of the SelectedValuePath. Setting this value to Name will allow you to retrieve the name of the Color object and use that object to extract the Red, Green and Blue color values.

    Getting the Color Object

    After you select a specific color from the list box, you click on the Get Color button on the window. This button will fire its Click event procedure shown in the code below.

    C#
    private void btnGetColor_Click(object sender, RoutedEventArgs e)
    {
      Color clr = new Color();

      clr = (Color)ColorConverter.ConvertFromString(
        lstColors.SelectedValue.ToString());

      lblRed.Content = clr.R.ToString();
      lblGreen.Content = clr.G.ToString();
      lblBlue.Content = clr.B.ToString();
    }

    Visual Basic
    Private Sub btnGetColor_Click(ByVal sender As System.Object, _
     ByVal e As System.Windows.RoutedEventArgs)
      Dim clr As New Color()

      clr = DirectCast(ColorConverter.ConvertFromString( _
                       lstColors.SelectedValue.ToString()), Color)

      lblRed.Content = clr.R.ToString()
      lblGreen.Content = clr.G.ToString()
      lblBlue.Content = clr.B.ToString()
    End Sub

    You first retrieve the name of the color from the SelectedValue property of the list box. Then you use the ColorConverter class to convert the name of the color to an actual Color object. Once you have the Color object, you can now extract the R, G and B properties of the color and display those values in each of the appropriate labels.

    Summary

    With just a tiny little bit of code you are able to extract the R, G and B properties from a Color object. These values can then be used to create <Color> resources in WPF or Silverlight. This handy utility is also a good tutorial on data binding and how to work with reflection. I hope this sample helped you.

    NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF: Color To RGB" from the drop-down.

    Good Luck With Your Coding,
    Paul Sheriff

    ** SPECIAL OFFER FOR MY BLOG READERS **
    Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

    Read more...

  • Load Resource Dictionaries at Runtime in WPF

    One of the really cool features of WPF is the ability to style controls. Styles in WPF can be grouped together into Resource Dictionaries. These resource dictionaries can then be loaded dynamically at runtime with just a small amount of Visual Basic or C# code. In Figure 1 you see an example WPF form that allows you to type in a XAML file name that contains a resource dictionary. The styles within this resource dictionary are applied to a Grid control. In this case, it is just a simple gradient fill, but the same concept will apply even if you had styles for all controls in your application.

    Load Resource 
    Figure 1: Loading resources on the fly allows you to change styles at runtime

    The Resource Dictionaries

    A resource dictionary is nothing more than a collection of styles grouped together under a <ResourceDictionary> xaml tag. Each style can be keyed, or have just a TargetType attribute. The samples in this article are very simple and just have one Style that applies to a Grid control. The style will set the Background property of a Grid to a LinearGradientBrush of a certain set of colors. Below is the Green.xaml file.

    <ResourceDictionary
     xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <Style TargetType="Grid"
             x:Key="DynamicGridBrush">
        <Setter Property="Background">
          <Setter.Value>
            <LinearGradientBrush StartPoint="0,0"
                                 EndPoint="0,1">
              <GradientStop Offset="0"
                            Color="LightBlue" />
              <GradientStop Offset="0.65"
                            Color="LightGreen" />
              <GradientStop Offset="1"
                            Color="White" />
            </LinearGradientBrush>
          </Setter.Value>     
        </Setter>
      </Style>
    </ResourceDictionary>

    Next, is the Blue.xaml resource dictionary.

    <ResourceDictionary
     xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <Style TargetType="Grid"
             x:Key="DynamicGridBrush">
        <Setter Property="Background">
          <Setter.Value>
            <LinearGradientBrush StartPoint="0,0"
                                 EndPoint="0,1">
              <GradientStop Offset="0"
                            Color="Blue" />
              <GradientStop Offset="0.65"
                            Color="LightBlue" />
              <GradientStop Offset="1"
                            Color="AliceBlue" />
            </LinearGradientBrush>
          </Setter.Value>     
        </Setter>
      </Style>
    </ResourceDictionary>

    Now that you have the resource dictionaries defined you will now want to switch between them dynamically at runtime.

    Setting the Style

    In the form shown in Figure 1 you see a grid with a linear gradient. This is accomplished using the following code:

    <Grid Style="{DynamicResource DynamicGridBrush}"
          Height="180"
          Width="250" />

    Notice the use of the {DynamicResource DynamicGridBrush}. This code allows your XAML code to compile even if it can’t find the “DynamicGridBrush” at compile-time. The DynamicResource keyword says to look for this resource, but if you can’t find that resource, then just do not set the Style at this time. If after loading a new resource dictionary the style becomes available, then go ahead and apply the style.

    Loading a Resource

    To load a XAML file from disk, you will first need to open that file as a file stream. Next you can use the XamlReader class from the System.Windows.Markup namespace to load that file stream and cast it into a ResourceDictionary. You can then clear any old dictionaries that were previously loaded. Next you add to the MergedDictionaries collection of the current Window’s Resources property the dictionary you just loaded from disk.

    C#
    private void DynamicLoadStyles()
    {
      string fileName;

      fileName = Environment.CurrentDirectory() +
                   @"\Dictionaries\" + txtXamlName.Text;

      if (File.Exists(fileName))
      {
        using (FileStream fs = new FileStream(fileName, FileMode.Open))
        {
          // Read in ResourceDictionary File
          ResourceDictionary dic =
             (ResourceDictionary)XamlReader.Load(fs);
          // Clear any previous dictionaries loaded
          Resources.MergedDictionaries.Clear();
          // Add in newly loaded Resource Dictionary
          Resources.MergedDictionaries.Add(dic);
        }
      }
      else
        MessageBox.Show("File: " + txtXamlName.Text +
           " does not exist. Please re-enter the name.");
    }

    VB.NET
    Private Sub DynamicLoadStyles()
      Dim fileName As String
      Dim dic As ResourceDictionary = Nothing

      fileName = Environment.CurrentDirectory & _
        "\Dictionaries\" & txtXamlName.Text

      If File.Exists(fileName) Then
        Using fs As FileStream = New FileStream(fileName, _
             FileMode.Open)
          ' Read in ResourceDictionary File
          dic = CType(XamlReader.Load(fs), ResourceDictionary)
          ' Clear any previous dictionaries loaded
          Resources.MergedDictionaries.Clear()
          ' Add in newly loaded Resource Dictionary
          Resources.MergedDictionaries.Add(dic)
        End Using
      Else
        MessageBox.Show("File: " + fileName + _
                        " does not exist. Please re-enter.")
      End If
    End Sub

    To ensure that you can access the Green.xaml and the Blue.xaml files you need to add each of them to your Visual Studio solution in a folder called \Dictionaries. Next you need to click on each of these files in turn and bring up the Properties Window. Change the Build Action to “Content” and the Copy to Output Directory to “Copy Always”. This ensures that the Environment.CurrentDirectory plus the directory plus the file name will find the appropriate file at runtime.

    Summary

    Having the ability to load resource dictionaries at runtime can aid you a lot in changing the look and feel of a WPF application. You can have a set of styles that a user is allowed to choose from. You could then store their choice in a user table, and load that value when the application starts up. This allows you to customize each user’s experience with your application. You can control color, font size and many other attributes about a user’s experience with your user interface.

    NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF: Dynamically Load Resources" from the drop-down.

    Good Luck With Your Coding,
    Paul Sheriff

    ** SPECIAL OFFER FOR MY BLOG READERS **
    Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

    Read more...