Blog Archives

WPF Datagrid formatting (part 1)

WpfDataGrid1

This source code demonstrates the use of a simple DataGrid. You can sort the rows by any column. The Age column is horizontally aligned to the right. The column headers are using a bold font.

The DataGrid element itself is very flexible. You can add all kinds of columns. The most interesting one is DataGridTemplateColumn, which allows you to add any template. The example template only uses a DatePicker, but you could add far more complexity to it.

Set the SortMemberPath to enable sorting, otherwise the DataGrid sorting algorithm cannot know what data to look at. Remember, we are using a template and not a clearly identifiable data type. In today’s example SortMemberPath is set to “Birthday.Day”, which sorts by the day of the month. In case you prefer to sort by date in general, use SortMemberPath=”Birthday” instead.

I changed the selection color, because the dark blue had a low contrast compared to the web-links. This is dealt with by style triggers. The advantage of triggers is that they only override properties temporarily. As soon as the trigger becomes invalid the control element returns to its previous formatting.

<Window x:Class="WpfDatagrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Language="en-GB"
        Loaded="Window_Loaded"
        Closed="Window_Closed"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="SeparatorBrush" Value="WhiteSmoke" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Height" Value="30" />
        </Style>
        <Style x:Key="AlignRight" TargetType="{x:Type TextBlock}">
            <Setter Property="HorizontalAlignment" Value="Right" />
        </Style>
    </Window.Resources>

    <Grid>
        <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" CanUserAddRows="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserResizeRows="False" SelectionUnit="Cell" SelectionMode="Extended">
            <DataGrid.CellStyle>
                <Style>
                    <Style.Triggers>
                        <Trigger Property="DataGridCell.IsSelected" Value="True">
                            <Setter Property="DataGridCell.Background" Value="SteelBlue" />
                            <Setter Property="DataGridCell.BorderBrush" Value="GreenYellow" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.CellStyle>

            <DataGrid.Columns>
                <DataGridCheckBoxColumn Header="Alive" Binding="{Binding Alive}" />
                <DataGridTextColumn Header="Name" Binding="{Binding FirstName}" />
                <DataGridTextColumn Header="LastName" Binding="{Binding LastName}" />

                <!--<DataGridTemplateColumn Header="Birthday" SortMemberPath="Birthday">-->
                <DataGridTemplateColumn Header="Birthday" SortMemberPath="Birthday.Day">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <DatePicker SelectedDate="{Binding Birthday}" BorderThickness="0" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTextColumn Header="Age" Binding="{Binding Age, StringFormat=N2}" ElementStyle="{StaticResource AlignRight}" IsReadOnly="True" />

                <DataGridHyperlinkColumn Header="Homepage" Binding="{Binding Homepage}" IsReadOnly="True">
                    <DataGridHyperlinkColumn.ElementStyle>
                        <Style>
                            <EventSetter Event="Hyperlink.Click" Handler="Hyperlink_Clicked"/>
                        </Style>
                    </DataGridHyperlinkColumn.ElementStyle>
                </DataGridHyperlinkColumn>



            </DataGrid.Columns>
        </DataGrid>
    </Grid>

</Window>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;

namespace WpfDatagrid {

  public partial class MainWindow : Window {

    public class Person {
      public bool Alive { get; set; }
      public string FirstName { get; set; }
      public string LastName { get; set; }
      public DateTime Birthday { get; set; }
      public double Age { get { return DateTime.Now.Subtract(Birthday).TotalDays / 365; } }
      public string Homepage { get; set; }
    } //

    public MainWindow() {
      InitializeComponent();
    } //

    private void Window_Loaded(object sender, RoutedEventArgs e) {
      List<Person> lPersons = new List<Person>();
      lPersons.Add(new Person() { FirstName = "Liza", LastName = "Minnelli", Birthday = new DateTime(1946, 03, 12), Alive = true, Homepage = "www.officiallizaminnelli.com" });
      lPersons.Add(new Person() { FirstName = "Bastian", LastName = "Ohta", Birthday = new DateTime(1975, 03, 13), Alive = true, Homepage = "www.ohta.de" });
      lPersons.Add(new Person() { FirstName = "Albert", LastName = "Einstein", Birthday = new DateTime(1879, 03, 14), Alive = false, Homepage = "www.alberteinsteinsite.com" });
      lPersons.Add(new Person() { FirstName = "Coenraad", LastName = "van Houten", Birthday = new DateTime(1801, 03, 15), Alive = false, Homepage = "www.vanhoutendrinks.com" });
      lPersons.Add(new Person() { FirstName = "Andrew", LastName = "Miller-Jones", Birthday = new DateTime(1910, 03, 16), Alive = false, Homepage = "dead as a Dodo" });
      lPersons.Add(new Person() { FirstName = "Gottlieb", LastName = "Daimler", Birthday = new DateTime(1834, 03, 17), Alive = false, Homepage = "www.daimler.com" });
      lPersons.Add(new Person() { FirstName = "Rudolf", LastName = "Diesel", Birthday = new DateTime(1858, 03, 18), Alive = false, Homepage = "http://en.wikipedia.org/wiki/Rudolf_Diesel" });
      DataContext = lPersons;
    } //

    private void Window_Closed(object sender, EventArgs e) {
      Application.Current.Shutdown(0);      
    } //

    private void Hyperlink_Clicked(object sender, RoutedEventArgs e) {
      try {
        Hyperlink lHyperlink = e.OriginalSource as Hyperlink;
        string lUri = lHyperlink.NavigateUri.OriginalString;
        Process.Start(lUri);
      }
      catch (Exception ex) { MessageBox.Show(ex.Message); }
    } //

  } // class
} // namespace
Advertisements

WPF Control Templates (part 1)

ControlTemplate

Styles and Templates can change the appearance of elements. So what is the difference then?
Templates are far more complex. Styles are mainly adjusting existing properties. You could say that Styles are like Face-Lifting whereas Templates are rather entire face replacements. Elements get new visual trees that can consist of other elements. This is not the case for Styles.

Before we start creating our own templates let’s have a look at the existing standard control templates. Today’s example code reads these Templates from the assembly and prints them in a TextBox.

Take a stroll and get a feeling for them.
Surely this is not for beginners. My advice is to copy, paste and reuse existing professional XAML rather than spending your precious time to figure out every little issue yourself. And today’s source code aims at exactly that approach.

<Application x:Class="WpfTemplates.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             ShutdownMode="OnMainWindowClose"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>



<Window x:Class="WpfTemplates.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="Window_Loaded"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel LastChildFill="True">
        <ListBox DockPanel.Dock="Left" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" MinWidth="100" />
        <Grid DockPanel.Dock="Right" Name="dummyGrid" Width="0" />
        <TextBox DockPanel.Dock="Left" Text="{Binding XAML}" TextWrapping="NoWrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" FontFamily="Courier New" />
    </DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Xml;

namespace WpfTemplates {
  public partial class MainWindow : Window {

    private class Data {
      public Type Type { get; set; }
      public string XAML { get; set; }

      public Data(Type xType) { this.Type = xType; XAML = "#N/A"; }
      public override string ToString() { return Type.Name; }
    } // class

    public MainWindow() {
      InitializeComponent();
    } //

    private void Window_Loaded(object sender, RoutedEventArgs e) {
      List<Data> lData = new List<Data>();

      Assembly lAssembly = Assembly.GetAssembly(typeof(Control));
      Type lControlType = typeof(Control);
      List<Data> lTypes = (from t in lAssembly.GetTypes().AsParallel()
                           where t.IsSubclassOf(lControlType) && t.IsPublic && !t.IsAbstract
                           orderby t.Name ascending
                           select new Data(t)).ToList();

      InsertXaml(lTypes);
      DataContext = lTypes;
    } //

    private void InsertXaml(List<Data> xTypes) {
      XmlWriterSettings lXmlWriterSettings = new XmlWriterSettings();
      lXmlWriterSettings.Indent = true;
      StringBuilder lStringBuilder = new StringBuilder(); // for output      

      foreach (Data lData in xTypes) {
        try {
          ConstructorInfo lConstructorInfo = lData.Type.GetConstructor(System.Type.EmptyTypes);
          if (lConstructorInfo == null) {
            lData.XAML = lData.Type.Name + " control cannot be instantiated.";
            continue;
          }
          Control lControl = lConstructorInfo.Invoke(null) as Control;  // create an instance
          lControl.Visibility = System.Windows.Visibility.Collapsed;
          bool lIsNullTemplate = (lControl.Template == null);          
          if (lIsNullTemplate) dummyGrid.Children.Add(lControl); // add a collapsed (invisible) control to get access to the template
          ControlTemplate lControlTemplate = lControl.Template; // now not null anymore
          using (XmlWriter lXmlWriter = XmlWriter.Create(lStringBuilder, lXmlWriterSettings)) { // will write to StringBuilder
            XamlWriter.Save(lControlTemplate, lXmlWriter);
            lData.XAML = lStringBuilder.ToString();
          }
          lStringBuilder.Clear();
          if (lIsNullTemplate) dummyGrid.Children.Remove(lControl);
        }
        catch (Exception ex) {
          lData.XAML = lData.Type.Name + " control cannot be added to the grid.\n\nException message:\n" + ex.Message;
        }
      }
    } //

  } // class
} // namespace

WPF Commands (part 2)

Let’s start with a program that uses Cut, Copy and Paste in two TextBoxes without writing any C# code. This is not a typo. We only need XAML for this.

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="400">    
    <DockPanel LastChildFill="True" >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Edit">
                <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut it!"/>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy it!"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste it!"/>
            </MenuItem>
        </Menu>     

        <ToolBarTray Background="Gray" DockPanel.Dock="Top">
            <ToolBar Band="0" BandIndex="0" >
                <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
                <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
                <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            </ToolBar>
            <ToolBar Band="1" BandIndex="1">
                <ToolBarPanel Orientation="Vertical">
                    <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" />
                </ToolBarPanel>
            </ToolBar>
        </ToolBarTray>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox>
            <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox>
        </Grid>
    </DockPanel>
</Window>

Editor

 

I added a Toolbar with some dummy labels just to keep the learning curve going. You remove them without any risk.

What is happening here?

Some input controls handle command events on their own. Everything is built-in already. All you need to do is to provide the Buttons or MenuItems which call these commands. The elements even enable/disable themselves. We have two textboxes in the example. These commands are applied to the element that has the focus.
How can this be achieved? The element finds the window instance and then determines what element was focused previously. This only works for Toolbars and Menus UNLESS you set the CommandTarget property  manually.

Let’s add standard buttons now. You cannot see any effect when you press them. The buttons are even ghosted. To solve this we assign the names TextBox1 and TextBox2 and link the Button CommandTargets to these elements.

 

NotWorking

 

You can now cut or copy from TextBox1 and paste it into TextBox2.
 

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="400">    
    <DockPanel LastChildFill="True" >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Edit">
                <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/>
            </MenuItem>
        </Menu>     

        <ToolBarTray Background="Gray" DockPanel.Dock="Top">
            <ToolBar Band="0" BandIndex="0" >
                <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
                <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
                <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            </ToolBar>
            <ToolBar Band="1" BandIndex="1">
                <ToolBarPanel Orientation="Vertical">
                    <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" />
                </ToolBarPanel>
            </ToolBar>
        </ToolBarTray>
<!-- changed -->
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
            <Button Command="{x:Static ApplicationCommands.Cut}" CommandTarget="{Binding ElementName=TextBox1}" Content="Cut" />
            <Button Command="{x:Static ApplicationCommands.Copy}" CommandTarget="{Binding ElementName=TextBox1}" Content="Copy" />
            <Button Command="{x:Static ApplicationCommands.Paste}" CommandTarget="{Binding ElementName=TextBox2}"  Content="Paste" />
        </StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
<!-- changed -->
            <TextBox Name="TextBox1" TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox>
            <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/>
<!-- changed -->
            <TextBox Name="TextBox2" TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox>
        </Grid>

    </DockPanel>
</Window>

But hardcoding is a really bad approach. Therefore we are going to use FocusManager.IsFocusScope=”True” instead. WPF then checks the parent focus. By default, the Window class is a focus scope as are the Menu, ContextMenu, and ToolBar classes.
The following example is flawless.

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="400">    
    <DockPanel LastChildFill="True" >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Edit">
                <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/>
            </MenuItem>
        </Menu>     

        <ToolBarTray Background="Gray" DockPanel.Dock="Top">
            <ToolBar Band="0" BandIndex="0" >
                <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
                <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
                <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            </ToolBar>
            <ToolBar Band="1" BandIndex="1">
                <ToolBarPanel Orientation="Vertical">
                    <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" />
                </ToolBarPanel>
            </ToolBar>
        </ToolBarTray>
<!-- changed -->
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True">
            <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
            <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
            <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
        </StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox>
            <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox>
        </Grid>

    </DockPanel>
</Window>

The IsFocusScope approach has the advantage that the same commands apply to several controls.

 
 
Custom Commands
 

We are going to write our own commands now. For this we need to create a class and add a property that returns a RoutedUICommand instance. This property needs to be static. And to initialize this class you also need a static constructor.

using System.Windows.Input;

namespace CustomCommands {
 
  public class PlaySound {

    static PlaySound() {
      KeyGesture lShortCut = new KeyGesture(Key.P, ModifierKeys.Control, "Ctrl+p");
      InputGestureCollection InputGestureCollection = new InputGestureCollection();
      InputGestureCollection.Add(lShortCut);
      PlaySoundCommand = new RoutedUICommand("Play", "PlaySound", typeof(PlaySound), InputGestureCollection);      
    } // static constructor

    public static RoutedUICommand PlaySoundCommand { get; private set; }
  } // class

} // namespace

The MainWindow class should look like this. The method CommandBinding_PlaySound_Executed plays the system beep sound.

using System.Windows;
using System.Windows.Input;

namespace DemoApp {

  public partial class MainWindow : Window {

    public MainWindow() {
      InitializeComponent();
    }

   private void CommandBinding_PlaySound_Executed(object sender, ExecutedRoutedEventArgs e) {
      System.Media.SystemSounds.Beep.Play();      
      MessageBox.Show("Source: " + e.Source.ToString() + Environment.NewLine + 
        "OriginalSource: " + e.OriginalSource.ToString() + Environment.NewLine + 
        "Parameter: " + e.Parameter.ToString());
    } //

  } // class
} // namespace

Add the class in your XAML namespace. I used xmlns:c=”clr-namespace:CustomCommands” .

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:c="clr-namespace:CustomCommands"
        Title="MainWindow" Height="400" Width="400">
<!-- above was changed -->
    <Window.CommandBindings>
<!-- changed -->
        <CommandBinding Command="c:PlaySound.PlaySoundCommand" Executed="CommandBinding_PlaySound_Executed" />
    </Window.CommandBindings>
    <DockPanel LastChildFill="True" >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Edit">
                <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/>
            </MenuItem>
            <MenuItem Header="_Media">
                <MenuItem Command="c:PlaySound.PlaySoundCommand" CommandParameter="Play"/>
            </MenuItem>
        </Menu>     

        <ToolBarTray Background="Gray" DockPanel.Dock="Top">
            <ToolBar Band="0" BandIndex="0" >
                <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
                <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
                <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            </ToolBar>
            <ToolBar Band="1" BandIndex="1">
                <ToolBarPanel Orientation="Vertical">
                    <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" />
                </ToolBarPanel>
            </ToolBar>
        </ToolBarTray>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True">
            <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
            <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
            <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
<!-- changed -->
            <Button Command="c:PlaySound.PlaySoundCommand" Content="Play" CommandParameter="What a lovely song!" />
        </StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox>
            <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox>
        </Grid>

    </DockPanel>
</Window>

PlaySound

There is a shortcut to calling commands. You can create an ICommand instance and provide it via a property. The downside – what did you expect? – is that you have no shortcut key or any other comfort.

Step 1: Create a class that inherits from interface ICommand.

using System;
using System.Windows.Input;

namespace CustomCommands {

  public class PlaySound2 : ICommand {
    object _DependencyObject;

    public PlaySound2(object xDependencyObject) {
      _DependencyObject = xDependencyObject;
    } // constructor

    public event EventHandler CanExecuteChanged {
      add { CommandManager.RequerySuggested += value; }
      remove { CommandManager.RequerySuggested -= value; }
    } //

    public bool CanExecute(object xParameter) {
      return (DateTime.Now.Second % 2 == 0); // timer based example
    } //

    public void Execute(object xParameter) {
      //_DependencyObject.DoSomething();
      System.Windows.MessageBox.Show("Parameter: " + xParameter.ToString());
      System.Media.SystemSounds.Beep.Play();
    } //


  } // class
} // namespace

Step 2: instantiate that class and provide it via a property. You do not need to expose the class in your MainWindow. You can use any class. Set the DataContext to your class where the property is (or use a precise path that leads to that object).

using CustomCommands;
using System;
using System.Windows;
using System.Windows.Input;

namespace DemoApp {

  public partial class MainWindow : Window {

    public MainWindow() {
      InitializeComponent();
      SimpleExecution = new PlaySound2("dummy");
      DataContext = this;
    }

    private void CommandBinding_PlaySound_Executed(object sender, ExecutedRoutedEventArgs e) {
      System.Media.SystemSounds.Beep.Play();
      MessageBox.Show("Source: " + e.Source.ToString() + Environment.NewLine +
        "OriginalSource: " + e.OriginalSource.ToString() + Environment.NewLine +
        "Parameter: " + e.Parameter.ToString());
    } //

    public ICommand SimpleExecution { get; private set; }

  } // class
} // namespace

Step 3: Bind the command in XAML.

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:c="clr-namespace:CustomCommands"
        Title="MainWindow" Height="400" Width="400">
    <Window.CommandBindings>
        <CommandBinding Command="c:PlaySound.PlaySoundCommand" Executed="CommandBinding_PlaySound_Executed" />
    </Window.CommandBindings>
    <DockPanel LastChildFill="True" >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Edit">
                <MenuItem Command="{x:Static ApplicationCommands.Cut}" CommandParameter="Cut"/>
                <MenuItem Command="{x:Static ApplicationCommands.Copy}" CommandParameter="Copy"/>
                <MenuItem Command="{x:Static ApplicationCommands.Paste}" CommandParameter="Paste"/>
            </MenuItem>
            <MenuItem Header="_Media">
                <MenuItem Command="c:PlaySound.PlaySoundCommand" CommandParameter="Play"/>
            </MenuItem>
        </Menu>     

        <ToolBarTray Background="Gray" DockPanel.Dock="Top">
            <ToolBar Band="0" BandIndex="0" >
                <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
                <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
                <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            </ToolBar>
            <ToolBar Band="1" BandIndex="1">
                <ToolBarPanel Orientation="Vertical">
                    <Label Content="Dummy0" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy1" ToolBar.OverflowMode="AsNeeded" />
                    <Label Content="Dummy2" ToolBar.OverflowMode="AsNeeded" />
                </ToolBarPanel>
            </ToolBar>
        </ToolBarTray>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" FocusManager.IsFocusScope="True">
            <Button Command="{x:Static ApplicationCommands.Cut}" Content="Cut" />
            <Button Command="{x:Static ApplicationCommands.Copy}" Content="Copy" />
            <Button Command="{x:Static ApplicationCommands.Paste}" Content="Paste" />
            <Button Command="c:PlaySound.PlaySoundCommand" Content="Play" CommandParameter="What a lovely song!" />
<!-- changed -->
            <Button Command="{Binding SimpleExecution}" Content="StraightForward" CommandParameter="The German way!" />
       </StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="0" Grid.ColumnSpan="1" Margin="0" >It happened that a Fox caught its tail in a trap, and in struggling to release himself lost all of it but the stump. At first he was ashamed to show himself among his fellow foxes.</TextBox>
            <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,0,0,0" Width="3"/>
            <TextBox TextWrapping="Wrap" Width="Auto" Grid.Column="2" Grid.ColumnSpan="1" Margin="0" >But at last he determined to put a bolder face upon his misfortune, and summoned all the foxes to a general meeting to consider a proposal which he had to place before them.</TextBox>
        </Grid>

    </DockPanel>
</Window>

LastExample

That’s it for today 🙂

WPF Commands (part 1)

Due to personal time limitations I add a shorter post today. Nonetheless this post is vital for programmers without any WPF experience.

Events are fairly low-level and deprecated in WPF to a certain degree. They are against the idea of MVVM, which in simple terms is the separation of code and user interface. The new technology is task based – known as commands. Commands act like glue between the user interface and the code. You can avoid a lot of repetitive event-handling code. They deal with text captions and enabled/disabled state synchronizations. Let’s have a look at the ICommand interface:

namespace System.Windows.Input {

   public interface ICommand {
      event EventHandler CanExecuteChanged;
      bool CanExecute(object parameter);
      void Execute(object parameter);
   } // interface

} // namespace

The interface ICommand is inherited by RoutedCommand and indirectly by RoutedUICommand. RoutedUICommand has an additional descriptive text property, which RoutedCommand does not have. Besides this they are the same. RoutedUICommand inherits from RoutedCommand and is used for commands that display some text in the UI like menu items or tooltips.

namespace System.Windows.Input {

   public class RoutedUICommand : RoutedCommand {
      public RoutedUICommand();
      public RoutedUICommand(string text, string name, Type ownerType);
      public RoutedUICommand(string text, string name, Type ownerType, InputGestureCollection inputGestures);
      public string Text { get; set; }       // Text that describes this command.
   } // class

} // namespace

RoutedCommand is the only class in WPF that inherits from ICommand. All other classes are derived from RoutedCommand. An important feature of RoutedCommand in WPF is the so-called bubbling behavior. When you have a button in a StackPanel, then events will be executed in that order: Button -> StackPanel -> Window. I will keep it simple here and explain that behavior in another posts.

WPF has prebuild commands like Cut, Copy, Paste, Open and Print. There is no code behind these commands. You have to bind them to your code. Bubbling does play a big role here. You could press Ctrl+C to copy a text. The same command could be part of a TextBox and a window menu. The bubbling can be used to place the same command in two different places. The key input triggers the window command and provides enough information to process it.
Prebuild commands define a standard and generally increase code reusability.

Notice that RoutedCommand has its enhanced versions of CanExecute() and Execute():

namespace System.Windows.Input {

   public class RoutedCommand : RoutedCommand {
      public RoutedCommand();
      public RoutedCommand(string name, Type ownerType);
      public RoutedCommand(string name, Type ownerType, InputGestureCollection inputGestures);
      public InputGestureCollection InputGestures { get; }
      public string Name { get; }
      public Type OwnerType { get; }

      public event EventHandler CanExecuteChanged;
      public bool CanExecute(object parameter, IInputElement target);
      public void Execute(object parameter, IInputElement target);
   } // class

} // namespace

This is confusing. You naturally expect the same definition as in ICommand. The answer must be that the interface ICommand is implemented explicitly. Thus the interface method is hidden and can only be accessed indirectly via an interface variable. See below example:

public interface IMyInterface {
    void hello(object o);
} // interface

public class MyClass : IMyInterface {
  public void hello(object i, string s) { }
  void IMyInterface.hello(object o) {}
} // class

static void Main(string[] args) {
  MyClass x = new MyClass();       
  x.hello("abc", "abc");  // interface not accessible
  IMyInterface c = x;
  c.hello("abc");  // class not accessible
}

There are several groups (static classes) of prebuild commands in WPF.
ApplicationCommands provides classical commands that are in most applications (eg. Cut, Copy, Paste, Save, New, Print).
NavigationCommands provide commands for navigation that you know from browsers (eg. BrowseForward, BrowseBack).
EditingCommands are known from text editors (eg. Delete, MoveToDocumentEnd, MoveRightByWord, DecreaseFontSize).
ComponentCommands are used to move the cursor around (some duplicates are in EditingCommands; eg. MoveDown, MoveFocusBack, ExtendSelectionDown).
MediaCommands are self-explanatory (eg. Play, Pause, Record, BoostBass, ChannelDown).

These classes are static, there can only one instance in your application. They all have default keystrokes. For instance Ctrl+C is predefined for Copy. When you bind them to a command source and add that command source to a window, then the key combination becomes active. You don’t even need a visible control. An additional feature is that key combinations of commands are automatically shown in menus.

We had some theory now. Let’s get practical:

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200">
    <StackPanel>
        <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProjectX" Content="New" FontSize="20" />
    </StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Input;

namespace DemoApp {

  public partial class MainWindow : Window {

    public MainWindow() {
      InitializeComponent();

      CommandBinding lCommandBinding = new CommandBinding(ApplicationCommands.New);
      lCommandBinding.Executed += Button_Pressed;
      CommandBindings.Add(lCommandBinding); // add the binding object to our main "window" instance
    } //

    private void Button_Pressed(object sender, ExecutedRoutedEventArgs e) {
      MessageBox.Show(sender.ToString() + " did send the message: " + e.Parameter.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource);
    } //

  } // class
} // namespace

Window1

Window2

You don’t have to add the command in your C# code. You can add it directly in XAML. This can be more comfortable. Unfortunately there is a downside as well. The IntelliSense support suffers and I personally believe that it is more error-prone.

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200">
    <Window.CommandBindings>
        <CommandBinding Command="{x:Static ApplicationCommands.New}" Executed="Button_Pressed" />
    </Window.CommandBindings>
    <StackPanel>
        <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProjectX" Content="New" FontSize="20" />
    </StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Input;

namespace DemoApp {

  public partial class MainWindow : Window {

    public MainWindow() {
      InitializeComponent();

      //CommandBinding lCommandBinding = new CommandBinding(ApplicationCommands.New);
      //lCommandBinding.Executed += Button_Pressed;
      //CommandBindings.Add(lCommandBinding); // add the binding object to the window
    } //

    private void Button_Pressed(object sender, ExecutedRoutedEventArgs e) {
      MessageBox.Show(sender.ToString() + " did send the message: " + e.Parameter.ToString() + Environment.NewLine + "OriginalSource: " + e.OriginalSource);
    } //

  } // class
} // namespace

The next example adds a menu. The shortcut Ctrl+N is automatically shown. There is a slight change for the button as well. The object content (text) now binds to itself and uses the text from the command. The advantage is that you hardcode less and become more flexible on multi-language support later on.
You don’t have to change anything in the C# source code.

<Window x:Class="DemoApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200">
    <Window.CommandBindings>
        <CommandBinding Command="{x:Static ApplicationCommands.New}" Executed="Button_Pressed" />
    </Window.CommandBindings>
    <StackPanel>
        <Menu>
            <MenuItem Header="_File">
                <MenuItem Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProject_Y"/>
            </MenuItem>
        </Menu>
        <Separator/>
        <Button Command="{x:Static ApplicationCommands.New}" CommandParameter="NewProject_X" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" FontSize="20" />
    </StackPanel>
</Window>

Window3

Window4

Final note: Do not use the old school WinForms like approach anymore:

<Button Click="Button_Click" CommandParameter="NewProject_C" FontSize="20">Click</Button>
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Button clicked"); } 

The next post will follow-up on custom commands.

Data Binding (part 2, advanced), WPF

The new C++ posts will take a lot time. The C# posts are shorter for the next three weeks. Today I created a DataGrid and three TextBoxes. They are all linked together with pretty much no code. You can even add new items to the list. Check it out!

<Window x:Class="WpfDatabinding2.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">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="26*" />
            <ColumnDefinition Width="192*" />
            <ColumnDefinition Width="285*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="10*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <DataGrid AutoGenerateColumns="True" Grid.ColumnSpan="3" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ColumnHeaderHeight="30">            
        </DataGrid>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name}" />
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Owner}" />
        <TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Age}" />
    </Grid>
</Window>
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace WpfDatabinding2 {  

   public partial class MainWindow : Window {
      public class Dog {
         public string Name { get; set; }
         public double Age { get; set; }
         public string Owner { get; set; }
      } // class

      public MainWindow() {
         InitializeComponent();

         List<Dog> lDogs = new List<Dog>();
         lDogs.Add(new Dog() { Name = "Spike", Owner = "Granny", Age = 12.6 });
         lDogs.Add(new Dog() { Name = "Pluto", Owner = "Mickey Mouse", Age = 7.0 });
         lDogs.Add(new Dog() { Name = "Snoopy", Owner = "Charlie Brown", Age = 5.3 });
         lDogs.Add(new Dog() { Name = "Lassie", Owner = "Rudd Weatherwax", Age = 8.5 });

         this.DataContext = lDogs;
      } //

   } // class
} // namespace

Hard to believe, but this is it. Code does not have to be long to be powerful. The magic mostly comes from these lines:

<DataGrid AutoGenerateColumns="True" Grid.ColumnSpan="3" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ColumnHeaderHeight="30">            
</DataGrid>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name}" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Owner}" />
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Age}" />

Data Binding (part 1, advanced), WPF

I was studying the “Google Authentication” as I plan to write a post about it. But then I came across the WPF data binding, which I was avoiding so far, because code pieces are not easy to display in posts due to their complexity. The “Google Authentication” source code will use WPF data binding. It therefore makes sense to post about WPF data binding first. Newbies have problems with WPF. It can be quite overwhelming at the beginning. This post is not covering the basics. I expect them to be known already. I will only concentrate on some data binding with TextBoxes today.

Have a look at the XAML:

<Window x:Class="WpfDatabinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        Title="MainWindow" Height="194" Width="374">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="Value1, Init only" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="0" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneTime}" />

        <Label Grid.Column="0" Grid.Row="1" Content="Value1, OneWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="1" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneWay}" />        

        <Label Grid.Column="0" Grid.Row="2" Content="Value2, OneWayToSource" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="2" Text="{Binding Value2, Mode=OneWayToSource}" VerticalAlignment="Center" />

        <Label Grid.Column="0" Grid.Row="3" Content="Value3, OneWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="3" Text="{Binding Value3, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
        
        <Label Grid.Column="0" Grid.Row="4" Content="Value3, TwoWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="4" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />

        <Label Grid.Column="0" Grid.Row="5" Content="Value3, TwoWay" VerticalAlignment="Center" />
        <TextBox Grid.Column=" 1" Grid.Row="5" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
    </Grid>
</Window>

Instead of assigning simple text literals to the TextBoxes like: Text=”Hello World!”, we assign binding definitions in curly brackets. For instance: Text=”{Binding Value2, Mode=OneWayToSource}”
In this case we connect the property “Value2” with the TextBox text. The Mode tells the binding direction:

Mode

The UpdateSourceTrigger=PropertyChanged implies that the binding source class has implemented the interface INotifyPropertyChanged, which requires the event implementation called PropertyChanged. When the program raises this event with the correct property name, which is also defined in the XAML file, then the binding will refresh the GUI element.

And here is the program. Create a BindingClass instance and assign it to the current window DataContext. There is nothing else to do. The rest is taken care of by the .Net Framework and compiler. WPF is quite abstract. A lot of code is generated out of the XAML files. You cannot see the generated code without pressing the icon “Show All Files” in the Solution Explorer. What is important to know is that we are mainly looking at partial files (here: “public partial class MainWindow : Window”). The rest is auto-generated source code.

using System.Windows;

namespace WpfDatabinding {
   public partial class MainWindow : Window {

      public MainWindow() {
         InitializeComponent();
         DataContext = new BindingClass();
      } //

   } // class
} // namespace
using System;
using System.ComponentModel;
using System.Timers;

namespace WpfDatabinding {
   public class BindingClass : INotifyPropertyChanged {
      public event PropertyChangedEventHandler PropertyChanged;

      private string _Value1 = "Value1 XX:XX:XX";
      private string _Value2 = "Value2 XX:XX:XX";
      private string _Value3 = "Value3 XX:XX:XX";

      public string Value1 { get { return _Value1; } set { _Value1 = value; RaiseEvent("Value1"); } }
      public string Value2 { get { return _Value2; } set { _Value2 = value; RaiseEvent("Value2"); Console.WriteLine(_Value2); } } // event has no effect
      public string Value3 { get { return _Value3; } set { _Value3 = value; RaiseEvent("Value3"); } } // event has effect on TwoWay

      public BindingClass() {
         Timer lTimer = new Timer(1000.0);
         lTimer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);
         lTimer.Start();
      } // constructor

      void Timer_Elapsed(object sender, ElapsedEventArgs e) {
         string lNow = DateTime.Now.ToString("HH:mm:ss");
         Value1 = "Value1 " + lNow;
      } // 

      private void RaiseEvent(string xPropertyName) {
         var lEvent = PropertyChanged;
         if (lEvent == null) return;
         lEvent(this, new PropertyChangedEventArgs(xPropertyName));
      } //

   } // class
} // namespace

The BindingClass is straight forward. There are no events that are triggered by the GUI. The separation of XAML and program code is a pretty cool approach.
There are three properties: Value1, Value2 and Value3.
Value1 is constantly written to by a timer event that is raised each second.
Value2 is only receiving values from the GUI, because the corresponding TextBox is using Mode=OneWayToSource. This does not print anything in the GUI, hence I added the Console.WriteLine(_Value2) statement. Check the output there. The statement RaiseEvent("Value2") has no impact, writing to a TextBox would be the wrong direction for this mode.
And RaiseEvent("Value3") only impacts TwoWay TextBoxes, but not on the OneWay TextBox.
Play with the textboxes and see what reacts on what. My hint is that the last three TextBoxes are the most interesting ones.

WpfDataBinding

example output on the console window:
What happens with Value 2 ?