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

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube