Category Archives: WPF

The “Custom” control type and WinAppDriver/Appium

So you’ve and application that you want to UI automation test using WinAppDriver/Appium. You’ve got a property grid with the left hand being the text/label and the right hand being the editor. You decided that a cool way to change values on the edit controls is to inspect what the ControlType is, then customise the code to SendKeys or Click or whatever on those controls.

Sound fair?

Well all this is great if your controls are not (as the title of this post suggests) “Custom” controls. So for WPF this is a UserControl or Control. This is fine if we have a single custom control but no so good if we have multiple custom control types.

This issue raise it’s head due to a HorizontalToggle control which we’re importing into our application via a NuGet package. The control derives from Control and is pretty much invisible to the UI Automation code apart from one Automation Id “SwithThumb”. So to fix this I wrapped the control in a UserControl and added an AutomationProperties.AutomationId attached property. Ofcourse, we could get the source if it’s available and change the code ourselves, but then we’ll have to handle upgrades etc. which may or may not be an issue in the future.

That’s great, now I can see the control but I have some generic code that wants to know the control type, so what can we do on this front?

The truth is we’re still quite limited in what we can do, if we’re getting all elements and trying to decide what to do based upon the ControlType. TextBoxes are Edit control types, Buttons are Button control types, but UserControls are still Custom control types.

Whilst this is NOT a perfect solutions, we can derive a class from a UserControl (which will still be used to wrap the original control), let’s call ours HorizontalToggleControl and it looks like this

public class HorizontalToggleControl : UserControl
{
   protected override AutomationPeer OnCreateAutomationPeer() => 
      new HorizontalToggleControlAutomationPeer(this);
}

What we’re doing here is taking over the OnCreateAutomationPeer and supplying our own automation peer, which will itself allow us to override some of the automation properties, specifically in our case the GetAutomationControlTypeCore.

My HorizontalToggleControlAutomationPeer class looks like this

internal class HorizontalToggleControlAutomationPeer : 
   UserControlAutomationPeer
{
   public HorizontalToggleControlAutomationPeer(UserControl owner) :
      base(owner)
   {
   }

   protected override AutomationControlType GetAutomationControlTypeCore() => 
      AutomationControlType.Thumb;

   protected override string GetLocalizedControlTypeCore() =>
      nameof(HorizontalToggleControl);

}

Now what’s happening in the above code is the we’re creating a localized control name “HorizontalToggleControl”, ofcourse this could literally be localised and read from the resources, but in our case we’re sticking with the actual control name. This, unfortunately is still no use to us as the ControlType in an element will still read as Custom. Changing the GetAutomationControlTypeCore return value fixes this but at the expense of only being able to set the control type to one of the AutomationControlType enums. So it’s of limited use, but as mentioned previously, we only really see the SwitchThumb automation id on the original control and so, Thumb seemed like a possible control type. In reality we might prefer CheckBox, but ofcourse the downside here is if we have check box elements, we’d need to ensure we also look at the automation name or property to determine what type of check box this is, a real Windows one or one that acts like a check box. Either way of doing this is fine.

Is your Universal Windows application running on a device which supports this hardware ?

Just going through some old draft posts and found this one, which might be of use to somebody. Let’s call it a Quick Post as there’s not too much substance…

When writing a Universal Windows application we’re basically trying to write code that will work on multiple devices. But different devices have different capabilities. For example a mobile phone has a back button, so we might want to handle the back button BackPressed event in some way, but this event is not available when the application is run on a desktop machine.

Obviously it’d be no good using #define to enable/disable code as we want the application’s code to be universal and run “as-is” on multiple devices. So we need a method call at runtime to tell us whether the device supports the BackButton. Or more specifically whether it supports the HardwareButtons input mechanism.

So to check whether we can hook up code to the BackPressed event we might code the following

if(ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
   HardwareButtons.BackPressed += HandleBackPressed;
}

I want row automation id’s on my XamDataGrid…

As part of work I’m doing at the moment, building a UI automation API for our testers. I continually come across issues around data grid controls and access the rows within it (we’re primarily using XamDataGrids from Infragistics).

What I need is to have an AutomationId reflecting some form of index in the grid. Good news is we can do this…

If we take a XamaDataGrid and create a style such as this

<Style x:Key="RowPresenterStyle" TargetType="igDP:DataRecordPresenter">
  <Setter Property="AutomationProperties.AutomationId" Value="{Binding DataItemIndex}" />
</Style>

and now in the XamDataGrid’s FieldLayoutSettings we can apply this style using

<igDP:XamaDataGrid.FieldLayoutSettings>
  <igDP:FieldLayoutSettings
     <!-- Other settings -->
     DataRecordPresenterStyle="{StaticResource RowPresenterStyle}" />
</igDP:XamaDataGrid.FieldLayoutSettings>

A few tips and tricks for using Infragistics XamDataGrid

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

I’m working on a project using the XamDataGrid. I’ve used the UltraWinGrid by Infragistics in the past but that doesn’t help at all when moving code from Windows Formds to WPF. So here’s a list of a few commonly required features and how to do them using the XamDataGrid.

Note: this post only refers to using version 12.2

Grid lines

Problem: By default there are no grid lines on the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style TargetType="{x:Type igDP:CellValuePresenter}">
   <Setter Property="BorderThickness" Value="0,0,1,1" />
   <Setter Property="BorderBrush" Value="{x:Static SystemColors.ControlLightBrush}" />
</Style>

Obviously replace the BorderBrush colour with your preferred colour.

Remove the group by area

Problem: I want to remove the group by section from the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style x:Key="IceGrid" TargetType="igDP:XamDataGrid">
   <Setter Property="GroupByAreaLocation" Value="None" />
</Style>

don’t forget to apply the style to your grid, i.e.

<dp:XamDataGrid Style="{StaticResource IceGrid}" DataSource="{Binding Details}">
   <!- Your grid code -->
</dp:XamDataGrid>

Column formatting

Problem: We want to change the numerical formatting for a column

Solution: We can set the EditorStyle for a field (editor doesn’t mean it will make the field editable)

<dp:XamDataGrid.FieldLayouts>
   <dp:FieldLayout>
      <dp:FieldLayout.Fields>
         <dp:Field Name="fee" Label="Fee" Width="80">
            <dp:Field.Settings>
               <dp:FieldSettings>
                  <dp:FieldSettings.EditorStyle>
                     <Style TargetType="{x:Type editors:XamNumericEditor}">
                        <Setter Property="Format" Value="0.####" />
                     </Style>
                  </dp:FieldSettings.EditorStyle>
               </dp:FieldSettings>
           </dp:Field.Settings>
        </dp:Field>         
      </dp:FieldLayout.Fields>
   </dp:FieldLayout>
</dp:XamDataGrid.FieldLayouts>

This code creates a field named fee and with the label Fee and the editor is set to only display decimal places if they actually exist.

As we’re defining the fields you’ll need to turn off auto generation of fields, as per

<dp:XamDataGrid.FieldLayoutSettings>
   <dp:FieldLayoutSettings AutoGenerateFields="False" />
</dp:XamDataGrid.FieldLayoutSettings>

Handling “unhandled” exceptions in WPF

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

None of us want our applications to just crash when an exception occurs so we often ensure we’ve got catch blocks around possible exceptions, but ofcourse, sometimes we either don’t care to handle an exception explicitly or we forget to code the catch block or the place the exception occurs such that it’s not possible to handle in such a structured way. In such scenarios we want to handle all “unhandled” exceptions at the application level.

Let’s take a look at some of the ways to handle exceptions in a WPF application. Here’s a list some of those ways to handle “unhandled” exceptions.

AppDomain.UnhandledException

The AppDomain.UnhandledException or more specifically AppDomain.CurrentDomain.UnhandledException.

Application.DispatcherUnhandledException

The Application.DispatcherUnhandledException or more specifically the Application.Current.DispatcherUnhandledException

Dispatcher.UnhandledException

The Dispatcher.UnhandledException or more specifically Dispatcher.CurrentDispatcher.UnhandledException

AppDomain.FirstChanceException

The AppDomain.FirstChanceException or more specifically AppDomain.CurrentDomain.FirstChanceException.

TaskScheduler.UnobservedTaskException

The TaskScheduler.UnobservedTaskException

WPF Validation methods

How do we handle validation in WPF ?

Before we begin…

Let’s start by looking at the view model we’re going to work on and write our validation code for – this is a very simple model with a single property “Number” which could ofcourse represent anything you like.

public class NumberViewModel : INotifyPropertyChanged
{
   // standard implementation of INotifyPropertyChanged removed 
   private int number;

   public int Number
   {
      get { return number; }
      set
      {
         if (number != value)
         {
            number = value;
            OnPropertyChanged("Number");
         }
      }
   }
}

You can assume that OnPropertyChanged fires the INotifyPropertyChanged.PropertyChanged event, but I’ve removed the implementation for brevity.

We’ll be equally simple with our UI, which has the following XAML

<TextBox Text="{Binding Number}" />

Good old IDataErrorInfo

So if you’ve used IDataErrorInfo in WinForms, this will be very familiar to you. We can simple implement the IDataErrorInfo interface in our NumberViewModel and add something like the following

string IDataErrorInfo.Error
{
   get { return null; }
}

string IDataErrorInfo.this[string columnName]
{
   get
   {
      if (columnName == "Number")
      {
         if (number < 0)
            return "Number must be greater or equal to 0";
      }
      return null;
   }
}

To get the WPF binding to actually interact with the IDataErrorInfo we need to change the XAML to look like this

<TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

In the above our IDataErrorInfo.this indexer will be called each time the property changes, at which time we can handle the validation either within the view model or ofcourse via some other validation rule class.

ValidationRule validation

An alternative to the IDataErrorInfo route for validation are ValidationRules. A ValidateRule implementation of the previous validator is listed below

public class PostiveValidationRule : ValidationRule
{
   public override ValidationResult Validate(object value, CultureInfo cultureInfo)
   {
      int result;
      if (value != null && Int32.TryParse(value.ToString(), out result))
      {
         if (result < 0)
            return new ValidationResult(
               false, "Number must be greater or equal to 0");
      }
      return ValidationResult.ValidResult;
   }
}

To use the above rule in XAML we write the following

<TextBox>
   <TextBox.Text>
      <Binding Path="Number" 
            ValidatesOnDataErrors="True" 
            UpdateSourceTrigger="PropertyChanged">
         <Binding.ValidationRules>
            <validators:PostiveValidationRule />
         </Binding.ValidationRules>
      </Binding>
   </TextBox.Text>
</TextBox>

The ValidationRules (as the pluralism suggests) can have multiple rules listed.

INotifyDataErrorInfo

.NET 4.5 included the INotifyDataErrorInfo. This allows us to validate in a more asynchronous way in that we can raise the ErrorsChanged event when we have errors and report them so that the binding engine can then call the GetErrors method to get the list of errors.

BindingGroup

The previously highlighted validation methods tend to be aimed more at a specific view model, but what if our view is made up of multiple view models and we want to validate across them all. Then we can look to use the BindingGroup.

The BindingGroup creates a relationship between multiple bindings, which can be validated and updated together.

To put it another way, the BindingGroup allows us to validate a group of bindings at the same time. Our sample only has a view model but if we had multiple view models it could validate all the items that make up a BindingGroup. Let’s look at how we could create a BindingGroup.

<StackPanel>
   <StackPanel.BindingGroup>
     <BindingGroup Name="ValidDataGroup">
        <BindingGroup.ValidationRules>
           <validators:ValidDataValidationRule />
        </BindingGroup.ValidationRules>
     </BindingGroup>
   </StackPanel.BindingGroup>
   <TextBox Text="{Binding Text, ValidatesOnDataErrors=True, BindingGroupName=ValidDataGroup}" />

We give the BindingGroup a name and then we can assign this name to the various bindings – in this example we don’t actually need to use the group name on the textbox as it will be used within the validation, but hopefully you can see how the syntax would look.

Now let’s take a look at the ValidDataValidationRule code

public class ValidDataValidationRule : ValidationRule
{
   public override ValidationResult Validate(object value, CultureInfo cultureInfo)
   {
      var bindingGroup = (BindingGroup)value;
      if (bindingGroup != null)
      {
         var numberViewModel = bindingGroup.Items[0] as NumberViewModel;
         if (numberViewModel != null)
         {
            if (numberViewModel.Number < 0)
            {
               return new ValidationResult(
                  false, 
                  "Number must be greater or equal to 0");
            }
         } 
      }
      return ValidationResult.ValidResult;
   }		
}

The Items collection will contain the various groups that have been assigned the BindingGroup name and then we can validate across all the data contexts.

Unfortunately, this doesn’t just happen magically. Instead, we need to invoke it. If we give the name DataElement to the StackPanel and for the sake of simplicity we add a button with a Click handler, we can then call the BindingGroup’s CommitEdit method to force validation, i.e.

DataElement.BindingGroup.CommitEdit();

Note: The default style is to draw a read line around the control which fails validation, if you’ve placed the StackPanel as the top level Window you might need to add a margin to see the red border.

ValidationStep

On both the BindingGroup and ValidationRule we can set the ValidationStep property. This allows us to tell the binding mechanism at what stage it should invoke the validation rule.

This can be set to one of four values

CommittedValue: Runs the ValidationRule after the value has been committed to the source.
ConvertedProposedValue: Runs the Validation rule after a value is converetd.
RawProposedValue: Runs the validation before any conversion occurs.
UpdatedValue: Runs the ValidationRule after the source is updated.

Note: The above definitions were taken from ValidationStep Enumeration

Data annotations

Finally, let’s take a look at data annotations or more specifically validation attributes that are part of the System.ComplonentMode.DataAnnotations namespace.

With the data annotations we can apply attributes to a class or its members which denote the validation rules to be used. For example

public class ValidationModel
{
   [Required(ErrorMessage = "First name is a required field")]
   public string FirstName { get; set; }
}

In this example we’ve removed the get/set actual implementation, for brevity.

Now this code requires use to write code to validation the property, for example in our setter we might have code like this

var validationContext = new ValidationContext(this, null, null);
validationContext.MemberName = nameof(FirstName);
Validator.ValidateProperty(value, validationContext);

Enhancing the user interface

So far, I’ve not done anything to make the user experience any better regarding validation, i.e. you will only see a red border around our text box, but this doesn’t really help the user identify what’s gone wrong, so now let’s look at how we might change our UI to better reflect the validation failures.

Ofcourse, we’ve been busy creating error messages, but so far not shown them in the UI, so we could use something like the following

<TextBox Text="{Binding Number, 
      UpdateSourceTrigger=PropertyChanged, 
      ValidatesOnDataErrors=True}">
   <Validation.ErrorTemplate>
      <ControlTemplate>
         <StackPanel>
            <AdornedElementPlaceholder />
            <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
         </StackPanel>
      </ControlTemplate>
   </Validation.ErrorTemplate>
</TextBox>

This will display the first error message just beneath the TextBox.

Another alternative is to style the text box with a trigger against the Validation.HasError property

<Style TargetType="{x:Type TextBox}">
   <Style.Triggers>
      <Trigger Property="Validation.HasError" Value="True">
         <Setter Property="Background" Value="Red" />
          <Setter Property="ToolTip"
             Value="{Binding RelativeSource={x:Static RelativeSource.Self},
             Path=(Validation.Errors)[0].ErrorContent}"/>
      </Trigger>
   </Style.Triggers>
</Style>

References

Data validation in WPF
Using BindingGroups For Greater Control Over Input Validation
BindingGroups For Total View Validation
WPF 3.5 SP1 Feature: BindingGroups with Item-level Validation
Validation in Windows Presentation Foundation

CommunityToolkit.Mvvm “already contains a definition for” error

The CommunuityToolkit.Mvvm includes a wonderful MVVM source generator, but in the current .NET 6.0 release I’m using, (doesn’t happen with MAUI but does with WPF) I get errors such as

  • The type ‘MyViewModel’ already contains a definition for ‘MyProperty’
  • A partial method may not have multiple defining declarations

To fix this (at least in the current version of .NET 6.x) add a global.json file to the folder with your solution file

{
  "sdk": {
    "version": "6.0.202",
    "rollForward": "disable"
  }
}

and things should work correctly – hopefully this post will be obsolete soon with the issue fixed, but for now this solves the problem.

WPF UserControl DependencyProperty Binding

On the weekend I was messing around creating a simple WPF card game application and wanted to just quickly knock together a UserControl with a dependency property (actually there were several controls and several dependency properties) which allows the hosting Window or UserControl to change the property.

Ofcourse we should probably look at implementing this as a lookless control but I was just trying to do the bare minimum to get this working as a proof of concept.

So what I want is UserControl which displays playing card, so let’s call it CardView and it looks like this (excluding the UserControl element for brevity)

<Grid>
   <Border Background="White" Padding="8" CornerRadius="8">
      <Image Source="{Binding CardImage, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
      <Border.Effect>
         <DropShadowEffect />
      </Border.Effect>
    </Border>
</Grid>

and the code behind looks like this

public partial class CardView : UserControl
{
   public static readonly DependencyProperty CardImageProperty = DependencyProperty.Register(
      nameof(CardImage), typeof(Uri), typeof(CardView), new PropertyMetadata(default(Uri)));

   public Uri CardImage
   {
      get => (Uri)GetValue(CardImageProperty);
      set => SetValue(CardImageProperty, value);
   }

   public CardView()
   {
      InitializeComponent();
   }
}

The key thing here is that we have this code-behind dependency property CardImage, and in the XAML we want to show the image of the card that was set through the dependency property.

If this was a lookless control we’d have set this binding up like this

RelativeSource={RelativeSource TemplatedParent}

but there’s no separation here of style and code, instead everything’s in the UserControl, so we have to use

RelativeSource={RelativeSource AncestorType=UserControl}

This will then bind the UserControl Image to the dependency property in code-behind and allow our host Window or UserControl to simply do the following

<local:CardView Width="140" Height="200" 
   CardImage="../Resources/HQ.png"/>

For this example, the png’s will be stored in a Resources folder with their Build Action set to Resource.

IValueConverter.Convert return values

I was updating some value converters in my converters GitHub repos. PutridParrot.Presentation.Converters and realised I had been returning a null in situations where I really should have been returning DependencyProperty.UnsetValue.

Generally we’ll either return a valid value from a converter or one of the following

  • null
  • DependencyProperty.UnsetValue
  • Binding.DoNothing
  • The original value

The documentation (see References) states that when a null is returned then a “valid null value is used”.

However we may actually prefer that the FallbackValue is used (if supplied) instead, hence we can return a DependencyProperty.UnsetValue.

In cases where we neither want a value returned and do not want the FallbackValue to be called, then we return the Binding.DoNothing.

Obviously, in situations where you do not know what to do with a binding value, it might be better to simply return the value passed into the Convert method.

References

IValueConverter.Convert(Object, Type, Object, CultureInfo) Method

The WPF Hyperlink

WPF comes with a HyperLink class, but it’s from the System.Windows.Documents namespace, so is mean’t to be embedded within a textual control, such as a TextBlock (i.e. it’s not a standalone control like the System.Windows.Forms.LinkLabel) and within WPF it doesn’t support opening the supplied Uri in an associated process.

Let’s see it’s usage in some XAML

<TextBlock>
   <Hyperlink NavigateUri="http://putridparrot.com/blog/the-wpf-hyperlink">
   The WPF Hyperlink
   </Hyperlink>
</TextBlock>

This XAML will display a the text “The WPF Hyperlink” with underline and when you move your mouse over the link it’ll change colour and the mouse cursor will change to the hand cursor, but clicking on the link will not result in any web browser (or other associated application opening). We must implement such functionality ourselves either responding to the Click event or supplying an ICommand to the Command property.

Here’s a simple example of the sort of code we’d use to running the Uri via it’s associated application.

var process = new Process
{
   StartInfo = new ProcessStartInfo(Uri)
};
process.Start();

We can use databinding to supply the NavigateUri in the standard way, but if you wanted to change the displayed text, then we need to embed another control into the Hyperlink. For example, here we’re using another class from the System.Windows.Documents namespace, the Run class

<TextBlock>
   <Hyperlink NavigateUri="{Binding Uri}">
      <Run>
         <Run.Text>
            <Binding Path="DisplayText"/>
         </Run.Text>
      </Run>
   </Hyperlink>
</TextBlock>