Tag Archives: wpf

Design-Time Data Binding in WPF

One of the cool things that WPF allows you to do is create sample data that can be bound to controls at design-time. This spiffy little feature allows you to do all kinds of tinkering with your UI without having to run your application. A short feedback loop is essential since WPF provides so much flexibility with what you can do. If you have an application that loads a significant amount of data, and you need to load all that data each time you want to see a UI change, that can lead to a significant amount of wasted time.

When you search for how to do this on the web, the most common method is to create a XAML file with your sample data, and then reference the file in the design data context. Here’s a very short example.

Spartan.cs

namespace adamprescott.net.DesignTimeDataBinding
{
    public class Spartan
    {
        public string Profession { get; set; }
        public byte[] Picture { get; set; }
    }
}

SpartanSampleData.xaml (Be sure to change BuildAction to DesignData!)

<m:Spartan xmlns:m="clr-namespace:adamprescott.net.DesignTimeDataBinding" 
           Profession="HooHooHoo">
</m:Spartan>

MainWindow.xaml

<Window x:Class="adamprescott.net.DesignTimeDataBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DataContext="{d:DesignData Source=/SpartanSampleData.xaml}"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel VerticalAlignment="Top">
            <Image Source="{Binding Picture}" Height="50" />
            <TextBlock Text="{Binding Profession}" />
        </DockPanel>
    </Grid>
</Window>

I had a problem with this method, though. My model had a byte array property that stored image data, and I couldn’t come up with a good way to include a sample image in the design data XAML. I learned that you can also accomplish design-time data binding through the use of static classes, and that gave me exactly what I was looking for: the ability to create and define sample data in code! Here’s the same example as above, modified to use a static class.

SpartanSampleDataContext.cs (Note that I also added an image to the project in the root namespace.)

namespace adamprescott.net.DesignTimeDataBinding
{
    using System;
    using System.IO;
    using System.Reflection;
    using System.Windows;

    public static class SpartanSampleDataContext
    {
        public static Spartan SpartanSampleData
        {
            get
            {
                var result = new Spartan
                {
                    Profession = "HooHooHoo",
                    Picture = GetSampleImageBytes()
                };
                return result;
            }
        }

        private static byte[] GetSampleImageBytes()
        {
            var assemblyName = new AssemblyName(
                Assembly.GetExecutingAssembly().FullName);
            var resourceUri = new Uri(
                String.Format("pack://application:,,,/{0};component/leonidas.jpg", 
                assemblyName.Name));
            using (var stream = Application.GetResourceStream(resourceUri).Stream)
            {
                using (var memoryStream = new MemoryStream())
                {
                    stream.CopyTo(memoryStream);
                    return memoryStream.ToArray();
                }
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="adamprescott.net.DesignTimeDataBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:local="clr-namespace:adamprescott.net.DesignTimeDataBinding"
        d:DataContext="{x:Static local:SpartanSampleDataContext.SpartanSampleData}"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel VerticalAlignment="Top">
            <Image Source="{Binding Picture}" Height="50" />
            <TextBlock Text="{Binding Profession}" />
        </DockPanel>
    </Grid>
</Window>

Advertisements

Closeable Tabs in WPF Made Easy

There are a number of good articles out there about creating closeable tabs in WPF, but they’re very complicated. It shouldn’t be that hard! So, I’m going to try to break it down and make it easy for you.

Here’s what we’re going to do:

  1. Create a new user control for the tab close button
  2. Create a new class that inherits from TabItem
  3. Try it out!

Create a new user control for the tab close button

Add a new UserControl to your project. Since it’s a full-blown UserControl, you can easily style the button however you’d like and add whatever additional code-behind logic you need. The important thing about the control is that it raises a “Close” event that can be handled in our custom TabItem control. You can get fancy with your button, but I wanted to keep it simple for this example so I just used a red button with a white X drawn on it.

XAML:

<UserControl x:Class="adamprescott.net.TabbedDocuments.TabCloseButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <Button Click="OnClick" Background="Red">
        <Path Data="M1,9 L9,1 M1,1 L9,9" Stroke="White" StrokeThickness="2" />
    </Button>
    
</UserControl>

Code-behind:

namespace adamprescott.net.TabbedDocuments
{
    using System;
    using System.Windows;
    using System.Windows.Controls;

    public partial class TabCloseButton : UserControl
    {
        public event EventHandler Click;

        public TabCloseButton()
        {
            InitializeComponent();
        }

        private void OnClick(object sender, RoutedEventArgs e)
        {
            if (Click != null)
            {
                Click(sender, e);
            }
        }
    }
}

Create a new class that inherits from TabItem

Add a new class to your project that derives from TabItem. My class has a single method, SetHeader. This method accepts the desired header content as an argument and adds it to a collection with our custom close button.

namespace adamprescott.net.TabbedDocuments
{
    using System.Windows;
    using System.Windows.Controls;

    public class CloseableTabItem : TabItem
    {
        public void SetHeader(UIElement header)
        {
            // Container for header controls
            var dockPanel = new DockPanel();
            dockPanel.Children.Add(header);

            // Close button to remove the tab
            var closeButton = new TabCloseButton();
            closeButton.Click +=
                (sender, e) =>
                {
                    var tabControl = Parent as ItemsControl;
                    tabControl.Items.Remove(this);
                };
            dockPanel.Children.Add(closeButton);

            // Set the header
            Header = dockPanel;
        }
    }
}

Try it out!

Now that all the hard stuff is done, let’s make a sample application to test it out. I have a simple Window with a Button and a TabControl. When the button is clicked, a tab is added with a TextBlock header and a TextBlock content. Each tab can be closed by clicking its close button.

XAML:

<Window x:Class="adamprescott.net.TabbedDocuments.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    
    <DockPanel>
        <Button DockPanel.Dock="Top" Click="OnPlusTabClick">+Tab</Button>
        <TabControl Name="uxTabs">
        </TabControl>
    </DockPanel>
    
</Window>

Code-behind:

namespace adamprescott.net.TabbedDocuments
{
    using System.Windows;
    using System.Windows.Controls;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnPlusTabClick(object sender, RoutedEventArgs e)
        {
            // Create the header
            var header = new TextBlock { Text = "Tab!" };
            
            // Create the content
            var content = new TextBlock
            {
                Text = string.Format("Tab numero {0}-o", 
                    uxTabs.Items.Count + 1)
            };

            // Create the tab
            var tab = new CloseableTabItem();
            tab.SetHeader(header);
            tab.Content = content;

            // Add to TabControl
            uxTabs.Items.Add(tab);
        }
    }
}

See? It didn’t have to be that hard!

Create a Button That Doesn’t Look Like a Button With WPF

One of my favorite things about WPF is that you can make controls look like anything you want. You want a button with text? <Button><TextBlock /></Button> You want a button with an image? <Button><Image /></Button>

So, when I was adding closeable tab functionality to my application, I figured it would be a breeze. When I added the button to the tab header, though, something undesirable happened: it looked like a button. I wanted a close button that just looked like an “X,” not the usual 3D Windows button. Luckily, this is easily accomplished through the use of control templates.

Creating a custom button is a two-step process:

  1. Create a ControlTemplate
  2. Apply the template

Here’s a short example of what I did to create a simple “X” close button:

<Window.Resources>
    <ControlTemplate x:Key="buttonTemplate" TargetType="Button">
        <Path Data="M0,0 L8,8 M8,0 L0,8" Margin="5,5,0,0" Stroke="Black" StrokeThickness="3" />
    </ControlTemplate>
</Window.Resources>

<Button Template="{StaticResource ResourceKey=buttonTemplate}" />

This works just fine, but I’ve created a usability problem. When the user sees or interacts with the button, there are no visual indicators that the item is clickable or anything happens when it’s clicked. No problems, though; we can fix these problems with a pair of style triggers.

<Window.Resources>
    <ControlTemplate x:Key="buttonTemplate" TargetType="Button">
        <Path Data="M0,0 L8,8 M8,0 L0,8" Margin="5,5,0,0" StrokeThickness="3">
            <Path.Style>
                <Style TargetType="{x:Type Path}">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="False">
                            <Setter Property="Stroke" Value="LightGray" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Stroke" Value="Red" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Path.Style>
        </Path>
    </ControlTemplate>
</Window.Resources>

<Button Template="{StaticResource ResourceKey=buttonTemplate}" />

Now when you hover over the button, the X turns red. This is the perfect button for my closeable tab.

Retrieve Active Directory User Photos in C#

Newer versions of Office (2010+) use Active Directory to retrieve and display user photos. It’s a useful feature that also adds visual interest. I can look quickly at the thumbnails at the bottom of an email or meeting request in Outlook to see who’s invited; this is much faster than reading through the semi-colon delimited list of email addresses.

I’m working on a TFS application that has some custom views. I thought it would be cool to display the user as a thumbnail instead of simply using their name. Doing this will add some pizzazz and, ultimately, result in a cleaner UI since vertical space is more abundant in my layout–it is less costly for me to show a 50×50 thumbnail than to display a 20×100 textbox.

So how do we do it? Like many tasks, the .NET Framework makes it relatively easy for us once we know what we’re doing. Here are the steps:

  1. Bind to a node in Active Directory Domain Services with the DirectoryEntry class
  2. Use the DirectorySearcher class to specify a search filter and find the desired user
  3. Extract the image bytes from the user properties
  4. Convert the bytes to a usable format

The method below accepts a username parameter, looks it up in AD, and binds the thumbnail to an Image. (Note that this is a WPF application, which is why I convert to the BitmapImage class. You may want to convert to a different type, like System.Drawing.Bitmap.)

private void GetUserPicture(string userName)
{
    var directoryEntry = new DirectoryEntry("LDAP://YourDomain");
    var directorySearcher = new DirectorySearcher(directoryEntry);
    directorySearcher.Filter = string.Format("(&(SAMAccountName={0}))", userName);
    var user = directorySearcher.FindOne();

    var bytes = user.Properties["thumbnailPhoto"][0] as byte[];

    using (var ms = new MemoryStream(bytes))
    {
        var imageSource = new BitmapImage();
        imageSource.BeginInit();
        imageSource.StreamSource = ms;
        imageSource.EndInit();

        uxPhoto.Source = imageSource;
    }
}

Rotate an Image in WPF Using Just XAML

One of the things that came along with WPF was the ability to create animations declaratively in XAML. I wanted to create a working-in-the-background spinner for a WPF application I was working on, and I thought that it would be a breeze with such powerful capability. However, I had a surprisingly difficult time finding a nice, simple example on the internet!

After a lot of looking and a bit of tinkering, I was able to come up with a solution that I’m mostly satisfied with. It’s a little more verbose than I was hoping for, but it’s still a XAML-only solution, which is what I was ultimately seeking.

Some notes about the solution:

  • The CenterX and CenterY properties of the RotateTransform element must be half of the image to make it rotate around the center of the image. My image is 24×24, so the X and Y centers are 12 and 12, respectively.
  • The DoubleAnimation will run on an infinite loop from angle 0 to 360 over a 1 second duration.
  • The storyboard will be active while the image’s IsEnabled property is set to True.
<Image Source="gear_24x24.png" Height="24">
    <Image.RenderTransform>
        <RotateTransform CenterX="12" CenterY="12" />
    </Image.RenderTransform>
    <Image.Style>
        <Style>
            <Style.Triggers>
                <Trigger Property="Image.IsEnabled" Value="True">
                    <Trigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="RenderTransform.Angle"
                                    From="0"
                                    To="360"
                                    Duration="0:0:1"
                                    RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </Trigger.EnterActions>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Image.Style>
</Image>

You could also extract the animation code away into a resource. This will improve reusability and keep your XAML cleaner if you have multiple spinners, but it’s more of a preference when dealing with a single spinner.

<Window.Resources>
    <Style x:Key="Spinner" TargetType="Image">
        <Setter Property="Height" Value="24" />
        <Setter Property="Image.RenderTransform">
            <Setter.Value>
                <RotateTransform CenterX="12" CenterY="12" />
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="True">
                <Trigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation 
                                        Storyboard.TargetProperty="RenderTransform.Angle" 
                                        From="0" 
                                        To="360" 
                                        Duration="0:0:1" 
                                        RepeatBehavior="Forever" />
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid>
    <Image Source="gear_24x24.png" Style="{StaticResource Spinner}" />
</Grid>

This solution is based on this post.

Windows Workflow 4 Designer WPF Application

One of the coolest things about Windows Workflow Foundation is the workflow designer. It’s visual programming.  You drag and drop your components onto the design surface and configure the properties of each module to create a functional application. Microsoft makes it easy to re-host the workflow designer in your own applications, providing you with an easy way to include customization and configuration tools for your workflows.

I found a great bare-bones tutorial at MSDNRehosting the Workflow Designer.

There’s some important functionality missing from this tutorial, though. There are no mechanisms for opening or saving workflow files, and the toolbox is only populated with two controls. So, I’ve taken the MSDN tutorial, compressed the steps, and added some additional key functionality.

  1. Create a new WPF Application. I called mine “WorkflowEditor.”
  2. Add references
    • System.Activities
    • System.Activities.Core.Presentation
    • System.Activities.Presentation
  3. Copy/paste the XAML below into MainWindow.xaml
  4. Copy/paste the code-behind below into MainWindow.xaml.cs

That’s it! Once you’re this far, you should be able to run the program. If you’re looking for a more in-depth, step-by-step explanation of the code, follow the link above to the MSDN tutorial. The only differences below are that I added some reflection code to extract the “standard” workflow activities, and I added Open and Save buttons.

<Window x:Class="adamprescott.net.WorkflowEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="800">
    <Grid Name="grid1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="4*" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="36" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <ToolBar Grid.Row="0" Grid.ColumnSpan="3">
            <Button Click="OnOpenClick">Load</Button>
            <Button Click="OnSaveClick">Save</Button>
        </ToolBar>
    </Grid>
</Window>
using System;
using System.Activities;
using System.Activities.Core.Presentation;
using System.Activities.Presentation;
using System.Activities.Presentation.Toolbox;
using System.Activities.Statements;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using Microsoft.Win32;

namespace adamprescott.net.WorkflowEditor
{
    public partial class MainWindow
    {
        private WorkflowDesigner _designer;

        public MainWindow()
        {
            InitializeComponent();
            RegisterMetadata();
            InitializeDesigner();
            AddToolBox();
        }

        private void AddDesigner()
        {
            _designer = new WorkflowDesigner();
            Grid.SetColumn(_designer.View, 1);
            Grid.SetRow(_designer.View, 1);
            grid1.Children.Add(_designer.View);
        }

        private void RegisterMetadata()
        {
            var dm = new DesignerMetadata();
            dm.Register();
        }

        private ToolboxControl GetToolboxControl()
        {
            var toolbox = new ToolboxControl();
            PopulateToolboxCategoryFromAssembly(toolbox, typeof(Assign).Assembly, "Standard");
            return toolbox;
        }

        private void PopulateToolboxCategoryFromAssembly(ToolboxControl toolbox, Assembly assembly, string categoryName)
        {
            var tools = assembly.GetTypes()
                .Where(t => t.IsSubclassOf(typeof(Activity))
                    && t.IsPublic
                    && !t.IsAbstract
                    && HasParameterlessContructor(t))
                .Select(t => new ToolboxItemWrapper(t.FullName, t.Assembly.FullName, null, t.Name))
                .OrderBy(t => t.DisplayName);

            var category = new ToolboxCategory(categoryName);
            foreach (var t in tools)
            {
                category.Add(t);
            }
            toolbox.Categories.Add(category);
        }

        private bool HasParameterlessContructor(Type t)
        {
            var ctors = t.GetConstructors();
            var parameterless = ctors.Where(c => c.GetParameters().Count() == 0)
                .FirstOrDefault();
            return parameterless != null;
        }

        private void AddToolBox()
        {
            var tc = GetToolboxControl();
            Grid.SetColumn(tc, 0);
            Grid.SetRow(tc, 1);
            grid1.Children.Add(tc);
        }

        private void AddPropertyInspector()
        {
            Grid.SetColumn(_designer.PropertyInspectorView, 2);
            Grid.SetRow(_designer.PropertyInspectorView, 1);
            grid1.Children.Add(_designer.PropertyInspectorView);
        }

        private void OnOpenClick(object sender, System.Windows.RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog();
            if (dlg.ShowDialog().Value)
            {
                InitializeDesigner();
                _designer.Load(dlg.FileName);
            }
        }

        private void OnSaveClick(object sender, System.Windows.RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            if (dlg.ShowDialog().Value)
            {
                _designer.Save(dlg.FileName);
            }
        }

        private void InitializeDesigner()
        {
            AddDesigner();
            AddPropertyInspector();
        }
    }
}

WPF Combo Box validation

I’ve been working on a small WPF project that requires some business rule validation. I thought this would be relatively simple but proved to be slightly more complicated than expected. At the end of the day, I found two relatively simple ways to perform the validation.

The first is to simply bind ComboBox.Selected item to a property and do the validation in the set block.

    private MyDataObject _someData;
    public MyDataObject SomeData
    {
        get
        {
            return _ someData;
        }
        set
        {
            _ someData = value;
            if (value == null || string.IsNullOrEmpty(value.MyProperty))
                throw new ApplicationException("SomeData is required");
        }
    }

Then, in the XAML, you just need to hook it up to treat exceptions as validation errors.

    <ComboBox.SelectedItem>
        <Binding Path="SomeData" ElementName="Window">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </ComboBox.SelectedItem>

The second method gives more flexibility but requires the creation of a custom ValidationRule class.

    public class MyCustomValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (value is MyDataObject)
            {
                var myDataObj = (MyDataObject)value;
                if (myDataObj.CheckCustomBusinessRules())
                    return new ValidationResult(true, null);
            }
 
            return new ValidationResult(false, "Invalid selection!");
        }
    }

And then you hook up the validation rules to use the new custom class.

    <ComboBox.SelectedItem>
        <Binding Path="SomeData" ElementName="Window">
            <Binding.ValidationRules>
                <local:PersonValidation />
            </Binding.ValidationRules>
        </Binding>
    </ComboBox.SelectedItem>

(Note that the “local” prefix requires an additional namespace at the top of your XAML file, xmlns:local=”clr-namespace:MyNamespace”)

One last tip that threw me off for a bit is that I was missing x:Name=”Window” in my Window tag at the top of my XAML; this caused the ComboBox’s SelectedItem to not bind properly and the validation rules consequently didn’t work.