Brett Burkhart

I am building a custom control, inherited from ListView, that contains a TextBox and a Button to handle filtering. This is a custom control with a Control Template, not a UserControl. To make this simple, forget that the control is inherited from ListView, but rather just Control. You have a TextBox and a Button. What I need is:

  1. Button should fire a click command
  2. TextBox should fire the KeyDown event to allow a user to press enter from within the TextBox only
  3. All of the commands should be wired up in the ControlTemplate in generic.xaml
  4. Have access to TextBox.Text in event handlers without having to do Template.FindName("TextBox", this)

Currently I have a custom RoutedCommand for the button, which works.

private static RoutedCommand _submitCommand;

public static RoutedCommand SubmitCommand

{

get { return _submitCommand; }

}

static CustomControl()

{

_submitCommand = new RoutedCommand("SubmitCommand", typeof(CustomControl));

CommandManager.RegisterClassCommandBinding(typeof(CustomControl), new CommandBinding(_submitCommand, OnSubmitCommand));

}

private static void OnSubmitCommand(object sender, ExecutedRoutedEventArgs e)

{

CustomControl control = sender as CustomControl;

if (control != null)

{

Button button = e.OriginalSource as Button;

if (button != null)

{

TextBox textBox = button.CommandParameter as TextBox;

if (textBox != null)

control.OnSubmit(textBox);

}

}

}

<TextBox Name="TextBox" Text="some text" />

<Button Content="Submit" Command="{x:Static local:CustomControl.SubmitCommand}" CommandParameter="{Binding ElementName=TextBox}" />

All of this code works, the user presses the button and access to the TextBox.Text is passed on to a worker method.

Now, what I need is to wire up the TextBox.KeyDown event to handle key down events from within the TextBox. Currently my solution is to wire up the TextBox.KeyDown event in C#, inside the public override void OnApplyTemplate() handler using the Template.FindName method. This all works just fine, but is this the correct way to do it The SDK talks about using commands, in place of events, for custom controls. "In the UserControl example, the RepeatButtons referred directly to event handlers defined in code. For a custom Control, a command is a more flexible way to accomplish the same behavior." See the NumericUpDown custom control sample in the SDK for more.

I wanted to apply the same methodology from the Button.Command to the TextBox so I wouldn't have to wire up the events in the CustomControl.cs. However, the TextBox does not have a Command property and wiring up the KeyDown event in the XAML, is not allowed of course, because the ControlTemplate doesn't have an associated class. So what is the best way to handle this I tried creating another RoutedCommand called KeyDownCommand and using CommandBindings to set the Command on the TextBox...

private static RoutedCommand _keyDownCommand;

public static RoutedCommand KeyDownCommand

{

get { return _keyDownCommand; }

}

private static void InitializeCommands()

{

_submitCommand = new RoutedCommand("SubmitCommand", typeof(CustomControl));

CommandManager.RegisterClassCommandBinding(typeof(CustomControl), new CommandBinding(_submitCommand, OnSubmitCommand));

_keyDownCommand = new RoutedCommand("KeyDownCommand", typeof(CustomControl));

CommandManager.RegisterClassCommandBinding(typeof(CustomControl), new CommandBinding(_keyDownCommand, OnKeyDown));

CommandManager.RegisterClassInputBinding(typeof(CustomControl), new InputBinding(_keyDownCommand, new KeyGesture(Key.Enter)));

}

private static void OnKeyDown(object sender, ExecutedRoutedEventArgs e)

{

CustomControl control = sender as CustomControl;

if (control != null)

{

TextBox textBox = e.OriginalSource as TextBox;

if (textBox != null)

{

control.OnSubmit(textBox);

}

}

}

<TextBox Name="TextBox" Text="some text">

<TextBox.CommandBindings>

<CommandBinding Command="{x:Static local:CustomControl.KeyDownCommand}" />

</TextBox.CommandBindings>

</TextBox>

What happens now is, anytime you press enter key the OnKeyDown command is fired, and the e.OriginalSource is equal to the CustomControl class. If you click into the TextBox and press the enter key, the OnKeyDown command is fired, and the e.OriginalSource is equal to the CustomControl class, not the TextBox.

So how should this be done How do you handle other events like GotFocus or TextChanged, or other events on controls that don't have a Command property Do you wire up the events in the C#, or do they become RoutedCommands, and if so, how do you wire them up to a specific control so the OriginalSource executing the command is known

Sample of wiring up events in OnApplyTemplate event...

public override void OnApplyTemplate()

{

// Implementers should always call the base implementation before their own implementation.

base.OnApplyTemplate();

// Get a reference to the filter TextBox and wire up event handlers.

TextBox textBox = (FrameworkElement)Template.FindName("TextBox", this) as TextBox;

if (textBox != null)

{

// wire up KeyDown event to handle Key.Enter commands from the text box.

textBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown);

// wire up focus events to handle hide/show of default caption text.

textBox.GotFocus += new RoutedEventHandler(OnTextBoxGotFocus);

textBox.LostFocus += new RoutedEventHandler(OnTextBoxLostFocus);

}

}



Re: Windows Presentation Foundation (WPF) Custom Controls best practice: Commands vs. Events

Brett Burkhart

What I'd like to have, for example, and for other events is in the generic.xaml ControlTemplate for my custom control:

<TextBox Name="TextBox" Width="200" KeyDown="{x:Static local:CustomControl.OnTextBoxKeyDown}" />

...and the following in the class file...

private static void OnTextBoxKeyDown(object sender, KeyEventArgs e)

{

}

...this throws a compile error...

"Error 1 'ResourceDictionary' root element requires a x:Class attribute to support event handlers in the XAML file. Either remove the event handler for the KeyDown event, or add a x:Class attribute to the root element."

This kind of touches on the subject of, where do you handle wiring...in the XAML or in the C#. To each his own, but you should be able to do it either place whether it's a custom control or a control in a window.

How do you wire up an event, for a custom control, in the XAML That's basically the big question here.





Re: Windows Presentation Foundation (WPF) Custom Controls best practice: Commands vs. Events

Marco Zhou - MSFT

Hi Brett,

When you author custom controls, the recommended way to wire up events is to override OnApplyTemplate() method, and use this.Template.FindName(name, this) or base.GetTemplatedChild(name) method to get reference to control you need, and wire up events there. Setting event handlers directly in XAML is not a good idea both from the extensibility / customization point of view, and from abstraction point of view. In my humble opinion, when authoring custom controls, you should make a clear seperatation between the visual aspect and behavioural aspect of your control. XAML should be only used for defining the visual appearance of your control, and leave all those logic and behaviour as part of control's implementation details. I think the built-in control library shipped with WPF is a good example of how custom control should be authored. Let's take TextBox for example, here is the default control template for TextBox :

Code Block

<ControlTemplate TargetType="TextBoxBase"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Border

BorderThickness="{TemplateBinding Border.BorderThickness}"

BorderBrush="{TemplateBinding Border.BorderBrush}"

Background="{TemplateBinding Panel.Background}"

Name="Bd"

SnapsToDevicePixels="True">

<ScrollViewer

Name="PART_ContentHost"

SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />

</Border>

<ControlTemplate.Triggers>

<Trigger

Property="UIElement.IsEnabled">

<Setter

Property="Panel.Background"

TargetName="Bd">

<Setter.Value>

<DynamicResource

ResourceKey="{x:Static SystemColors.ControlBrushKey}" />

</Setter.Value>

</Setter>

<Setter

Property="TextElement.Foreground">

<Setter.Value>

<DynamicResource

ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />

</Setter.Value>

</Setter>

<Trigger.Value>

<s:Boolean>False</s:Boolean>

</Trigger.Value>

</Trigger>

</ControlTemplate.Triggers>

</ControlTemplate>

From the above ControlTemplate definition, you will find that there is no application logic here. All the behaviour and logic (such keyboard handling, caret management, document management) is abstracted away from the ControlTemplate defnition. This has a number of benefits, for instance you can change the appearance of TextBox with any type of crazy look as long as you obey the contract of

"PART_ContentHost". TextBox enforces this contract through TemplatePartAttribute custom attribute, the presence of "PART_ContentHost" can ensure the correct behaviour of TextBox.

TextBox is just one tip of iceberg, many WPF controls follow this design philosophy. As custom control author, if you want your control to be fully customizable and extensible just as the built-in framework controls are, you'd better follow this design pattern as well.

As to the Commands vs Events question. As I understand it, commands and events are closely related but they convey different semantical meaning. Commands define the semantics of a specified action, whereas events define the handling logic of an action. To put it another way, commands tell us what a particular action is, but they don't tell us when and how to handle this action. Commands leave the handling of an action to application developers. This has many benefits, for example, many application supports copy, cut, and paste, and those actions are abstracted away as commands. which means that you can invoke copy, cut or paste action either through keystrokes or through the tool bar buttons or through the menus. those command sources (aka keystrokes, tool bar buttons, menu items) don't need to care about how the application handles the copy, cut, or paste operation.

Back to your custom control example, you want your custom control to expose KeyDownCommand. I think this probably is not what command is designed for, Because KeyDown is too specific to the action when users hit the keyboard. It probably doesn't made sense to the semantics of your custom control. Let me know if you agree with me on this





Re: Windows Presentation Foundation (WPF) Custom Controls best practice: Commands vs. Events

Brett Burkhart

Thanks, that is exactly what I figured as far as overridding the OnApplyTemplate() and wiring up the events there. I guess my concern, for fully customizable controls, how do you know that the control you are wiring up to doesn't get removed in a customized ControlTemplate...and it sounds like the answer is using the TemplatePartAttribute tags to inforce that they do in fact exist. Cool, I always wondered about those tags during control customization but never fully dove into the specifics of them. Nice to know. Thanks for your guidance, exactly what I was looking for.

As far as Commands vs. Events, I think we are in agreement. I'm of the mindset that I don't like seeing developers wire up event handlers in the XAML. That's me personally. I like XAML for layout and C# for everything else. And it looks like MS has taken the compiler far enough to check that any event wiring in XAML does in fact exist in the Class file. Working with WPF way back in Beta, that check didn't exist and we ran into instances where someone would remove the event wiring in the XAML by mistake and you never knew until runtime. Dooh!