Just some fun with Avalon

Did you know that the text editor in Avalon is currently implemented on the commanding architecture? Most of the keyboard stuff goes through commands - inserting text is a very interesting topic I'll save for another post.

Commands are typically exposed as entries on some class or another. In this case, we have commands on EditingCommand and ApplicationCommand. I built a little sample app that lists these commands and lets you fire them. Along the way, we'll also learn a bit about the cool stuff you can do with data binding (interesting article over here).

First, let's create a new project and add the following XAML to the window. (note: all this code is based on the May bits)

<Window x:Class="VancouverApp.Window1"
xmlns="https://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005"
Text="My Vancouver App"
>
<DockPanel>
<Button DockPanel.Dock="Top"
Click="DisplayButtonClick">Display Commands</Button>
<ListBox DockPanel.Dock="Left"
Name="CommandsListBox" PreviewKeyDown="CommandsKeyDown"/>
<Button DockPanel.Dock="Bottom"
Click="RunCommandClick">Run Command</Button>
<RichTextBox Name="EditingBox" />
</DockPanel>
</Window>

Here we have a couple of Button controls, a ListBox, and a RichTextBox we will fire the commands on.

Now, let's go to the code-behind file and start adding our event handlers.

private void DisplayButtonClick(object sender, RoutedEventArgs e)
{
// Build a list of commands we'll use.
List<RoutedCommand> commands = new List<RoutedCommand>();
AddCommandsFromType(typeof(EditingCommands), commands);
AddCommandsFromType(typeof(ApplicationCommands), commands);

  // Now, let's decide how we want RoutedCommands to look like...
FrameworkElementFactory contentFactory = new FrameworkElementFactory(typeof(TextBlock));
contentFactory.SetBinding(TextBlock.TextContentProperty, new Binding("Name"));

  // ... and make sure that that's the template that's used.
DataTemplate commandTemplate = new DataTemplate(typeof(RoutedCommand));
commandTemplate.VisualTree = contentFactory;

  // Wire everything and we're ready to go!
CommandsListBox.ItemsSource = commands;
CommandsListBox.ItemTemplate = commandTemplate;
}

This populates the ListBox with a bunch of commands, using a helper AddCommandsFromType. AddCommandsFromType, will add all static properties that are of type RoutedCommand (which is how these commands are exposed). Note how C# generics make the code straightforward and type-safe, by the way.

A little bit of Reflection will come in handy here. If I were writing a real application, I wouldn't blinding go and pick all RoutedCommands, but I'm lazy (which is ocassionally a good trait in developers).

private static void AddCommandsFromType(Type type, List<RoutedCommand> commands)
{
foreach (PropertyInfo property in type.GetProperties())
{
// Skip any non-static properties, as we don't have an instance.
if (!property.GetGetMethod().IsStatic)
{
continue;
}

    // Get the property and see if it's a valid RoutedCommand.
RoutedCommand command = property.GetValue(null, null) as RoutedCommand;
if (command != null)
{
commands.Add(command);
}
}
}

The handlers for firing the command are super-straightforward.

private void RunCommandClick(object sender, RoutedEventArgs e)
{
// The selected item will be the underlying command we put
// in the list, not the generated TextBlock - whee!
RoutedCommand selectedCommand = CommandsListBox.SelectedItem as RoutedCommand;
if (selectedCommand != null)
{
selectedCommand.Execute(EditingBox);
}
}

private void CommandsKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
RunCommandClick(null, null);
}
}

Now, to play with this, run the sample, write up some content and go about selectin items and pressing Enter. This will trigger the actions on the RichTextBox. Note that the text selection is invisible because the list box takes focus.

As usual, everything is subject to change, no warranties implied, etc. - but it's still fun to play with this, and to see how straightforward it is to put things together in Avalon.

 

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.