Contents tagged with WPF

  • WPF for the Visual Basic Programmer - Part 2 (Pluralsight)

    Hi All,

    I have just published a new course on www.pluralsight.com. Check it out! 

    Visual Basic Programmers are still in demand. Upgrade your skills by learning XAML and WPF. This course is specifically designed for the Visual Basic programmer and will get you up to speed quickly with WPF. In Part 2 you will learn the correct way to design WPF windows, how to use styles and all the most commonly used controls for business applications. You will gain a good knowledge of data binding and how to use the various list controls in WPF.

    http://pluralsight.com/training/Courses/TableOfContents/wpf-vb-programmer-pt2

    Enjoy!

    Paul

     

    Read more...

  • WPF for the Visual Basic Programmer (Pluralsight)

    Hello All,

    I have just published a new course on www.pluralsight.com! Check it out!

    WPF For the Visual Basic Programmer

    Visual Basic Programmers are still in demand. Upgrade your skills by learning XAML and WPF. This course is specifically designed for the Visual Basic programmer and will get you up to speed quickly with WPF. In Part 1 you will learn the correct way to design WPF windows, how to use styles and all the most commonly used controls for business applications. You will gain a good knowledge of data binding and how to use the various list controls in WPF.

    http://pluralsight.com/training/Courses/TableOfContents/wpf-vb-programmer-pt1

     

    Read more...

  • Two more courses on Pluralsight

    Hello All,

    I am pleased to announce that I have released two more courses on Pluralsight.com.

    How to Start and Run a Consulting Business

    Have you ever wanted to break out of the corporate life and be your own boss? Many people have this dream. In this course you will learn what it takes to start and be successful in your own consulting business. Throughout this video you will learn how to determine what your company should look like, how to manage your money wisely, how to get and keep clients, how best to survive the ups and downs of the economy and how to create an exit strategy. Your instructor has been running his own successful business for over 22 years and shares his valuable insight so you too can be a success!

    Module 1: Hanging Your Shingle
    Module 2: Money Matters
    Module 3: How to Get and Keep Clients (or Marketing 101)
    Module 4: How To Survive the Ups and Downs
    Module 5: Creating an Exit Strategy

    http://pluralsight.com/training/Courses/TableOfContents/start-run-consulting-business


    WPF for the Business Programmer
    There are so many cool things you can do with WPF that would be impossible or very difficult with Windows Forms. In this video you will see some creative ways to use the WPF List Box. Some screens that you can use right away in your business applications will be presented. You will learn some unique ways to put together user controls to create unique shapes. Finally you will create a shell application to make your WPF application look like Windows 8. This course goes way beyond the basics of WPF to give you some unique insights on how to create business applications.

    Module 1: The Flexible WPF List Box
    Module 2: More Fun with the WPF List Box
    Module 3: Develop Business Application Screens in WPF
    Module 4: Create WPF Shapes with Text
    Module 5: Creating a Windows 8 Shell in WPF

    http://pluralsight.com/training/Courses/TableOfContents/wpf-business-programmer


    And of course, my first course is:

    The Many Approaches to XML Processing in .NET Applications

    Did you know that you can use XML instead of a SQL database for applications? Did you know there is LINQ to XML that allows you to process XML documents quickly and efficiently? If the answer is no to either of these two questions, then you need to watch this course. XML files are very common in today's programming world. You will most likely need to read files, write files and query XML within your applications. .NET provides a rich set of XML processing classes that you can use to perform all these functions. C# allows you to easily create, read and query XML files using XPath, LINQ to XML and other methods. In this course you will learn to read and write XML files using the various .NET classes. You will see some real-world uses of XML files in applications.

    Module 1: What is this XML Thing Anyway?
    Module 2: A Myriad of Methods to Read XML
    Module 3: 16 Ways to Write XML Documents
    Module 4: Real World Uses of XML
    Module 5: LINQ to XML Step-by-Step
    Module 6: Use XML: An Alternative to SQL


    http://pluralsight.com/training/Courses/TableOfContents/xml-processing-approaches-dotnet-applications

    Go to www.pluralsight.com and sign up today and start learning! 

    Read more...

  • Create a Login Window in WPF (2013)

     One of my most widely-read blog posts had to do with creating a Login Windows in WPF that I wrote several years ago. I thought I would revisit this login screen with an updated version in Visual Studio 2012 and with an updated look and feel.

    Most business applications require some sort of security system. You can always use Windows Authentication to authenticate a user, but sometimes you might want your own authentication scheme. When you do, you will need to create a login screen for your user to enter her login id and password. This article will explore creating a login window in WPF.

    The UI for your login screen can contain any images and controls that you would like. In Figure 1 you can see a sample login screen that has an image of a key, a large title label across the top, two labels, a text box, a password box and two button controls. You can also make the border of the window rounded so that there is no close, minimize or maximize button and no title bar.

    WPF Login Screen

    Figure 1: A Login Screen

    Of course this is just one version of a login screen but let’s take a look at how this is put together.

    Creating the Window

    To start, you need to create a window with no border and can be made into any shape you want. To do this you will set a few different attributes. The WindowStyle attribute normally allows you to set a single border, three-D border, or a Tool Window border. Setting this attribute to None will eliminate the border. The ShowInTaskbar attribute is optional, but if you are building a login screen you probably won’t want this window to show up in the Task Bar as it is going to be modal style form. The next two attributes, AllowsTransparency and Background work together. You must set AllowsTransparency to True to allow the Background to be set to Transparent. If you do not set these two attributes, then your border will still show up. Below is the xaml for this window.

    <Window ...
       WindowStartupLocation="CenterScreen"
       AllowsTransparency="True"
       ShowInTaskBar=False
       Background="Transparent"
       WindowStyle="None"
       SizeToContent="WidthAndHeight"
       FocusManager.FocusedElement=
              "{Binding ElementName=txtUserName}">

       ...
       ...

    </Window>

    There are three additional attributes that are set on this window. The WindowStartupLocation attribute is set to “CenterScreen”  to ensure that the login screen is displayed in the middle of the screen when it is shown. You also set the SizeToContent attribute to WidthAndHeight to just take as much room for this window as the controls need that are contained within this window. The FocusManager.FocusedElement attribute is data-bound to the textbox control next to the User Name label. This tells WPF to place the cursor in this textbox once the screen is displayed.

    The Border

    Now that you have the Window xaml defined you now can create the look for the outside border of the window. A Border control is used to form the outside of this login screen. You will set the CornerRadius attribute to “10” to give the nice rounded corners. You can set the BorderBrush to “Gray” and the BorderThickness to “3”. You also want to give this border a nice wide Margin to allow room for the DropShadowEffect that we add to the outside of this border. If you do not do this, then the drop shadow will be chopped off.

    The DropShadowEffect is created as a resource with a key of “shadowWindow” as shown below:

    <Window.Resources>
      <DropShadowEffect x:Key="shadowWindow"
                        Color="Black"
                        Opacity=".75"
                        ShadowDepth="12" />
    </Window.Resources>

    When you create the Border for this window you set the Effect property to reference this static resource.

    <Border CornerRadius="10"
            BorderBrush="#FF5F5F5F"
            BorderThickness="4"
            Background="{StaticResource backBrush}"
            Effect="{StaticResource shadowWindow}"
            Margin="24"
            Padding="24">

    Using a Grid Layout

    To place each of the login screen elements within the border, a Grid control is used with specific column and row definitions. There are three columns in this login screen. One for the image of the key, one for the labels and one for the TextBox, PasswordBox and Button controls.

    <Grid>
      <Grid.ColumnDefinitions>
        <ColumnDefinition MinWidth="80" Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
      </Grid.RowDefinitions>

       ...
       ...

    </Grid>

    Placing the Key Image

    The Key image that is in the upper left hand corner of this login screen is placed there by using an Image control. Notice the Grid.Column, Grid.Row and Grid.RowSpan attributes that are set on the StackPanel. The Grid.Row and Grid.Column specify in which row and column of the grid you wish to display the Image control. The Grid.RowSpan allows the key to float down over the next three rows of the Grid control if necessary. If you were to use a smaller or larger key image, then you would probably need to adjust this attribute accordingly. The Image control sets the source of its image to the KeyComputer.jpg file located in the /Images folder. A drop shadow effect is applied to this image control just like you did with the Border control.

    <Image Grid.Column="0"
            Grid.Row="0"
            Grid.RowSpan="3"
            Effect="{StaticResource shadowWindow}"
            VerticalAlignment="Top"
            HorizontalAlignment="Left"
            Name="imgKey"
            Width="50"
            Margin="8"
            Source="Images/KeyComputer.png" />

    The Large Title Label

    The large label across the top of the login screen is simply a TextBlock control within a Border control that has the appropriate Grid.Row, Grid.Column and Grid.ColumnSpan attributes set for placement. A FontSize of 18 is applied to make the text appear larger than the other labels on this screen. A Margin of 10 is used to give us some spacing from the border of the grid.

    <Border Grid.Column="1"
            Grid.Row="0"
            Grid.ColumnSpan="2"
            Margin="4,10,4,20"
            CornerRadius="10">
      <Border.Background>
        <LinearGradientBrush EndPoint="0.5,1"
                              StartPoint="0.5,0">
          <GradientStop Color="#FFC7C2C2"
                        Offset="0" />
          <GradientStop Color="#FF8D8787"
                        Offset="1" />
        </LinearGradientBrush>
      </Border.Background>
      <TextBlock FontSize="18"
                  Margin="10"
                  Text="Please Login To Access This Application" />
    </Border>

    The Login Controls

    The controls that gather the user name and password should be fairly familiar to you if you have been doing any WPF at all. Each control is placed into a specific row and column of the Grid control. Notice the use of the Tooltip attribute on the Login TextBox, the PasswordBox and Domain TextBox control. This gives the user an idea of what to put into each control if they hover their mouse over that control.

    <TextBlock Grid.Column="1"
                Grid.Row="1"
                Text="User Name" />
    <TextBox Grid.Column="2"
              Grid.Row="1"
              ToolTipService.ToolTip="Enter Your User Name"
              Name="txtUserName" />
    <TextBlock Grid.Column="1"
                Grid.Row="2"
                Text="Password" />
    <PasswordBox Grid.Column="2"
                  Grid.Row="2"
                  ToolTipService.ToolTip="Enter Your Password"
                  Name="txtPassword" />
    <TextBlock Grid.Column="1"
                Grid.Row="3"
                Text="Domain" />
    <TextBox Grid.Column="2"
              Grid.Row="3"
              ToolTipService.ToolTip="Enter Domain Name to Login To"
              Name="txtDomain" />

    The Buttons

    The two buttons at the bottom of the screen are placed into the last row of the Grid control and into the second column of the grid by wrapping them into a StackPanel. The StackPanel has its HorizontalAlignment attribute set to Center and its Orientation attribute to Horizontal to allow the buttons to be centered within the StackPanel and to have the buttons appear side-by-side to each other.

    <StackPanel Grid.Column="2"
                Grid.Row="4"
                Margin="4"
                HorizontalAlignment="Right"
                Orientation="Horizontal">
      <Button Name="btnCancel"
              Click="btnCancel_Click"
              IsCancel="True"
              Effect="{StaticResource shadowWindow}"
              ToolTipService.ToolTip="Cancel">
        <Image Source="Images/XBlack.png" />
      </Button>
      <Button Name="btnLogin"
              Click="btnLogin_Click"
              IsDefault="True"
              Width="75"
              Effect="{StaticResource shadowWindow}"
              ToolTipService.ToolTip="Login">
        <Image Source="Images/CheckMarkBlack.png" />
      </Button>
    </StackPanel>

    There are two special attributes that are set on these buttons. The IsCancel attribute is set to true on the Cancel button. Setting this attribute to true will fire the click event procedure on the Cancel button if the user presses the Escape key. The IsDefault attribute is set to true on the on the Login button. Setting this attribute to true will fire the click event procedure on the Login button if the user presses the Enter key.

    Writing the Code for the Login Screen

    In each of the click event procedures you will need to close the screen. In the Cancel click event procedure you will set the DialogResult property of the screen to a false value. This will inform the calling procedure that the user clicked on the Cancel button on this screen. In the Login click event procedure you will set the DialogResult property of the screen to a true value. This informs the calling procedure that the user clicked on the Login button and was authenticated. I am leaving it up to you to write the code for authenticating the user. Here is the code for the Cancel event procedure.

    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
      DialogResult = false;
    }

    And, here is the code for the Login event procedure.

    private void btnLogin_Click(object sender, RoutedEventArgs e)
    {
      // Write code here to authenticate user
      // If authenticated, then set DialogResult=true
      DialogResult = true;
    }

    Displaying the Login Screen

    At some point when your application launches, you will need to display your login screen modally. Below is the code that you would call to display the login form (named frmLogin in my sample application). This code is called from the main application form, and thus the owner of the login screen is set to “this”. You then call the ShowDialog method on the login screen to have this form displayed modally. After the user clicks on one of the two buttons you need to check to see what the DialogResult property was set to. The DialogResult property is a nullable type and thus you first need to check to see if the value has been set.

    private void DisplayLoginScreen()
    {
      winLogin win = new winLogin();

      win.Owner = this;
      win.ShowDialog();
      if (win.DialogResult.HasValue && win.DialogResult.Value)
        MessageBox.Show("User Logged In");
      else
        this.Close();
    }

    Summary

    Creating a nice looking login screen is fairly simple to do in WPF. Using the DropShadowEffect can add a nice finished look to not only your form, but images and buttons as well. Using a border-less window is a great way to give a custom look to a login screen or splash screen. The DialogResult property on WPF Windows allows you to communicate back to the calling routine what happened on the modal screen. I hope this article gave you some ideas on how to create a login screen in WPF.

    NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select "Tips & Tricks", then select "WPF Login Screen 2013" from the drop down list.

    Read more...

  • Creating a XAML Tile Control

    One of the navigation mechanisms used in Windows 8 and Windows Phone is a Tile. A tile is a large rectangle that can have words and pictures that a user can click on. You can build your own version of a Tile in your WPF or Silverlight applications using a User Control. With just a little bit of XAML and a little bit of code-behind you can create a navigation system like that shown in Figure 1.

    Read more...

  • A WPF Image/Text Button

    Some of our customers are asking us to give them a Windows 8 look and feel for their applications. This includes things like buttons, tiles, application bars, and other features. In this blog post I will describe how to create a button that looks similar to those you will find in a Windows 8 application bar.

    Read more...

  • A WPF Image Button

    Instead of a normal button with words, sometimes you want a button that is just graphical. Yes, you can put an Image control in the Content of a normal Button control, but you still have the button outline, and trying to change the style can be rather difficult. Instead I like creating a user control that simulates a button, but just accepts an image. Figure 1 shows an example of three of these custom user controls to represent minimize, maximize and close buttons for a borderless window. Notice the highlighted image button has a gray rectangle around it. You will learn how to highlight using the VisualStateManager in this blog post.

    Read more...

  • A Communication System for XAML Applications

    In any application, you want to keep the coupling between any two or more objects as loose as possible. Coupling happens when one class contains a property that is used in another class, or uses another class in one of its methods. If you have this situation, then this is called strong or tight coupling. One popular design pattern to help with keeping objects loosely coupled is called the Mediator design pattern. The basics of this pattern are very simple; avoid one object directly talking to another object, and instead use another class to mediate between the two. As with most of my blog posts, the purpose is to introduce you to a simple approach to using a message broker, not all of the fine details.

    IPDSAMessageBroker Interface

    As with most implementations of a design pattern, you typically start with an interface or an abstract base class. In this particular instance, an Interface will work just fine. The interface for our Message Broker class just contains a single method “SendMessage” and one event “MessageReceived”.

    public delegate void MessageReceivedEventHandler(
     object sender, PDSAMessageBrokerEventArgs e);

    public interface IPDSAMessageBroker
    {
      void SendMessage(PDSAMessageBrokerMessage msg);

      event MessageReceivedEventHandler MessageReceived;
    }

    PDSAMessageBrokerMessage Class

    As you can see in the interface, the SendMessage method requires a type of PDSAMessageBrokerMessage to be passed to it. This class simply has a MessageName which is a ‘string’ type and a MessageBody property which is of the type ‘object’ so you can pass whatever you want in the body. You might pass a string in the body, or a complete Customer object. The MessageName property will help the receiver of the message know what is in the MessageBody property.

    public class PDSAMessageBrokerMessage
    {
      public PDSAMessageBrokerMessage()
      {
      }

      public PDSAMessageBrokerMessage(string name, object body)
      {
        MessageName = name;
        MessageBody = body;
      }

      public string MessageName { get; set; }

      public object MessageBody { get; set; }
    }

    PDSAMessageBrokerEventArgs Class

    As our message broker class will be raising an event that others can respond to, it is a good idea to create your own event argument class. This class will inherit from the System.EventArgs class and add a couple of additional properties. The properties are the MessageName and Message. The MessageName property is simply a string value. The Message property is a type of a PDSAMessageBrokerMessage class.

    public class PDSAMessageBrokerEventArgs : EventArgs
    {
      public PDSAMessageBrokerEventArgs()
      {
      }

      public PDSAMessageBrokerEventArgs(string name,
        PDSAMessageBrokerMessage msg)
      {
        MessageName = name;
        Message = msg;
      }

      public string MessageName { get; set; }

      public PDSAMessageBrokerMessage Message { get; set; }
    }

    PDSAMessageBroker Class

    Now that you have an interface class and a class to pass a message through an event, it is time to create your actual PDSAMessageBroker class. This class implements the SendMessage method and will also create the event handler for the delegate created in your Interface.

    public class PDSAMessageBroker : IPDSAMessageBroker
    {
      public void SendMessage(PDSAMessageBrokerMessage msg)
      {
        PDSAMessageBrokerEventArgs args;

        args = new PDSAMessageBrokerEventArgs(
          msg.MessageName, msg);

        RaiseMessageReceived(args);
      }

      public event MessageReceivedEventHandler MessageReceived;

      protected void RaiseMessageReceived(
        PDSAMessageBrokerEventArgs e)
      {
        if (null != MessageReceived)
          MessageReceived(this, e);
      }
    }

    The SendMessage method will take a PDSAMessageBrokerMessage object as an argument. It then creates an instance of a PDSAMessageBrokerEventArgs class, passing to the constructor two items: the MessageName from the PDSAMessageBrokerMessage object and also the object itself. It may seem a little redundant to pass in the message name when that same message name is part of the message, but it does make consuming the event and checking for the message name a little cleaner – as you will see in the next section.

    Create a Global Message Broker

    In your WPF application, create an instance of this message broker class in the App class located in the App.xaml file. Create a public property in the App class and create a new instance of that class in the OnStartUp event procedure as shown in the following code:

    public partial class App : Application
    {
      public PDSAMessageBroker MessageBroker { get; set; }

      protected override void OnStartup(StartupEventArgs e)
      {
        base.OnStartup(e);

        MessageBroker = new PDSAMessageBroker();
      }
    }

    Sending and Receiving Messages

    Let’s assume you have a user control that you load into a control on your main window and you want to send a message from that user control to the main window. You might have the main window display a message box, or put a string into a status bar as shown in Figure 1.

    Figure 1: The main window can receive and send messages

    Figure 1: The main window can receive and send messages

    The first thing you do in the main window is to hook up an event procedure to the MessageReceived event of the global message broker. This is done in the constructor of the main window:

    public MainWindow()
    {
      InitializeComponent();

      (Application.Current as App).MessageBroker.
         MessageReceived += new MessageReceivedEventHandler(
           MessageBroker_MessageReceived);
    }

    One piece of code you might not be familiar with is accessing a property defined in the App class of your XAML application. Within the App.Xaml file is a class named App that inherits from the Application object. You access the global instance of this App class by using Application.Current. You cast Application.Current to ‘App’ prior to accessing any of the public properties or methods you defined in the App class. Thus, the code (Application.Current as App).MessageBroker, allows you to get at the MessageBroker property defined in the App class.

    In the MessageReceived event procedure in the main window (shown below) you can now check to see if the MessageName property of the PDSAMessageBrokerEventArgs is equal to “StatusBar” and if it is, then display the message body into the status bar text block control.

    void MessageBroker_MessageReceived(object sender,
       PDSAMessageBrokerEventArgs e)
    {
      switch (e.MessageName)
      {
        case "StatusBar":
          tbStatus.Text = e.Message.MessageBody.ToString();
          break;
      }
    }

    In the Page 1 user control’s Loaded event procedure you will send the message “StatusBar” through the global message broker to any listener using the following code:

    private void UserControl_Loaded(object sender,
     RoutedEventArgs e)
    {
      // Send Status Message
      (Application.Current as App).MessageBroker.
        SendMessage(new PDSAMessageBrokerMessage("StatusBar",
          "This is Page 1"));
    }

    Since the main window is listening for the message ‘StatusBar’, it will display the value “This is Page 1” in the status bar at the bottom of the main window.

    Sending a Message to a User Control

    The previous example sent a message from the user control to the main window. You can also send messages from the main window to any listener as well. Remember that the global message broker is really just a broadcaster to anyone who has hooked into the MessageReceived event.

    In the constructor of the user control named ucPage1 you can hook into the global message broker’s MessageReceived event. You can then listen for any messages that are sent to this control by using a similar switch-case structure like that in the main window.

    public ucPage1()
    {
      InitializeComponent();

      // Hook to the Global Message Broker
      (Application.Current as App).MessageBroker.
        MessageReceived += new MessageReceivedEventHandler(
          MessageBroker_MessageReceived);
    }

    void MessageBroker_MessageReceived(object sender,
     PDSAMessageBrokerEventArgs e)
    {
      // Look for messages intended for Page 1
      switch (e.MessageName)
      {
        case "ForPage1":
          MessageBox.Show(e.Message.MessageBody.ToString());
          break;
      }
    }

    Once the ucPage1 user control has been loaded into the main window you can then send a message using the following code:

    private void btnSendToPage1_Click(object sender,
     RoutedEventArgs e)
    {
      PDSAMessageBrokerMessage arg =
        new PDSAMessageBrokerMessage();

      arg.MessageName = "ForPage1";
      arg.MessageBody = "Message For Page 1";

      // Send a message to Page 1
      (Application.Current as App).MessageBroker.SendMessage(arg);
    }

    Since the MessageName matches what is in the ucPage1 MessageReceived event procedure, ucPage1 can do anything in response to that event. It is important to note that when the message gets sent it is sent to all MessageReceived event procedures, not just the one that is looking for a message called “ForPage1”. If the user control ucPage1 is not loaded and this message is broadcast, but no other code is listening for it, then it is simply ignored.

    Remove Event Handler

    In each class where you add an event handler to the MessageReceived event you need to make sure to remove those event handlers when you are done. Failure to do so can cause a strong reference to the class and thus not allow that object to be garbage collected. In each of your user control’s make sure in the Unloaded event to remove the event handler.

    private void UserControl_Unloaded(object sender,
     RoutedEventArgs e)
    {
      if (_MessageBroker != null)
        _MessageBroker.MessageReceived -=
            _MessageBroker_MessageReceived;
    }

    Problems with Message Brokering

    As with most “global” classes or classes that hook up events to other classes, garbage collection is something you need to consider. Just the simple act of hooking up an event procedure to a global event handler creates a reference between your user control and the message broker in the App class. This means that even when your user control is removed from your UI, the class will still be in memory because of the reference to the message broker. This can cause messages to still being handled even though the UI is not being displayed. It is up to you to make sure you remove those event handlers as discussed in the previous section. If you don’t, then the garbage collector cannot release those objects.

    Instead of using events to send messages from one object to another you might consider registering your objects with a central message broker. This message broker now becomes a collection class into which you pass an object and what messages that object wishes to receive. You do end up with the same problem however. You have to un-register your objects; otherwise they still stay in memory. To alleviate this problem you can look into using the WeakReference class as a method to store your objects so they can be garbage collected if need be. Discussing Weak References is beyond the scope of this post, but you can look this up on the web.

    Summary

    In this blog post you learned how to create a simple message broker system that will allow you to send messages from one object to another without having to reference objects directly. This does reduce the coupling between objects in your application. You do need to remember to get rid of any event handlers prior to your objects going out of scope or you run the risk of having memory leaks and events being called even though you can no longer access the object that is responding to that event.

    NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select “Tips & Tricks”, then select “A Communication System for XAML Applications” from the drop down list.

    Read more...

  • A WPF Message Box you can Style

    You go to great pains to add styles, colors, gradients, and a really cool look and feel to your WPF application only to have that ruined by the standard Windows message box as shown in Figure 1.

    Figure 1: The normal Windows message box just does not look right on a styled WPF application.

    Figure 1: The normal Windows message box just does not look right on a styled WPF application.

    What would be nice is if Microsoft offered a styled message box. But, they don’t. So it is up to us to create a window that we can style and do whatever we want with it. Thus, I can up with a message box that looks like Figure 2 and Figure 3.

    Figure 2: Creating your own dialog from a Window, a Text Block and a custom button control.

    Figure 2: Creating your own dialog from a Window, a Text Block and a custom button control.

    Figure 3: You can completely change the theme of your button through the use of resource dictionaries.

    Figure 3: You can completely change the theme of your button through the use of resource dictionaries.

    The PDSAMessageBoxView XAML

    The first step in creating a custom message box is to add a new Window to your WPF project. You then need to set some styles to get a border-less window. You set the following attributes on your Window.

    WindowStyle="None"
    ShowInTaskbar="True"
    ResizeMode=”NoResize”
    AllowsTransparency="True"
    Background="Transparent"

    The WindowStyle attribute normally allows you to set a single border, three-D border, or a Tool Window border. Setting this attribute to None will eliminate the border. The ShowInTaskbar attribute is optional, but if you are doing a dialog window you probably want this window to show up in the Task Bar. Since this is a dialog window, you probably do not want to allow the user to resize this window, thus you set the ResizeMode to “NoResize”. The next two attributes, AllowsTransparency and Background work together. You must set AllowsTransparency to True to allow the Background to be set to Transparent. If you do not set these two attributes, then your border will still show up.

    The listing that follows shows the complete XAML for the PDSAMessageBoxView.xaml file. This XAML file contains a Border, a Grid, a TextBlock for the message, and a StackPanel control with four PDSAucButton controls (see my previous blog post on how to create these buttons). All of the attributes for the border, the text block and the buttons are controlled via styles in a Resource Dictionary file. Using a resource dictionary allows you to create new resource dictionaries with different colors and other attributes to style the message box in any manner you see fit.

    <Window x:Class="PDSA.WPF.PDSAMessageBoxView"
            ...
            xmlns:my="clr-namespace:PDSA.WPF"
            WindowStyle="None"
            AllowsTransparency="True"
            Background="Transparent"
            ResizeMode="NoResize"
            ShowInTaskbar="True"
            FontFamily="Segoe UI"
            WindowStartupLocation="CenterScreen"
            Height="300"
            Width="420"
            MouseLeftButtonDown="Window_MouseLeftButtonDown"
            Deactivated="Window_Deactivated" >
      <Window.Resources>
        <!-- Set style for PDSA Button -->
        <Style TargetType="my:PDSAucButton">
          <Setter Property="Effect"
           Value="{StaticResource pdsaMessageBoxButtonShadow}" />
          <Setter Property="Width"
                  Value="80" />
        </Style>
      </Window.Resources>
      <Border Style="{StaticResource pdsaMessageBoxBorder}">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>
          <TextBlock Name="tbMessage"
             Style="{StaticResource pdsaMessageBoxTextBlock}"
             Text="Message goes here..."
             TextWrapping="Wrap" />
          <StackPanel Grid.Row="1"
              Style="{StaticResource pdsaMessageBoxStackPanel}">
            <my:PDSAucButton Text="Yes"
                             x:Name="btnYes"
                             Click="btnYes_Click" />
            <my:PDSAucButton Text="No"
                             x:Name="btnNo"
                             Click="btnNo_Click" />
            <my:PDSAucButton Text="OK"
                             x:Name="btnOk"
                             Click="btnOk_Click" />
            <my:PDSAucButton Text="Cancel"
                             x:Name="btnCancel"
                             Click="btnCancel_Click" />
          </StackPanel>
        </Grid>
      </Border>
    </Window>

    Styles for PDSAMessageBox Class

    As mentioned previously, all of the styles for the PDSAMessageBoxView class are contained within a resource dictionary. There is one resource dictionary in the PDSA.WPF DLL where the PDSAMessageBoxView class is located. However, there is an additional resource dictionary in the main project with a blue theme as shown in Figure 3. The listing below is the complete resource dictionary for the gray message box theme.

    <ResourceDictionary ... >
      <!-- Background for Message Box -->
      <LinearGradientBrush x:Key="pdsaMessageBoxBackground"
                           StartPoint="0.5,0"
                           EndPoint="0.5,1">
        <GradientStop Color="DarkSlateGray"
                      Offset="0" />
        <GradientStop Color="LightGray"
                      Offset="0.70" />
        <GradientStop Color="DarkSlateGray"
                      Offset="1" />
      </LinearGradientBrush>
      <!-- Drop Shadow Effect for Message Box -->
      <DropShadowEffect Color="Black"
                        x:Key="pdsaMessageBoxDropShadow"
                        Opacity=".85"
                        ShadowDepth="16" />
      <!-- Drop Shadow for Message Box Buttons -->
      <DropShadowEffect x:Key="pdsaMessageBoxButtonShadow"
                        Color="Black"
                        ShadowDepth="8" />
      <!-- Border for Message Box -->
      <Style TargetType="Border"
             x:Key="pdsaMessageBoxBorder">
        <Setter Property="CornerRadius"
                Value="10" />
        <Setter Property="BorderBrush"
                Value="Black" />
        <Setter Property="BorderThickness"
                Value="3" />
        <Setter Property="Margin"
                Value="16" />
        <Setter Property="Effect"
            Value="{StaticResource pdsaMessageBoxDropShadow}" />
        <Setter Property="Background"
            Value="{StaticResource pdsaMessageBoxBackground}" />
      </Style>
      <!-- Buttons for Message Box -->
      <Style TargetType="Button"
             x:Key="pdsaMessageBoxButton">
        <Setter Property="Margin"
                Value="10" />
        <Setter Property="Width"
                Value="70" />
        <Setter Property="Height"
                Value="35" />
        <Setter Property="FontSize"
                Value="14" />
        <Setter Property="FontWeight"
                Value="SemiBold" />
        <Setter Property="Effect"
           Value="{StaticResource pdsaMessageBoxButtonShadow}" />
      </Style>
      <!-- Text Block for Message Box -->
      <Style TargetType="TextBlock"
             x:Key="pdsaMessageBoxTextBlock">
        <Setter Property="Foreground"
                Value="Black" />
        <Setter Property="Margin"
                Value="20,40,20,10" />
        <Setter Property="FontSize"
                Value="28" />
        <Setter Property="TextWrapping"
                Value="Wrap" />
      </Style>
      <!-- StackPanel for Message Box -->
      <Style TargetType="StackPanel"
             x:Key="pdsaMessageBoxStackPanel">
        <Setter Property="Orientation"
                Value="Horizontal" />
        <Setter Property="HorizontalAlignment"
                Value="Right" />
        <Setter Property="Margin"
                Value="8" />
      </Style>
    </ResourceDictionary>

    Feel free to modify any of the above styles to make the message box look like however you want.

    The PDSAMessageBox Class

    Just like .NET has the MessageBox class in order to display the dialog, I have created a PDSAMessageBox class with a Show method as well. I made the Show method with the same parameters as the MessageBox class in order to help you make an easier transition to the PDSAMessageBox class. However, I do not use any of the MessageBoxImage parameters, so you will need to remove any of these, or add an image to this view if you so desire. Below is the PDSAMessageBox class with the static Show methods defined.

    public class PDSAMessageBox
    {
      public static MessageBoxResult Show(string message)
      {
        return Show(message, string.Empty, MessageBoxButton.OK);
      }

      public static MessageBoxResult Show(string message,
        string caption)
      {
        return Show(message, caption, MessageBoxButton.OK);
      }

      public static MessageBoxResult Show(string message,
        string caption, MessageBoxButton buttons)
      {
        MessageBoxResult result = MessageBoxResult.None;
        PDSAMessageBoxView dialog = new PDSAMessageBoxView();

        dialog.Title = caption;
        dialog.tbMessage.Text = message;
        dialog.Buttons = buttons;
        // If just an OK button, allow the user to just
        // move away from the dialog
        if (buttons == MessageBoxButton.OK)
          dialog.Show();
        else
        {
          dialog.ShowDialog();
          result = dialog.Result;
        }

        return result;
      }
    }

    All of the Show methods end up calling just a single Show that takes care of displaying the PDSAMessageBoxView dialog. One thing I did change in my Show method is that if the user pops up a message box with just an “Ok” button, I do allow the user to navigate away from this dialog without pressing the Ok button. To me, it does not make sense to force the user to press a single button on the form. This could have side-effects however, if you have code immediately following the call to the Show method because this is no longer a modal dialog. Again, feel free to modify this code if you do not like this functionality.

    Calling the PDSAMessageBox

    Below is an example of calling the PDSAMessageBox. The Show method will return a MessageBoxResult enumeration. There was no need to change the return value from that of the normal MessageBox class. This code can be found in the sample that you download along with this blog post.

    private void btnOKOnly_Click(object sender, RoutedEventArgs e)
    {
      PDSAMessageBox.Show("This just displays an OK Button",
         "OK", MessageBoxButton.OK);
    }

    private void btnYesNo_Click(object sender, RoutedEventArgs e)
    {
      MessageBoxResult result;

      result = PDSAMessageBox.Show("Do you want to Quit?",
          "Quit?", MessageBoxButton.YesNo);

      MessageBox.Show("Result is " + result.ToString());
    }

    Summary

    Creating your own dialog box is very easy using just a little bit of XAML, some styles and a class with a few methods. Break up your styles into resource dictionaries in order to have the flexibility to modify the look and feel of your dialogs. Keep your dialog boxes and buttons in a separate DLL in order to make it easy to reuse your controls.

    NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select “Tips & Tricks”, then select “A WPF Message Box you can Style” from the drop down list.

    Read more...

  • Create your own WPF Button User Controls

    In Figure 1 you can see examples of the standard WPF Button controls. You can add a drop shadow and you can change the color, but you can’t change much else without creating a whole new control template. For example, you are unable to modify the BorderBrush or the BorderThickness properties of the Button control. Additionally you might want to use some other animation than the default, which again requires you to change the control template.

    Sometimes all you want to do is to just have some simple buttons where you can modify the border brush and the thickness and have different color options via styles. I have found that instead of working with the whole control template thing, just creating a User Control is sometimes much easier.

    Figure 1: The Normal WPF Buttons can not be styled very well

    Figure 1: The normal WPF Buttons cannot be styled very well.

    There are many ways to create custom buttons and there are advantages and disadvantages to each way. The purpose of this blog post is to present one method that is easily understood by almost any XAML programmer, and hopefully to those new to XAML as well. User controls have been around since the Visual Basic 4.0 days. Most developers understand the value of using user controls. With XAML user controls you can put these controls into a WPF User Control Library or a Silverlight Class Library and reference those DLLs from any WPF or Silverlight application. This gives you great reusability. By using resource dictionaries and appropriate use of styles you can make your user controls very customizable as shown in Figure 1. The 2nd row of buttons you see are the same button user control with just different styles applied.

    The User Control

    The XAML for your Button user control is actually very simple. You use a single Border control and a TextBlock control within that Border as shown in Listing 1.

    <Border Name="hoverBorder"
            Style="{DynamicResource hoverBorder}"
            MouseDown="hoverBorder_MouseDown"
            MouseUp="hoverBorder_MouseUp"
            MouseEnter="hoverBorder_MouseEnter"
            MouseLeave="hoverBorder_MouseLeave"
            MouseLeftButtonUp="hoverBorder_MouseLeftButtonUp">
      <TextBlock Text="{Binding Path=Text}"
                 Name="tbText"
                 Style="{DynamicResource hoverTextBlock}" />
    </Border>

    Listing 1: The XAML for a Button user control is just a Border and TextBlock

    The definition for this user control is in a DLL named PDSA.WPF. Notice that there are Style definitions for both the Border and the TextBlock. We used styles contained in a resource dictionary as opposed to setting up dependency properties for each individual attribute that we might want to set. This allows us to create a few different resource dictionaries, each with a different theme for the buttons. You can see the three different themes we created in Figure 1. The Gray Style button uses a resource dictionary that is contained in the PDSA.WPF DLL. The other two styles are in the main project and can be referenced from your App.xaml or from within the Window/User Control where you need the button.

    Changing Colors in Response to Mouse Events

    When a user moves over a button or presses a button you should give some visual feedback to that user. You can do this with animation using the Visual State Manager or Event Triggers in WPF. To keep things simple for this blog post, I simply respond to the various mouse events and change the Background property to a different color. When the mouse is pressed, I also change the Foreground color of the Text in the TextBlock control. The code for each of the mouse events is shown below.

    private void pdsaButtonBorderStyle_MouseEnter(
     object sender, MouseEventArgs e)
    {
      pdsaButtonBorderStyle.Background =
       (Brush)this.FindResource("pdsaButtonOverStyle");
    }

    private void pdsaButtonBorderStyle_MouseLeave(
     object sender, MouseEventArgs e)
    {
      pdsaButtonBorderStyle.Background =
       (Brush)this.FindResource("pdsaButtonNormalStyle");
    }

    private void pdsaButtonBorderStyle_MouseDown(
     object sender, MouseButtonEventArgs e)
    {
      // Save old Foreground Brush
      _TextBrush = tbText.Foreground;

      pdsaButtonBorderStyle.Background =
       (Brush)this.FindResource("pdsaButtonPressedStyle");
      tbText.Foreground =
       (SolidColorBrush)this.FindResource(
         "pdsaButtonTextBlockStylePressed");
    }

    private void pdsaButtonBorderStyle_MouseUp(
     object sender, MouseButtonEventArgs e)
    {
      RestoreNormal();
    }

    private void RestoreNormal()
    {
      pdsaButtonBorderStyle.Background =
       (Brush)this.FindResource("pdsaButtonNormalStyle");

      tbText.Foreground = _TextBrush;
    }

    Notice that in the code above that you use the FindResource() method instead of accessing the this.Resources[] collection. That is because you want to be able to set the resource dictionary at any level, not just on this user control. FindResource() will search upward through your UI tree looking for a resource that match the names you see that start with “pdsa”.

    You will need a Click event that you can raise when the user clicks on the button. Here is the definition for that Click event.

    private void pdsaButtonBorderStyle_MouseLeftButtonUp(
     object sender, MouseButtonEventArgs e)
    {
      RaiseClick(e);
    }

    public delegate void ClickEventHandler(object sender,
     RoutedEventArgs e);

    public event ClickEventHandler Click;

    protected void RaiseClick(RoutedEventArgs e)
    {
      if (null != Click)
        Click(this, e);

      RestoreNormal();
    }

    The Default Resource Dictionary

    Below is the definition of the resource dictionary file contained in the PDSA.WPF DLL. This dictionary can be used as the default look and feel for any button control you add to a window. I have included two additional resource dictionaries in the main project as examples of how you can change the resources to give your buttons a different look. You need to keep the x:Key names the same, but you can change any of the attributes of color or thickness that you want. You can even change from Gradient colors to a SolidColorBrush as you can see I did when you look at the different resource dictionaries.

    <ResourceDictionary xmlns="http://schemas.microsoft.com/
                               winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <!-- Hover TextBlock Style -->
      <Style TargetType="TextBlock"
             x:Key="pdsaButtonTextBlockStyle">
        <Setter Property="Foreground"
                Value="GhostWhite" />
        <Setter Property="Margin"
                Value="10" />
        <Setter Property="FontSize"
                Value="14" />
      </Style>
      <!-- Hover Brush for Pressed Hover Button Text Block -->
      <SolidColorBrush x:Key="pdsaButtonTextBlockStylePressed"
                       Color="Black" />
      <!-- Hover Border Thickness -->
      <Thickness x:Key="pdsaButtonBorderStyleThickness"
                 Bottom="0"
                 Left="0"
                 Right="0"
                 Top="0" />
      <!-- Hover Border Brush -->
      <SolidColorBrush x:Key="pdsaButtonBorderBrushStyle"
                       Color="Black" />
      <!-- Style for when hovering over button -->
      <RadialGradientBrush x:Key="pdsaButtonOverStyle">
        <GradientStop Color="#FF5F5F5F"
                      Offset="0" />
        <GradientStop Color="#FFADADAD"
                      Offset="1" />
      </RadialGradientBrush>
      <!-- Style for when button is pressed -->
      <LinearGradientBrush EndPoint="1,0.5"
                           StartPoint="0,0.5"
                           x:Key="pdsaButtonPressedStyle">
        <GradientStop Color="#FF5F5F5F"
                      Offset="0" />
        <GradientStop Color="#FFADADAD"
                      Offset="1" />
      </LinearGradientBrush>
      <!-- Style for normal button -->
      <LinearGradientBrush EndPoint="0.5,1"
                           StartPoint="0.5,0"
                           x:Key="pdsaButtonNormalStyle">
        <GradientStop Color="#FF5F5F5F"
                      Offset="0" />
        <GradientStop Color="#FFADADAD"
                      Offset="1" />
      </LinearGradientBrush>
      <!-- Overall Style for Hover Button -->
      <Style TargetType="Border"
             x:Key="pdsaButtonBorderStyle">
        <Setter Property="Margin"
                Value="6" />
        <Setter Property="CornerRadius"
                Value="5" />
        <Setter Property="Background"
                Value="{StaticResource pdsaButtonNormalStyle}" />
        <Setter Property="BorderBrush"
                Value="{StaticResource
                        pdsaButtonBorderBrushStyle}" />
        <Setter Property="BorderThickness"
                Value="{StaticResource
                        pdsaButtonBorderStyleThickness}" />
      </Style>
    </ResourceDictionary>

    You can look at the other two Resource Dictionary files when you download the sample.

    Using the Button Control

    Once you make a reference to the PDSA.WPF DLL from your WPF application you will see the “PDSAucButton” control appear in your Toolbox. Drag and drop the button onto a Window or User Control in your application. I have not referenced the PDSAButtonStyles.xaml file within the control itself so you do need to add a reference to this resource dictionary somewhere in your application such as in the App.xaml.

    <Application.Resources>
      <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
          <ResourceDictionary
            Source="/PDSA.WPF;component/PDSAButtonStyles.xaml" />
        </ResourceDictionary.MergedDictionaries>
      </ResourceDictionary>
    </Application.Resources>

    This will give your buttons a default look and feel unless you override that dictionary on a specific Window/User Control or on an individual button. The “Gray Style” button shown in Figure 1 is what the default button looks like after setting the above code in your App.xaml and then dragging a button onto a Window.

    If you wish to give a specific style to just a single button you can override the default by using the code below:

    <my:PDSAucButton HorizontalAlignment="Left"
                      Margin="6"
                      Text="Flat Blue Style"
                      x:Name="btn2"
                      Click="btn2_Click"
                      VerticalAlignment="Top">
      <my:PDSAucButton.Resources>
        <ResourceDictionary Source="FlatBlueButtonStyles.xaml" />
      </my:PDSAucButton.Resources>
    </my:PDSAucButton>

    If you want to override a series of buttons within one specific StackPanel, or within a specific Window or User Control, you simply set the Resources section for that control as shown below:

    <StackPanel Orientation="Horizontal"
                VerticalAlignment="Top">
      <!-- Set Styles for All Buttons.
           This overrides the App.xaml styles -->
      <StackPanel.Resources>
        <ResourceDictionary Source="FlatBlueButtonStyles.xaml" />
      </StackPanel.Resources>
      <my:PDSAucButton HorizontalAlignment="Left"
                        Margin="6"
                        Text="Blue Style"
                        x:Name="btn11"
                        Effect="{StaticResource mainDropShadow}"
                        VerticalAlignment="Top" />
      <!-- Can assign a custom set of styles -->
      <my:PDSAucButton HorizontalAlignment="Left"
                        Margin="6"
                        Text="Flat Blue Style"
                        x:Name="btn12"
                        VerticalAlignment="Top" />
    </StackPanel>

    In the above sample, all buttons within this StackPanel control are styled using the resource dictionary FlatBlueButtonStyles.xaml.

    Summary

    Creating your own button control can be done in a variety of ways. You can create a new Button control template, apply styles to change just a few things about a Button, create your own Custom control that inherits from Button, or build a User Control as in this blog post. Which method you choose depends a little on how comfortable you are with each method, and how much control you want over the final details of your new Button. The approach outlined in this blog post is simple and easy to understand and almost anyone can use this technique right away. Feel free to add your own animation to this control or maybe add dependency properties to control the BorderBrush, BorderThickness or any other properties you want.

    NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select “Tips & Tricks”, then select “Create your own WPF Buttons” from the drop down list.

     

    Read more...