One of the features that WPF has is actually a blast from the past -
something that MFC used to have, but was never included in WinForms.
This is the concept of Commands. Commands are somewhat like events in
that they are based on actions in the user interface, a command can get
triggered (just like an event). But unlike regular events like 'click',
commands are built in a way that makes it very easy to expose the same
functionality at multiple places in an interface, with very little code
duplication.
On of the very useful features of commands is the fact that you actually
get an automatic form of two way communication between the interface
elements and the command they are bound too. This is because a command
is made up of two main elements -
Execute
and CanExecute
. So unlike
in the regular world of events, when an action becomes unavailable, you
can just have the CanExecute
part of the command return false - and
everything hooked to that command becomes disabled. Without this, you
would have to do any of a number of ugly things to make sure that all
user interface elements that perform an action are always correctly
enabled or disabled.
Today we are going to take a look at how to use the built in commands
that WPF provides (and there are a lot of them). The handy thing about
the built in commands is that a number of the standard controls
automatically respond to them. Take a look at the XAML code below:
<Window x:Class="SimpleTextEditor.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandTest" Height="300" Width="318">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="textBoxCommands">
<Setter Property="Content" Value=
"{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />
<Setter Property="CommandTarget" Value="{Binding ElementName=textBox}" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<Button Command="Cut" Style="{StaticResource textBoxCommands}" />
<Button Command="Copy" Style="{StaticResource textBoxCommands}" />
<Button Command="Paste" Style="{StaticResource textBoxCommands}" />
<Button Command="AlignCenter" Style="{StaticResource textBoxCommands}" />
<Button Command="AlignLeft" Style="{StaticResource textBoxCommands}" />
<Button Command="AlignRight" Style="{StaticResource textBoxCommands}" />
<Button Command="Undo" Style="{StaticResource textBoxCommands}" />
</StackPanel>
<RichTextBox Grid.Row="1" Name="textBox"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Window>
Essentially, what we have here is a set of buttons in one row that
operate on the
RichTextBox
below them. Here is a simple screenshot of
what this looks like:
Because of these commands, there is no actual code behind required - and
really, the buttons know virtually nothing about the rich text box. If
you grab the xaml and compile and play with it, you might notice
something really cool - the buttons even become enabled and disabled
appropriately. For instance, if you have nothing selected in the text
box, the Copy and Cut buttons will be disabled, but as soon as you
select something, the buttons will automatically enable.
The two important properties that are being set on each button are
Command
and CommandTarget
. The Command
property specifies what
command should be executed when the button is clicked. This is different
for each button, because each button causes a different command to be
executed. The Command
property can be written in two forms for built
in commands - the short form that you saw above (like
Command="Paste"
), or the long form:
Command="{x:Static ApplicationCommands.Paste}"
. The short form can
only be used for the built in commands, because WPF knows where they
are. For the commands used above, Cut
, Copy
, Paste
, and Undo
are
all in the ApplicationCommands
class, and AlignCenter
, AlignLeft
,
and AlignRight
are all in the EditingCommands
class. There are a a
couple of other built in command classes as well: NavigationCommands
,
MediaCommands
, and ComponentCommands
.
OK, now onto the
CommandTarget
. The command target specifies what
element the command is going to be executed on. In the case of these
buttons, the target is always the textBox
, which is why the target is
factored out into that textBoxCommands
style in the window resources.
If the command target is not set, the command will just bubble up in the
application until it hits some parent that watches for that command (if
there is no such parent, the command does nothing and in fact will
always be disabled). So, for instance, if you somehow place one of these
buttons inside the texbox, you wouldn't need to set the target, because
the command would bubble up and the textbox would grab it.
The only item left in the XAML above that still needs to be explained is
what is being done with the
Button
Content
property. Well, every
command has a Text
field on it which should contain a readable name
for the command, so why not use that as the text on the button instead
of explicitly setting the content to something like "Cut"? That is what
that binding is doing in the style - it is grabbing the Text
property
off of the command attached to the button, and setting that text as the
content of the button.
Well, that is all for this introduction to WPF commands. I'll be back
soon with a tutorial on how to write and use your own WPF commands - and
that is where the real power comes in.
Comments
Post a Comment