Blog Archives
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>
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.
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>
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>
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
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>
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.
Inversion of Control and Dependency Injection (advanced, Part 1), Programming Patterns
I finally had some time and spent some hours on Inversion of Control (IoC) and Dependency Injection (DI). When I did the same a few weeks ago I did not understand a lot. I got lost on the concept of inversion. I tried to figure out, what the inversion was. This blocked all and I ended up pretty much blank despite nearly an hour of pumping information into my head. When you get lost then you should do it at least properly and at full steam 😉 Well, I finally understood the headline … and the world started making sense again.
Why the name “Inversion of Control”? (IoC)
An entirely hardcoded class controls the program execution path. All branches are predetermined. By using interfaces you can decouple theses flows, so that at the time of creation the class does not exactly know what instructions to call next.
Let’s say you are using events. The class, which is raising an event, hands the control over to another class. The subscribing class is in control of the program flow, not the class that raises the event. The subscribing class can subscribe or unsubscribe. The class, which is providing the event, should not have any active control over subscriptions. Hence it is an inversion of control.
By using IoC classes become more encapsulated. Let’s say a perfect class is blind and urgently needs a guide dog. The control can now be taken over by external factors. This inverts the control entirely.
Unit testing uses that mechanism. A container can be used to control classes and change the bindings.
What is a container?
The expression “container” is rarely used in C#. The C++ world calls containers what C# calls collections. We have come across containers in my C++ posts. Follow the link for a quick refresher.
To simplify the matter we can call a UnityContainer a dictionary of objects with some additional methods.
This container performs binding between components. For instance it replaces all specific interface declarations by fully instantiated classes without the need to explicitly call any initialization.
What is dependency?
The structure of a program is: Input, Calculations, Output. The same generally applies to classes. Let’s say you want to run an analysis of a text file. That analysis class can only function properly if the required text file does exist. The calculations depend on the input. The same applies to the output, which can only run if the calculation was successful.
The input class calls the calculation class, which in turn calls the output class. As we are discussing inversion, let’s decouple the classes and implement events. In this case the output class subscribes to the calculation result event and the calculator subscribes to the input event.
What Dependency Injection? (DI)
“Dependency Injection” is a subset of “Inversion of Control”. IoC is a principle, whereas DI is an actual implementation of the principle.
DI is a software design pattern that allows removing hard-coded dependencies. First of all you do not create objects directly; you just describe how they look like. To accomplish this, the pattern uses interfaces or base classes. As the dependency is not hardcoded, the actual object can vary each time. It just needs to fulfill the inheritable pattern of an interface or base class.
public class Car { } // base class public class Audi : Car { } public class BMW : Car { } public class Exhibition { Car StageObject { set; get; } // assign Audi or BMW as the dependency object of class Exhibition }
Interfaces
What are the benefits of interfaces besides multiple inheritance? Several people can work on different problems simultaneously by using interfaces. One person for instance writes the code for logging to text files and another person writes the code for logging to databases. If both use the same interface definition, then the underlying classes can be easily replaced by each other. Interfaces are like promises to provide predefined class patterns at runtime.
Thus we end up with component separation, which is very useful in unit testing. Interfaces can eliminate unfathomable dependencies if used wisely.
Dependency Injection and Unit Testing
When you run unit tests, then you will need input data. But it could be too complex to run the entire program just to test some methods. You would try to only instantiate the minimum requirements. Think of a syringe and inject the data into the test case to create an acceptable environment. This can be difficult in case the program was not structured well. You need to examine the code to find dependencies.
I will cover some IoC/DI basics today and will follow-up on this after some other posts, which were queuing up in the last weeks:
- Calling Java from C#
- Calling C# from Java
- Implement all Java source code examples of the 15 day series C# to C++
- WPF Datagrid formatting
- Google Authenticator
- create a post index for my blog
Dependency Injection
There are several ways to implement dependencies in a class. The easiest way is to have a field that holds a reference to the dependency, which is probably the worst approach you can have in terms of flexibility.
public class Report1 { private IDataBase _DB = new DataBase(); } Report1 r1 = new Report1(); // dependency DataBase must be figured out by examining the code
You can use methods or properties to tell your classes what dependency objects they should use. The best approach though is via constructors. You can hardly miss parameters when trying to call the constructor. Of course bad constructor overloading can jeopardize this concept.
public class Car { } // base class (instead of an interface) public class Audi : Car { } public class BMW : Car { } public class Exhibition { Car StageObject { set; get; } // assign Audi or BMW as the dependency object of class Exhibition } public class Report2 { public IDataBase DB { get; private set; } public void SetDB(IDataBase xDB) { DB = xDB; } } public class Report3 { public IDataBase DB { get; set; } } public class Report4 { private IDataBase _DB; public Report4(IDataBase xDB) { _DB = xDB; } } DataBase lDB = new DataBase(); Report2 r2 = new Report2(); r2.SetDB(lDB); Report3 r3 = new Report3(); r3.DB = lDB; Report4 r4 = new Report4(lDB);
If the world was just like class Report4, then we could more or less end the post here. Unfortunately dependencies are often not that obvious. They are well hidden and you need to read the code thoroughly to build unit tests.
Dependency Injection goes further and the real take off takes place with Moq, which I will explain in the follow-up post.
The following code example was didactically compiled. You don’t need any further information, it should be self-explanatory. You can download unity by following this link or using NuGet.
using System; using Microsoft.Practices.Unity; public interface IDataBase { void QuerySomething(); } // interface public interface ITextFile { void LoadSomething(); } // interface public interface INetwork { string Text { set; get; } void ReceiveSomething(); } // interface public class Network : INetwork { public void ReceiveSomething() { Console.WriteLine("Receiving TCP data ..."); } public string Text { set; get; } } // class public class DataBase : IDataBase { private string _Dummy = "I am doing something."; public void QuerySomething() { Console.WriteLine(_Dummy); } } // class public class TextFile1 : ITextFile { public void LoadSomething() { Console.WriteLine("TF1: Loading something..."); } } // class public class TextFile2 : ITextFile { public void LoadSomething() { Console.WriteLine("TF2: Loading something..."); } } // class public class TextFile3 : ITextFile { public void LoadSomething() { Console.WriteLine("TF3: Loading something..."); } } // class public class Report5 { public string Text = "#N/A"; private IDataBase _DB; public readonly ITextFile TF; public IDataBase getDB() { return _DB; } public Report5(IDataBase xDB, ITextFile xTextFile) { _DB = xDB; TF = xTextFile; } } // class public class Report6 { public readonly string Text1; public readonly string Text2; private readonly ITextFile _TextFile; public readonly INetwork Network; public Report6(ITextFile xTextFile, INetwork xNetwork, string xText1, string xText2) { _TextFile = xTextFile; Network = xNetwork; Text1 = xText1; Text2 = xText2; } // constructor } // class class Program { static void Main(string[] args) { UnityContainer lContainer = new UnityContainer(); // using Microsoft.Practices.Unity; Report5 r; // insufficient data Console.WriteLine("test: insufficient data"); Console.WriteLine("Registering IDataBase"); lContainer.RegisterType(typeof(IDataBase), typeof(DataBase)); // whenever someone asks for an IDataBase, then return a new DataBase instance try { r = lContainer.Resolve<Report5>(); // throws an exception, because ITextFile is undefined } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine(); // full data Console.WriteLine("test: sufficient data"); Console.WriteLine("Registering ITextFile TF1"); Console.WriteLine("IDataBase is still registered"); lContainer.RegisterType(typeof(ITextFile), typeof(TextFile1)); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile1 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile1 Console.WriteLine(); // override a previous type registration with another type Console.WriteLine("test: override a previous type registration with another type"); Console.WriteLine("Registering ITextFile TF2"); lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); // override the first type registration //lContainer.RegisterType<ITextFile, TextFile2>(); // same as lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile2 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile2 Console.WriteLine(); // override a previous type registration with an instance Console.WriteLine("test: override a previous type registration with an instance"); Console.WriteLine("Registering an instance of TextFile3"); ITextFile lTextFile = new TextFile3(); lContainer.RegisterInstance(lTextFile); r = lContainer.Resolve<Report5>(); Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile3 r.getDB().QuerySomething(); r.TF.LoadSomething(); // this is TextFile3 Console.WriteLine(); // using names to register instances Console.WriteLine("test: using names to register instances"); lContainer.RegisterType<Report5, Report5>(); // creates a default class without any name Report5 a = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("A", a); a.Text = "Report A"; Report5 b = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("B", b); b.Text = "Report B"; r = lContainer.Resolve<Report5>("A"); Console.WriteLine("got " + r.Text); r = lContainer.Resolve<Report5>("B"); Console.WriteLine("got " + r.Text); r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?"; r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text); // new instance r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // new instance Console.WriteLine(); // => HAVE A LOOK AT THE BELOW CONTAINER SNAPSHOT => there are 3 instances for Report5 // using names to register instances Console.WriteLine("test: revision, using the same instance"); Console.WriteLine("ContainerControlledLifetimeManager: re-use instances (singleton behaviour for objects)"); lContainer.RegisterType<Report5, Report5>(new ContainerControlledLifetimeManager()); r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?"; r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text); // new instance r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // same instance !!!!! Console.WriteLine(); // constructors with parameters lContainer.RegisterType<INetwork, Network>(); lContainer.RegisterType<ITextFile, TextFile2>(); Console.WriteLine("test: constructors with parameters"); ResolverOverride[] lParameters = new ResolverOverride[] { new ParameterOverride("xText1", "Hello "), new ParameterOverride("xText2", "world") }; Report6 lReport6 = lContainer.Resolve<Report6>(lParameters); Console.WriteLine("Report6 text field values are: " + lReport6.Text1 + lReport6.Text2); Console.ReadLine(); } // } // class
example output:
test: insufficient data
Registering IDataBase
Resolution of the dependency failed, type = “Report5”, name = “(none)”.
Exception occurred while: while resolving.
Exception is: InvalidOperationException – The type ITextFile does not have an ac
cessible constructor.
———————————————–
At the time of the exception, the container was:
Resolving Report5,(none)
Resolving parameter “xTextFile” of constructor Report5(IDataBase xDB, ITextFil
e xTextFile)
Resolving ITextFile,(none)
test: sufficient data
Registering ITextFile TF1
IDataBase is still registered
type of r.TF is TextFile1
I am doing something.
TF1: Loading something…
test: override a previous type registration with another type
Registering ITextFile TF2
type of r.TF is TextFile2
I am doing something.
TF2: Loading something…
test: override a previous type registration with an instance
Registering an instance of TextFile3
type of r.TF is TextFile3
I am doing something.
TF3: Loading something…
test: using names to register instances
got Report A
got Report B
got #N/A
got #N/A
got #N/A
test: revision, using the same instance
ContainerControlledLifetimeManager: re-use instances (singleton behaviour for ob
jects)
got #N/A
got #N/A
got same instance?
test: constructors with parameters
Report6 text field values are: Hello world
Explicit interface implementations (basic)
We have come across an interface example in my post Extension methods yesterday.
I am going to follow up on interfaces today.
Let’s have a look at an explicit and an implicit interface implementation:
public interface IInterfaceA { void MethodA(); } class Implicit : IInterfaceA { public void MethodA() { } } class Explicit : IInterfaceA { void IInterfaceA.MethodA() { } //public void IInterfaceA.MethodA() { } // won't compile, cannot use the "public" modifier }
The class Explicit does not allow a public modifier for MethodA(). This restricts the access.
Explicit interface implementations can only be accessed by using the interface directly. In the next example the compiler will complain about lExplicit.MethodA(). The instance lExplict cannot access the method, but the interface call b.MethodA() on the same object apparently is no problem. This way explicit interface implementation is used to hide members of a class.
void Test() { Implicit lImplicit = new Implicit(); lImplicit.MethodA(); // business as usual IInterfaceA a = lImplicit; a.MethodA(); Explicit lExplicit = new Explicit(); lExplicit.MethodA(); // compiler error IInterfaceA b = lExplicit; b.MethodA(); // works 🙂 } //
In theory we could simply write a class with an overridden modifier to have a similar behaviour. To tell you the truth right away: It won’t compile.
public class MyGeekClass : IInterfaceA { private void MethodA() { } // compiler error: private modifier not allowed }
Why does it not compile? First of all the private modifier does not exactly describe the public behavior via the interface instance.
Secondly using the private modifier would not solve naming issues. Interfaces support multiple inheritance, so the problem is a bit more tricky. Microsoft solved it in an appropriate way. Here is an example:
public interface ICat { void DoSomething(); } public interface IDog { void DoSomething(); } public class Animal : ICat, IDog { void ICat.DoSomething() { Console.WriteLine("Cat"); } void IDog.DoSomething() { Console.WriteLine("Dog"); } } static void Main(string[] args) { Animal lAnimal = new Animal(); //lAnimal.DoSomething(); // compiler error ICat lCat = lAnimal; IDog lDog = lAnimal; lCat.DoSomething(); lDog.DoSomething(); } //
example output:
Cat
Dog