Cheat Sheet for Path Markup Syntax

Path Markup Syntax provides a mini-language for describing complex collection of lines and curves. Make no mistake when describing it as mini-language – It is quite powerful and could reduces a lot of nested collection of Xaml elements into a single line.

Having said so, I personally favour the Xaml Element way of constructing elements, mainly due to readability factor. But the language is quite useful in its own ways.

Here is a little cheat sheet for Path Markup Syntax to aid your development cause.

Cheat Sheet Path Markup Syntax

Why be wary of Value Coercion in Dependency Properties

If you are not quite familiar with Value Coercion, it allows you to change/correct value of a property, when it is assigned an unexpected value. This also allows you to ensure relative properties are also kept in sync or in other words, allows you to enforce relation between properties of an object. For example, Date of Birth should not exceed Date of Demise, or Minimum Value in a slider should not exceed the Maximum Value.

You could achieve such a synchronization mechanism using Value Coercion. Consider the following code.

// Note that code has a 'little problem', we will discuss it shortly.
public int MinimumValue
{
    get { return (int)GetValue(MinimumValueProperty); }
    set { SetValue(MinimumValueProperty, value); }
}

public static readonly DependencyProperty MinimumValueProperty = 
    DependencyProperty.Register(nameof(MinimumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0, null, new CoerceValueCallback(OnMinimumValueCoerce)));

private static object OnMinimumValueCoerce(DependencyObject d, object baseValue)
{
    if(d is Configuration config && baseValue is int newValue)
    {
        var maxValue = config.MaximumValue;
        var oldValue = config.MinimumValue;
        return maxValue > newValue ? newValue : oldValue;
    }
    return baseValue;
}

public int MaximumValue
{
    get { return (int)GetValue(MaximumValueProperty); }
    set { SetValue(MaximumValueProperty, value); }
}

public static readonly DependencyProperty MaximumValueProperty =
    DependencyProperty.Register(nameof(MaximumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0,null,new CoerceValueCallback(OnMaximumCoerce)));

private static object OnMaximumCoerce(DependencyObject d, object baseValue)
{
    if(d is Configuration config && baseValue is int newValue)
    {
        var minValue = config.MinimumValue;
        var oldValue = config.MaximumValue;
        return newValue > minValue ? newValue : oldValue;
    }
    return baseValue;
}

In the above code, you are using the CoerceValueCallback within the PropertyMetada to ensure MinimumValue and MaximumValue are kept in sync with each other. Every time a new value is assigned for MinimumValue, it checks if the value is exceeds the MaximumValue. If yes, it reverts the changes and retains the old value.

Little Problem

While the above code looks seemingly harmless, there is a small problem associated with. If you examine the code, we have coercing values for both MinimumValue and Maximum value. Now imagine a scenario when are using the Dependency properties in your code.

<controls:Configuration MinimumValue="{Binding MinValue}"  MaximumValue="{Binding MaxValue}" />

Consider the initial values of MinValue and MaxValue are 1 and 100. At the first glance, this looks valid value, however if you execute the code, you would realize the MinimumValue nevers gets set. Instead, the OnMinimumValueCoerce comes into play and reverts the changes. This is becase, at first the MinimumValue is set (by the order in which is defined in our Xaml – Change the order and behavior would be different).

When a value 1 is assigned to MinimumValue, the OnMinimumValueCoerce notices that it exceeds the current value of MaximumValue (which hasn’t changed yet and is still default of 0). This causes the values to reverts as per our logic in OnMinimumValueCoerce.

PropertyChangedCallback Vs CoerceValueCallback Vs ValidateValueCallback

At this point, it is worth noticing the difference between the 3 seemingly similiar callbacks associated with DependencyProperty – PropertyChangedCallbackCoerceValueCallback and ValidateValueCallback.

The difference could be summarized as

  • PropertyChangedCallback – Reacts to a value change
  • ValidateValueCallback – Determine if the value is valid
  • CoerceValueCallback – Coerce a value.

Order of execution

  1. ValidateValueCallback
  2. CoerceValueCallback
  3. PropertyChangedCallback

Solution

One solution that could be applied in this scenario is

  • Allow MinimumValue to be set to any value
  • In PropertyChangedCallback of MinimumValue, force coercion of MaximumValue.
  • In CoerceValueCallback of MaximumValue, check if it is less than MinimumValue and set it to MinimumValue.

For example,

public int MinimumValue
{
   get { return (int)GetValue(MinimumValueProperty); }
   set { SetValue(MinimumValueProperty, value); }
}

public static readonly DependencyProperty MinimumValueProperty = 
   DependencyProperty.Register(nameof(MinimumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0, new PropertyChangedCallback(OnMinimumValueChanged)));

private static void OnMinimumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
   if(d is Configuration config)
   {
       config.CoerceValue(MaximumValueProperty);
   }
}

public int MaximumValue
{
   get { return (int)GetValue(MaximumValueProperty); }
   set { SetValue(MaximumValueProperty, value); }
}

public static readonly DependencyProperty MaximumValueProperty =
   DependencyProperty.Register(nameof(MaximumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0,null,new CoerceValueCallback(OnMaximumCoerce)));

private static object OnMaximumCoerce(DependencyObject d, object baseValue)
{
   if(d is Configuration config && baseValue is int newValue)
   {
       var minValue = config.MinimumValue;
       return newValue > minValue ? newValue : minValue;
   }
   return baseValue;
}

As you can see, while 3 muskeeters of PropertyChangedCallbackCoerceValueCallback and ValidateValueCallback are extremly useful, one needs to be aware of similiar issues which could come along and hence one needs to be aware of what is the difference between them and the sequence of execution.

Circular Progressbar in WPF

One of the things I have been working recently required me to use a Circular Progress bar. Incidently, I was surprised there wasn’t something useful in the WPF package, but it wasn’t that hard to do at the hindsight.

The core idea would be to draw two overlapping circles – one for the background circle and other indicating the progress. We will begin by defining certain characterstics of the Cricle (or better called Arc), which we will find useful later on. So, let us first define our model.

public class ProgressArc : PropertyChangedBase
{
    public Point StartPosition { get; set; } = new Point(50, 0);
    public Point EndPosition { get; set; } = new Point(100, 0);
    public Size Radius { get; set; }
    public double Thickness { get; set; } = 2;
    public double Angle { get; set; }
}

Each of the Circle/Arcs are characterized by 5 properties as defined in the Model above.

  • Start Position
  • End Position
  • Radius Of the Arc
  • Thickness of the Stroke
  • Angle

While most of the characterstics are self explanatory, the Angle might need a bit of exaplantion. Let us take a step back and think about what we are attempting to accomplish here. We would like to represent a Value within a Range (Maximum and Minimum Values) just as in regular ProgressBar, but this time as a Circle/Arc. When the Value is equavalent to the Maximum, we would like to see a full circle. But anything short of maximum value (or in other words less than 100%), we would need to draw an arc that ends at an angle, which would represent the percentage of completion. The angle could be calculated pretty easily with the following.

var percent = (currentValue / (maxValue - minValue) * 100);
var valueInAngle = (percent / 100) * 360;

Alright – so with that explanation under our belt, let us proceed to construct our view.

<Viewbox Stretch="Uniform" Margin="10">
    <Grid>
        <Canvas Height="100" Width="100" >
            <Path Stroke="LightGray" StrokeThickness="{Binding BackgroundCircle.Thickness}" >
                <Path.Data>
                    <PathGeometry>
                        <PathFigure  StartPoint="{Binding BackgroundCircle.StartPosition}">
                            <PathFigure.Segments>
                                <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                                            Size="{Binding BackgroundCircle.Radius}"
                                            IsLargeArc="True"
                                        Point="{Binding BackgroundCircle.EndPosition}"
                                            >

                                </ArcSegment>

                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry>
                </Path.Data>
            </Path>

            <Path Stroke="Blue" StrokeThickness="{Binding ValueCircle.Thickness}" StrokeEndLineCap="Round">
                <Path.Data>
                    <PathGeometry>
                        <PathFigure  StartPoint="{Binding ValueCircle.StartPosition}">
                            <PathFigure.Segments>
                                <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                                            Size="{Binding ValueCircle.Radius}"
                                            IsLargeArc="{Binding ValueCircle.Angle,Converter={StaticResource AngleToIsLargeConverter}}"
                                        Point="{Binding ValueCircle.EndPosition}"
                                            >

                                </ArcSegment>

                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry>
                </Path.Data>
            </Path>

        </Canvas>
        <TextBlock Text="{Binding CurrentValue}" FontSize="32" VerticalAlignment="Center" HorizontalAlignment="Center" />
    </Grid>
</Viewbox>

Of course, that would look pretty incomplete without our ViewModel. Let us define our ViewModel too before we discuss the finer points.

 public class ShellViewModel : Screen
{
    public ShellViewModel()
    {
        BackgroundCircle.Angle = 360;
        BackgroundCircle.PropertyChanged += OnCircleChanged;
        ValueCircle.PropertyChanged += OnCircleChanged;
        PropertyChanged += OnPropertyChanged; ;
    }

    private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(MaxValue):
            case nameof(MinValue):
            case nameof(CurrentValue):
            case nameof(SelectedOverlayMode):
                RefreshControl();
                break;
        }
    }

    private void OnCircleChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        RefreshControl();
    }

    protected override void OnViewAttached(object view, object context)
    {
        base.OnViewAttached(view, context);
        RefreshControl();
    }
    private void RefreshControl()
    {
        ArcCalculatorBase arcCalculator;
        switch (SelectedOverlayMode)
        {
            case OverlayMode.Centered:
                arcCalculator = new CenteredArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness); 
                break;
            case OverlayMode.InnerCircle:
                arcCalculator = new InsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
            case OverlayMode.OuterCircle:
                arcCalculator = new OutsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
            default:
                arcCalculator = new OutsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
        }
        arcCalculator.Calculate(MinValue, MaxValue, CurrentValue);

        BackgroundCircle.Radius = arcCalculator.BackgroundCircleRadius;
        BackgroundCircle.StartPosition = arcCalculator.BackgroundCircleStartPosition;
        BackgroundCircle.EndPosition = arcCalculator.BackgroundCircleEndPosition;

        ValueCircle.Radius = arcCalculator.ValueCircleRadius;
        ValueCircle.StartPosition = arcCalculator.ValueCircleStartPosition;
        ValueCircle.EndPosition = arcCalculator.ValueCircleEndPosition;
        ValueCircle.Angle = arcCalculator.ValueAngle;
    }

    public ProgressArc BackgroundCircle { get; set; } = new ProgressArc();
    public ProgressArc ValueCircle { get; set; } = new ProgressArc();
    public double MinValue { get; set; } = 10;
    public double MaxValue { get; set; } = 120;
    public double CurrentValue { get; set; } = 60;

    public OverlayMode SelectedOverlayMode { get; set; }
}

The ViewModel defined is pretty straightforward. We have to instances (BackgroundCircle & ValueCircle) of our model (ProgressArc). These would represent our circles. We have few other properties, including the MinValueMaxValue and CurrentValue, which would be used to configure the ProgressBar.

Each time any of our configuration parameter changes( MinValueMaxValue or CurrentValue), we would need to recalculate the dimentions of the arcs. This is being done in the RefreshControl method. The SelectedOverlayMode property defines how the two circles needs to be aligned with respect to each other.

public enum OverlayMode
{
   InnerCircle,
   OuterCircle,
   Centered
}

Depending on the alignment of the two circles, we would need to alter our calculations. These are defined as the following.

 public abstract class ArcCalculatorBase
{
    protected const double ORIGIN = 50;
    protected double _backgroundCircleThickness;
    protected double _valueCircleThickness;
    public ArcCalculatorBase(double backgroundCircleThickness, double valueCircleThickness)
    {
        _backgroundCircleThickness = backgroundCircleThickness;
        _valueCircleThickness = valueCircleThickness;
    }

    public Size BackgroundCircleRadius { get; protected set; }
    public Size ValueCircleRadius { get; protected set; }

    public Point BackgroundCircleStartPosition { get; protected set; }
    public Point BackgroundCircleEndPosition { get; protected set; }

    public Point ValueCircleStartPosition { get; protected set; }
    public Point ValueCircleEndPosition { get; protected set; }

    public double ValueAngle { get; set; }

    protected double GetAngleForValue(double minValue, double maxValue, double currentValue)
    {
        var percent = (currentValue - minValue) * 100 / (maxValue - minValue);
        var valueInAngle = (percent / 100) * 360;
        return valueInAngle;
    }
    public abstract void Calculate(double minValue, double maxValue, double currentValue);
    protected Point GetPointForAngle(Size radiusInSize,double angle)
    {
        var radius = radiusInSize.Height;
        angle = angle == 360 ? 359.99 : angle;
        double angleInRadians = angle * Math.PI / 180;

        double px = ORIGIN + (Math.Sin(angleInRadians) * radius);
        double py = ORIGIN + (-Math.Cos(angleInRadians) * radius);

        return new Point(px, py);
    }
}

public class OutsetArcCalculator : ArcCalculatorBase
{
    public OutsetArcCalculator(double backgroundCircleThickness, double valueCircleThickness):base(backgroundCircleThickness,valueCircleThickness)
    {

    }

    public override void Calculate(double minValue,double maxValue,double currentValue)
    {
        BackgroundCircleRadius =  new Size(ORIGIN - _backgroundCircleThickness / 2, ORIGIN - _backgroundCircleThickness / 2);
        ValueCircleRadius = new Size(ORIGIN - _valueCircleThickness / 2, ORIGIN - _valueCircleThickness / 2); ;

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

public class CenteredArcCalculator : ArcCalculatorBase
{
    public CenteredArcCalculator(double backgroundCircleThickness, double valueCircleThickness) : base(backgroundCircleThickness, valueCircleThickness)
    {

    }

    public override void Calculate(double minValue, double maxValue, double currentValue)
    {
        var maxThickness = Math.Max(_backgroundCircleThickness, _valueCircleThickness);
        
        BackgroundCircleRadius = new Size(ORIGIN - maxThickness / 2, ORIGIN - maxThickness / 2);
        ValueCircleRadius = new Size(ORIGIN - maxThickness / 2, ORIGIN - maxThickness / 2); ;

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

public class InsetArcCalculator : ArcCalculatorBase
{
    public InsetArcCalculator(double backgroundCircleThickness, double valueCircleThickness) : base(backgroundCircleThickness, valueCircleThickness)
    {

    }

    public override void Calculate(double minValue, double maxValue, double currentValue)
    {
        var maxThickness = Math.Max(_backgroundCircleThickness, _valueCircleThickness);

        BackgroundCircleRadius = new Size((ORIGIN - maxThickness) + (_backgroundCircleThickness / 2), (ORIGIN - maxThickness) + (_backgroundCircleThickness / 2));
        ValueCircleRadius = new Size((ORIGIN - maxThickness) + (_valueCircleThickness / 2), (ORIGIN - maxThickness) + (_valueCircleThickness / 2));

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

The GetPointForAngle() converts the given angle to a point on Canvas using the following formulae

Point = (InitialPosition + (Sin(AngleInRadians) * radius),InitialPosition + (-Cos(AngleInRadians) * radius))

The final outcode, based on different Overlay configuration are shown in figures below

Overlay with Equal Thickness
Center Aligned
Inner Circle Aligned
Outer Circle Aligned

The Complete code could found in here in my Github.

Fody and “OnPropertyChanged” – The Unusual behavior

As a WPF Developer, Fody has been an extremely vital component in my aresenal. It takes a lot of burden off me by injecting some of the boiler plate codes. Despite that, there is one unusually behavior of Fody, which I have difficulty in digesting.

For demonstration, let us create an example View for ourselves. For the sake of example here, am relying on Caliburn Micro for my MVVM.

<StackPanel>
    <TextBlock Text="{Binding RandomNumber}" FontSize="16" HorizontalAlignment="Center"/>
    <Button Content="Randomize" x:Name="Randomize"/>
</StackPanel>

The ViewModel, at the moment, looks like the following.

public class ShellViewModel:Screen
{
    private Random _random;

    public ShellViewModel()
    {
        _random = new Random();
    }
    public long RandomNumber { get; set; }

    public void Randomize()
    {
        RandomNumber = _random.Next();
    }
}

This works perfectly fine. If you examine the code via ILSpy, you could notice that Fody has injected the code correctly as one would expect.

public class ShellViewModel : Screen
{
	private Random _random;

	public long RandomNumber
	{
		[CompilerGenerated]
		get
		{
			return <RandomNumber>k__BackingField;
		}
		[CompilerGenerated]
		set
		{
			if (<RandomNumber>k__BackingField != value)
			{
				<RandomNumber>k__BackingField = value;
				OnPropertyChanged(<>PropertyChangedEventArgs.RandomNumber);
			}
		}
	}


	public ShellViewModel()
	{
		_random = new Random();
	}

	public void Randomize()
	{
		RandomNumber = _random.Next();
	}
}

The OnPropertyChanged call here points to the PropertyChangedBase.OnPropertyChanged method of Caliburn Micro (PropertyChangedBase implements INotifyPropertyChanged in Caliburn Micro)

protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
	this.PropertyChanged?.Invoke(this, e);
}

Let us add some more code to our ViewModel and see how Fody behaves. Assume that we need to do some special handling when the Property Changed event is triggered in our ViewModel. So we will go ahead and subscribe in our constructor.

public ShellViewModel()
{
    _random = new Random();
    PropertyChanged += HandlePropertyChanged;
}

private void HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    // Do something here
}

Fody, quite expectedly, handles the change gracefully.

public class ShellViewModel : Screen
{
	private Random _random;

	public long RandomNumber
	{
		[CompilerGenerated]
		get
		{
			return <RandomNumber>k__BackingField;
		}
		[CompilerGenerated]
		set
		{
			if (<RandomNumber>k__BackingField != value)
			{
				<RandomNumber>k__BackingField = value;
				OnPropertyChanged(<>PropertyChangedEventArgs.RandomNumber);
			}
		}
	}

	public ShellViewModel()
	{
		_random = new Random();
		PropertyChanged += HandlePropertyChanged;
	}

	private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
	{
	}

	public void Randomize()
	{
		RandomNumber = _random.Next();
	}
}

So far, so good. This is exactly like one would expect. The injected code still calls the INotifyPropertyChanged implementation(in our base class).

set
{
    if (<RandomNumber>k__BackingField != value)
    {
        <RandomNumber>k__BackingField = value;
        OnPropertyChanged(<>PropertyChangedEventArgs.RandomNumber);
    }
}


But this is also where things start to behave strangely (one could counter argue this, but i still feel the following behavior is strange). Let us rename our PropertyChanged event handler as HandlePropertyChanged.

public ShellViewModel()
{
    _random = new Random();
    PropertyChanged += OnPropertyChanged;
}

private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    // Do something here
}

Let us examine the code generated by Fody now.

public long RandomNumber
{
    [CompilerGenerated]
    get
    {
        return <RandomNumber>k__BackingField;
    }
    [CompilerGenerated]
    set
    {
        if (<RandomNumber>k__BackingField != value)
        {
            <RandomNumber>k__BackingField = value;
            OnPropertyChanged(this, <>PropertyChangedEventArgs.RandomNumber);
        }
    }
}

public ShellViewModel()
{
    _random = new Random();
    PropertyChanged += OnPropertyChanged;
}

private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
}

The code injected by Fody has changed now and instead of calling the implementation of INotifyPropertyChanged, it is invoking the Event Handler of PropertyChanged.

set
{
    if (<RandomNumber>k__BackingField != value)
    {
        <RandomNumber>k__BackingField = value;
        OnPropertyChanged(this, <>PropertyChangedEventArgs.RandomNumber);
    }
}

In a way, if one was to think about it, it is a good behavior. Fody, on sensing that we have subscribing to PropertyChanged handler, ensure we are calling the handler instead. But, then the behavior should have been same when the Handler name was HandlePropertyChanged.

The strange part, which am not comfortable with this, is that this call to Handler occurs only when the Handler is named OnPropertyChanged. In every other case the injected code points to the implementation of INotifyPropertyChanged. I would like to call this a bug – You could expect the behavior to be consistent.

One of the troubles with this behavior (other than the obvious inconsistency) is that is extremely hard to trace this issue (remember – Fody injects the code) unless one is aware of this strange naming convention followed by Fody. I hope Fody does something to correct, if not make the make the behavior more consistent.

View Model First Approach using Caliburn Micro

While it is possible to use both View Model First and View First approach while using Caliburn Micro, I personally feel one should stick to one single appraoch thoughout your application. Mixing the two approaches would impact the readability of code adversely.

View Model First Approach

In this post we will look at the ViewModel first approach, which is the default approach used by Caliburn Micro. Simply stated, it uses ViewModel to recognize the associated View.

Let us assume we have a ShellViewModel class, which contains an instance of UserProfileViewModel, defined as in example code below.

public class ShellViewModel:Conductor<object>
{
    public UserProfileViewModel UserProfile { get; set; }

    public ShellViewModel()
    {
        UserProfile = IoC.Get<UserProfileViewModel>();
        UserProfile.Name = "Anu Viswan";
        UserProfile.Age = 37;
    }
}

The Xaml part, particularly the View detection is quite simpler here, thanks to the Caliburn Micro’s conventions.

<ContentControl  x:Name="UserProfile"/>

The caliburn micro would detect the required View using the ViewLocator.LocateForModelType method. Following is how it looks like.

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>{
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    {
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    }

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
        : GetOrCreateViewType(viewType);
};

As you can observe, the method removes Model from the full name of the viewmodel to recognize the associate view. The parameter context brings us to an interesting scenario, which we will discuss a bit later. But for now, it becomes clear that how the naming conventions of Caliburn Micro works under the hood.

Custom naming conventions is easily possible with Caliburn Micro. But I guess that is another topic, which need’s a post of its own.

Binding to Collection of ViewModel

For now, let us look into another scenario. Let us assume, we have a collection of ViewModels which needs to be displayed in an ItemsControl.

public class ShellViewModel:Conductor<object>
{
    public IEnumerable<UserProfileViewModel> UserProfileCollection { get; set; }
    public ShellViewModel()
    {
        UserProfileCollection = Enumerable.Range(1, 10).Select(x => new UserProfileViewModel { Name = $"Sample Name {x}", Age = 37 + x });
    }
}

Binding the collection to ItemsControl and displaying each of the Items in a ContentControl would require a minor change. The usage of View.Model attached property, which is defined as

public static DependencyProperty ModelProperty =
            DependencyPropertyHelper.RegisterAttached(
                "Model",
                typeof(object),
                typeof(View),
                null,
                OnModelChanged
                );


public static void SetModel(DependencyObject d, object value) {
    d.SetValue(ModelProperty, value);
}

public static object GetModel(DependencyObject d) {
    return d.GetValue(ModelProperty);
}

static void OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args)
{
    if (args.OldValue == args.NewValue) {
        return;
    }

    if (args.NewValue != null) {
        var context = GetContext(targetLocation);

        var view = ViewLocator.LocateForModel(args.NewValue, targetLocation, context);
        ViewModelBinder.Bind(args.NewValue, view, context);
        if (!SetContentProperty(targetLocation, view)) {

            Log.Warn("SetContentProperty failed for ViewLocator.LocateForModel, falling back to LocateForModelType");

            view = ViewLocator.LocateForModelType(args.NewValue.GetType(), targetLocation, context);

            SetContentProperty(targetLocation, view);
        }
    }
    else {
        SetContentProperty(targetLocation, args.NewValue);
    }
}

As you can observe, the View.Model attached property, under the hood uses the ViewLocator.LocateForModel method itself, which we had previously seen.

We have now seen how View.Model is defined, so let us go ahead and write our xaml to finish off the example.

<ItemsControl x:Name="UserProfileCollection">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Grid.Row="1" BorderThickness="1" BorderBrush="LightGray">
                <ContentControl cal:View.Model="{Binding}"/>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The Context Parameter – Multiple Views for single View Model

We had skipped the Context Parameter for the ViewLocator.LocateForModel method earlier. Let us now examine the role of the parameter in detail.

var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
if(context != null)
{
    viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
    viewTypeName = viewTypeName + "." + context;
}

From the above code (from the ViewLocator.LocateForModel), it is obvious that if the context parameter is non-null value, then it would replace the View string with a . followed by the context string. With that in mind, let us build our alternative views using the following folder structure.

Let us now pass the context parameter in our example above using the attached property.

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border Grid.Row="1" BorderThickness="1" BorderBrush="LightGray">
            <ContentControl cal:View.Model="{Binding}" cal:View.Context="StudentProfile"/>
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

The UserProfileView would be now replaced with StudentProfile view which we have created in the previous step.

Conclusion 

In this post, we examined how to name resolution happens behind the scene for Caliburn Micro in a View Model First approach. We will examine View First approach in a later post, but if you understand the difference between the approach, and the working of one, it becomes easier to expect the other should behave.

WPF Validations using DataAnnotation and INotifyDataErrorInfo

DataAnnotations and INotifyDataErrorInfo provides an easy and cleaner way to implement validations in WPF ViewModels. Unlike IDataErrorInfo, INotifyDataErrorInfo allows you to raise multiple errors (there are many more features which makes it more interesting than IDataErrorInfo including making it possible to use asynchronous validations).

This being a hands-on demonstration, we will keep the theoritical part to the minimum and hit the code at the earliest. We will begin by writing our little Model class, which would be used for demonstration.

public class Model
{
    [Required(ErrorMessage = "Name cannot be empty")]
    [StringLength(9, MinimumLength = 3, ErrorMessage = "Name should have min 3 characters and max 9")]
    public string Name { get; set; }
    [Required]
    [Range(0,100,ErrorMessage = "Age should be between 1 and 100")]
    public int Age { get; set; }
}


Not too much to explain there. We have used the DataAnnotationAttribute to specify requirements of each property. We will now define a View to display this model. We will get to the ViewModel last, because thats where the trick lies.

<Grid>
    <StackPanel>
        <Grid Margin="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="7*"/>
            </Grid.ColumnDefinitions>

            <Label Content="Name"/>
            <TextBox Grid.Column="1" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" >
                <Validation.ErrorTemplate>
                    <ControlTemplate>
                        <StackPanel>
                            <AdornedElementPlaceholder x:Name="textBox"/>
                            <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                        </StackPanel>
                    </ControlTemplate>
                </Validation.ErrorTemplate>
            </TextBox>
        </Grid>

        <Grid Margin="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="7*"/>
            </Grid.ColumnDefinitions>

The most significant part here is of course the property ValidatesOnNotifyDataErrors being set to true. Additionally I have also used ErrorTemplate to display the error but that is more of an implementation detail for this particular demo, you could opt for the default template or choose another that suits your needs.

The next task obiovusly would be to implement the INotifyDataErrorInfo property in your ViewModel. The INotifyDataErrorInfo is defined as following.

 public interface INotifyDataErrorInfo
 {
       bool HasErrors { get; }
       event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
       IEnumerable GetErrors(string? propertyName);
 }


As noticed, the interface comprises mainly of a property,method and event.

  • HasErrors : Indicates a value whether the current entity has Validation errors. This is a read-only property.
  • GetErrors : A Method which retrieves all the validation errors for the given Property. The property name is passed via parameter.
  • ErrorsChanged : This event would be raised each time the Validation errors has changed for a particular property or for the Entity as a whole.

Let us go ahead and implement our view model. We will use a dictionary to store the errors.

private IDictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

The next step is to create our Properties.

private Model _model = new ();

public string Title => "Data Annotation and INotifyDataErrorInfo";
public string Name
{
    get => _model.Name;
    set
    {
        if (Equals(_model.Name, value)) return;

        _model.Name = value;
        NotifyOfPropertyChange();
        Validate(value);
    }
}


public int Age
{
    get => _model.Age;
    set
    {
        if (_model.Age == value) return;

        _model.Age = value;
        NotifyOfPropertyChange();
        Validate(value);
    }
}

Notice a method Validate is being invoked each time a property changes. This method is responsible for validating the property and maintaining the dictionary of errors.

The implementation of Validate looks like following.

private void Validate(object val, [CallerMemberName] string propertyName = null)
{
    if (_errors.ContainsKey(propertyName)) _errors.Remove(propertyName);

    ValidationContext context = new ValidationContext(_model) { MemberName = propertyName };
    List<ValidationResult> results = new();

    if (!Validator.TryValidateProperty(val, context, results))
    {
        _errors[propertyName] = results.Select(x => x.ErrorMessage).ToList();
    }

    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}

What remains now is to impement our interface, which is pretty straightforward.

public bool HasErrors => _errors.Any();

public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

public IEnumerable GetErrors(string propertyName)
{
    return _errors.ContainsKey(propertyName) ? _errors[propertyName] : null;
}

The complete ViewModel looks like following.

public class DataAnnotionViewModel : ViewModelBase, INotifyDataErrorInfo
{
    private IDictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
    private Model _model = new ();

    public string Title => "Data Annotation and INotifyDataErrorInfo";
    public string Name
    {
        get => _model.Name;
        set
        {
            if (Equals(_model.Name, value)) return;

            _model.Name = value;
            NotifyOfPropertyChange();
            Validate(value);
        }
    }


    public int Age
    {
        get => _model.Age;
        set
        {
            if (_model.Age == value) return;

            _model.Age = value;
            NotifyOfPropertyChange();
            Validate(value);
        }
    }

    private void Validate(object val, [CallerMemberName] string propertyName = null)
    {
        if (_errors.ContainsKey(propertyName)) _errors.Remove(propertyName);

        ValidationContext context = new ValidationContext(_model) { MemberName = propertyName };
        List<ValidationResult> results = new();

        if (!Validator.TryValidateProperty(val, context, results))
        {
            _errors[propertyName] = results.Select(x => x.ErrorMessage).ToList();
        }

        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
    }

    public bool HasErrors => _errors.Any();

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        return _errors.ContainsKey(propertyName) ? _errors[propertyName] : null;
    }
}

That’s all you need for Validating your ViewModels using DataAnnotationAttribute and INotifyDataErrorInfo. The complete code sample is available in my Github here.

Dragging Shapes in Wpf

One of the projects I have in mind as my side-projects needs ability to drag-move shapes on a Canvas. I thought it would idea to explore the possibilities of achieving it via test projects first before starting the real one. So the goal of this article would be
* Create a Rectangle in a Canvas
* Support ability to Drag-move it within the Canvas boundary

Let’s begin by defining the basic UI including our Canvas and Rectangle. We would be using Caliburn Micro for supporting MVVM in the example.

<Canvas Grid.Row="2" Background="AliceBlue" >
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" />
</Canvas>

Our ViewModel, at the moment looks like,

public class ShellViewModel:PropertyChangedBase
{
public double Left { get; set; } = 10;
public double Top { get; set; } = 10;
}

The Left and Top are double properties which will have the position of the Rectange with respect to the Canvas.

The next step is to capture the Mouse Position each time the User holds the Left Mouse Button down and drags the object. For this there are few things needs to be done. We need to detect when the Left Mouse Button is pressed down and released. Also, we need to trace the Mouse Position when the User holds the Left Mouse Down within the rectangle.

To detect the mouse position, we will write a Behavior.

public class MouseMoveBehavior:Behavior<Canvas>
{
public double MouseTop
{
get { return (double)GetValue(MouseTopProperty); }
set { SetValue(MouseTopProperty, value); }
}

public static readonly DependencyProperty MouseTopProperty =
DependencyProperty.Register("MouseTop", typeof(double), typeof(MouseMoveBehavior), new PropertyMetadata(0d));

public double MouseLeft
{
get { return (double)GetValue(MouseLeftProperty); }
set { SetValue(MouseLeftProperty, value); }
}

public static readonly DependencyProperty MouseLeftProperty =
DependencyProperty.Register("MouseLeft", typeof(double), typeof(MouseMoveBehavior), new PropertyMetadata(0d));

protected override void OnAttached()
{
AssociatedObject.MouseMove += AssociatedObject_MouseMove;
}

private void AssociatedObject_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
var currentPosition = e.GetPosition(AssociatedObject);
MouseLeft = currentPosition.X;
MouseTop = currentPosition.Y;
}

protected override void OnDetaching()
{
AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
}
}

The Behavior MouseMoveBehavior defines two Dependency Properties named MouseTop and MouseLeft indicating the X-Y cordinates of the mouse position. The behavior intends to capture the Mouse position using the MouseEventArgs.GetPosition and update the dependency properties with the Position.X and Position.Y values.

So, we have our behavior in place. Now is the time to update our Xaml to use our Behavior.

<Canvas Grid.Row="2" Background="AliceBlue" cal:Message.Attach="[Event MouseMove]=[Action MouseMove];
[Event PreviewMouseLeftButtonUp]=[Action MouseUp];[Event MouseLeave]=[Action MouseUp]" >
<i:Interaction.Behaviors>
<behaviors:MouseMoveBehavior MouseLeft="{Binding Left,Mode=TwoWay}" MouseTop="{Binding Top,Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" cal:Message.Attach="[Event PreviewMouseLeftButtonDown]=[Action MouseDown]" />
</Canvas>

As you can observe, we are updating the Left and Top properties by capturing them in Behavior and assigning the the values of current mouse position. This would ensure the the shape would move along the mouse pointer.

But you want to update the Left and Top position only when the Mouse Left Button is pressed and dragged, not when it is freely moved. To help us achieve this, we will introduce three additional properties in our ViewModel. Let us rewrite our ViewModel. This would also explain why we are capturing the Mouse Events in the XAML, which was intensionally not explained till this point.

public class ShellViewModel:PropertyChangedBase
{
public double Left { get; set; } = 10;
public double Top { get; set; } = 10;

public double CurrentMouseX { get; set; }
public double CurrentMouseY { get; set; }

public bool IsShapeCaptured { get; set; }

public void MouseDown()
{
IsShapeCaptured = true;
NotifyOfPropertyChange(nameof(IsShapeCaptured));
}

public void MouseMove()
{
if (!IsShapeCaptured) return;
Left = CurrentMouseX;
Top = CurrentMouseY;
NotifyOfPropertyChange(nameof(Left));
NotifyOfPropertyChange(nameof(Top));
}

public void MouseUp()
{
IsShapeCaptured = false;
NotifyOfPropertyChange(nameof(IsShapeCaptured));
}
}

The IsShapeCaptured is enabled only when the Mouse Left Button is pressed. As soon as the Mouse Button is released, you are resetting the IsShapeCaptured flag. The MouseMove method ensures that the Left & Top properties (which are bound to the position of Shape) are updated only when the IsShapeCaptured is set.

I guesss that bit of code is quite self explanatory. This is one way to achieve the drag/drop functionality, but obviously there could be others. Will provide another solution in coming days.

Non-Blinking Caret in WPF Textbox

A recent question I came across recently was “How could one stop the caret in a WPF Textbox from blinking ?“.

My first instinct drove me in the direction of HideCaret method, but quickly realized WPF applications doesn’t allow you to get the Handle for individual controls. It only provides handles for Windows. This was quite unlike WinForm applications and ruled out usage of HideCaret API as it requires Handle for the control to disable the Caret.

The next solution I came up with ( and am still sticking on to it until i get a better one) was to emulate the behavior. What if it was possible to disable the original caret and mock the behavior using something else. The first part of the challenge was pretty easy, you could set the CaretBursh property to a Transparent color and the original Caret is hidden.

<TextBox x:Name="txtName" CaretBrush="Transparent" />

But the more important question was how to mock the caret. The solution lies in using a Canvas and Border overlaying the Textbox to emulate the caret. Let’s update our XAML first.

<Grid>
<TextBox x:Name="txtName" CaretBrush="Transparent" />
<Canvas>
<Border x:Name="Caret" Visibility="Collapsed" Canvas.Left="0" Canvas.Top="0" Width="1" Height="15" Background="Black"/>
</Canvas>
</Grid>

The final act of the trick involves writing a bit of code-behind logic.

txtName.SelectionChanged += (sender, e) => MoveCustomCaret();
txtName.LostFocus += (sender, e) => Caret.Visibility = Visibility.Collapsed;
txtName.GotFocus += (sender, e) => Caret.Visibility = Visibility.Visible;

Where the MoveCustomCaret is defined as

private void MoveCustomCaret()
{
var caretLocation = txtName.GetRectFromCharacterIndex(txtName.CaretIndex).Location;

if (!double.IsInfinity(caretLocation.X))
{
Canvas.SetLeft(Caret, caretLocation.X);
}

if (!double.IsInfinity(caretLocation.Y))
{
Canvas.SetTop(Caret, caretLocation.Y);
}
}

And now you have your non-blinking caret in textbox ready. Here is a screenshot of the control.

sample1

If you have a better solution, please let me know. That would be interesting to learn.

Verifying if View Exists for specified ViewModel

There might raise situations in your Project where you might be interested to check if the ViewModel specified has a corresponding View defined and gradefully handle the error, than throwing an exception.

Caliburn Micro’s ViewLocator class provides you methods that enables you to do exactly that.

if ((ViewLocator.LocateForModelType(typeof(DummyViewModel), null, null) is TextBlock tb
&& tb.Text.StartsWith("Cannot find", StringComparison.InvariantCultureIgnoreCase)))
{
// View does not exist, Terminate or redirect to another
}
else
{
// View found, redirect to intended one
}

Shaders with HLSL #03: Passing Additional Parameters

In previous parts of this series, we briefly discussed the need for Shaders and created our first Shader Program. In this subsequent part, we would be exploring on passing additional parameters to our Custom Shader.

In previous example, we replaced entire color of applied Control with a single color. What if we need to replace only certain colors, among different colors in the control. We would then need to pass the ‘color to replace’ and ‘new color’ to the Shader.

Step 01 : The Shader

Let’s write our Shader, again with HLSL. We will delve into details soon.

sampler2D input: register(s0);
float4 oldColor : register(c0);
float4 newColor : register(c1);

float4 main(float2 uv:TEXCOORD) : COLOR
{
float4 color = tex2D(input, uv);
if (color.r == oldColor.r && color.b == oldColor.b && color.g == oldColor.g)
{
return newColor;
}
return color;
}

We now have two additional parameters defined in the Line 2 & 3. While it follows the earlier parameter declaration, there is an important difference. For additional (non-image) parameters, we are using the C series registers, which are, as discussed in last post, floating point registers. We have two parameters, both of type float4, which would hold details of ‘old color‘ and ‘new color‘.

float4 oldColor : register(c0);
float4 newColor : register(c1);

In the first line of our main function, we see a new function, which we haven’t seen in previous example.

float4 color = tex2D(input, uv);

tex2D is a texture sampling intrinsic function, which generates a vector from its a sampler and a float2, which holds the two-dimensional texture cordinates at which the sampler is to be sampled. The cordinates (u and v), ranges from (0,0) to (1,1) as one moves from top-left to bottom-right.

In the subsequent code, which is self-explanatory, we compare the RGB values to decide whether we need to replace the color.

Step 02 : The ShaderEffect Class

Having written our Shader, the next obvious step is create our custom class derieved from ShaderEffect. Like in Shader code, we will write the code first and then explore the details.

public class ReplaceColorEffect : ShaderEffect
{
public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ReplaceColorEffect), 0);
public Brush Input
{

get => ((Brush)(GetValue(InputProperty)));
set => SetValue(InputProperty, value);
}

public Color OldColor
{
get { return (Color)GetValue(OldColorProperty); }
set { SetValue(OldColorProperty, value); }
}

public static readonly DependencyProperty OldColorProperty =
DependencyProperty.Register("OldColor", typeof(Color), typeof(ReplaceColorEffect), new PropertyMetadata(Colors.Black,PixelShaderConstantCallback(0)));
public Color NewColor
{
get { return (Color)GetValue(NewColorProperty); }
set { SetValue(NewColorProperty, value); }
}

public static readonly DependencyProperty NewColorProperty =
DependencyProperty.Register("NewColor", typeof(Color), typeof(ReplaceColorEffect), new PropertyMetadata(Colors.Black, PixelShaderConstantCallback(1)));

public ReplaceColorEffect()
{
PixelShader pixelShader = new PixelShader();

pixelShader.UriSource = new Uri(@"E:\App Store\GitHub\anuviswan\LearningPoint\Shaders\ShaderExample001\Shader\ReplaceColor.ps", UriKind.Absolute);

PixelShader = pixelShader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(OldColorProperty);
UpdateShaderValue(NewColorProperty);

}
}

While most of the code is pretty similiar to code in our previous example, ones that needs more attension is discussed below. The most significant difference is the two additional parameters, which are mapped to floating point registers (c registers) and how it is defined in C# code.

public Color OldColor
{
get { return (Color)GetValue(OldColorProperty); }
set { SetValue(OldColorProperty, value); }
}

public static readonly DependencyProperty OldColorProperty =
DependencyProperty.Register("OldColor", typeof(Color), typeof(ReplaceColorEffect), new PropertyMetadata(Colors.Black,PixelShaderConstantCallback(0)));

As observed in the code above, the declaration of dependency properties for c-registers are in the more familiar & conventional syntax, using the DependencyProperty class. What makes it different from normal dependency properties is the PixelShaderConstantCallback parameter of PropertyMetadata class. This tells the compiler that this is a special Dependency property which needs to be mapped to c-registers when communicating with the Shader application.

Rest of the code is pretty self-explanatory and XAML is no different than ones you might have seen countless times, using the dependency properties.

<shadereffects:ReplaceColorEffect OldColor="Red" NewColor="Green"/>

Hit F5 and you can now see your Shader in action. For complete code discussed in this series, please refer to my Github.

Complete Shader with HLSL Series