Practical .NET

Extending XAML Applications with Custom Commands

In WPF and Silverlight, you can separate your UI logic into a set of Command classes that facilitate loose coupling, testable designs, and reusability.

An ideal application would remove all coding logic from the presentation layer (which requires human beings to test it) and put it in standalone classes (which can be automatically tested), but the process would make it easy to connect those classes to your UI. XAML's ability to create custom commands and attach them to UI components provides that solution.

For instance, assume that you have a business rule that requires that, if a customer buys a specific product, then they must buy another product (e.g., a warranty). While this rule should be enforced in the business layer (model) for the application, it would be convenient to offer the user a "fix order" button in the UI. The logic behind this button is sufficiently complex that I'd want to be able to test it without having to bang test cases into the application. Commands let me do that.

Creating a Command Class
My first step is to create a "command" class with two methods required by XAML:
  • A method that implements the command—the code that will fix the orders, in this case
  • A method that returns True if the user should be allowed to execute the first method, and False otherwise

In this application, the UI is driven by a collection of Product objects that integrates with a WPF or Silverlight grid. My command method (the one that fixes the orders) will accept that collection of Products and fix any problems in the order (including adding or removing products). I can call the class and the method anything I want and have the method accept any parameters I need; but to integrate with the WPF Command framework, the method must be declared as Shared/static:

Public Class ProductManagement    
    Public Shared Sub CheckForProblems(prods As ObjectModel.ObservableCollection(Of Product))
        '…code to fix order…
    End Sub

For the method that decides if the command can be executed, I'll write a method that examines the collection of orders and returns True if there's anything that needs fixing. Again, the only requirement is that the method be declared Shared/static:

Public Shared Function CheckForProblems(
          prods As ObjectModel.ObservableCollection(Of Product) _
            ) As Boolean
    Dim ProblemFound As Boolean
    '…check for problem and set ProblemFound…
    Return ProblemFound
  End Function

I can now test these methods by creating test cases that pass a collection of Products to either method and check the method's results. Once I know that my command class is working correctly, I can integrate it with (for instance) a WPF form.

Wiring up the Command Class
To integrate my command class with a XAML form, I first declare a Shared/static field at the top of the form's code file that holds a RoutedCommand object (the RoutedCommand handles the communication between the form and my class):

Public Shared WireUpChannel As New  RoutedCommand() 

In the Load event, I create a CommandBinding that ties the RoutedCommand to two events in the code file. WPF fires the first event when the user wants to execute my command (in this case, to fix the orders) and fires the second event when determining if the user should be allowed to execute my command. I can call these events anything I want, also:

Me.CommandBindings.Add(New CommandBinding(WireUpChannel,
                 AddressOf ExecuteCommand, AddressOf EnableCommand))

In the first event, I call my command event, passing whatever parameters are required (in this case, a variable called prods that holds the collection of Products):

Public Sub ExecuteCommand(sender As Object, e As ExecutedRoutedEventArgs)
    ProductManagement.FixProblems(prods)
  End Sub

The second event is passed, in its e parameter, an object with a property called CanExecute that I must set to True when the user is allowed to execute the command. So, in that event, I call the other method in my command class and use its output to set the CanExecute property:

 Public Sub EnableCommand(sender As Object, 
               e As CanExecuteRoutedEventArgs)
    e.CanExecute = ProductManagement.CheckForProblems(prods)
  End Sub

With those four lines of code I've wired my command class' methods into my UI. The only step left is to let the user trigger the command from the UI by clicking the button. In my XAML file, I declare an alias for the project's namespace that I can use later:

 <Window 
    xmlns:w="clr-namespace:DataBoundIntegration">	

And the final step is to tie the button to the field I declared earlier, using my project's namespace, the name of my form (Products) and the field I declared in the form's code file:

<Button  Command="{x:Static w:Products.WireUpChannel}" …

Not only will clicking the button now execute my command method, WPF will automatically enable and disable the button based on the value returned from my CheckForProblems method, called through the EnableCommand event. I've moved my UI logic into a set of testable classes that I might even find useful in a related application.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube