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

Shaders with HLSL #02 : Applying Shader to WPF Application

In the first part of series, we wrote our first Shader Program. Now it is time to compile it so that we could use it in our WPF Application.

HLSL Compiler

To compile your HLSL code to a format that could be used with .Net WPF application, you would require the fxc compiler. For Windows 7 and earlier editions, you would need to install DirectX SDK in order to get fxc.exe. For Windows 8 and above, this comes as part of the Windows SDK.

The compiler can found under the location ‘Program Files (x86)\Windows Kits‘. If we had saved our previous written code (from previous part of this series) as RedTint.hlsl, we would need to use following command to compile it.

fxc /T ps_2_0 /E main /Fo RedTint.ps RedTint.hlsl

This would give the necessary ps file, or Pixel Shader file which we could now refer in our WPF Application. Now that we have our Pixel Shader file, we will move on to our WPF application.

Creating our Test Application

We will keep our text application simple. The XAML code is shown below.

<Grid Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Normal Button" HorizontalAlignment="Center"/>
<Button Grid.Column="0" Grid.Row="1" Content="Click Me"/>

<Label Grid.Column="1" Grid.Row="0" Content="Button with Shader" HorizontalAlignment="Center"/>
<Button Grid.Column="1" Grid.Row="1" Content="Click Me"/>
</Grid>

Also enclosed is a screenshot of the application. The purpose of our application would be apply our shader to one of the buttons, while leaving the other button untouched, so that we could easily demonstrate the difference (or rather impact of shader). We will also add our RentTint.ps file to our project as a resource.

Defining Custom Effect

To use the compiled shader application, we need to load it to the rendering engine input stream. This is accomplished with the help of ShaderEffect class in .Net. The ShaderEffect base class makes it possible to wrap the HLSL pixel shader and apply them our applications XAML elements.

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

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

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

pixelShader.UriSource = new Uri(@"path_to_ps_resource", UriKind.Absolute);

PixelShader = pixelShader;
UpdateShaderValue(InputProperty);

}
}

We have defined our custom effect, namely RedTintEffect, derieved from ShaderEffect. Let’s examine the code closely beginging with the constructor.

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

pixelShader.UriSource = new Uri(@"path_to_ps_resource", UriKind.Absolute);

PixelShader = pixelShader;
UpdateShaderValue(InputProperty);
}

The first step we need to is to initialize an object of PixelShader (which is a protected member of ShaderEffect class) and refer our ps file using the UriSource property. This would enable the ShaderEffect to communicate with the GPU.

The next step is to create a Dependency Property of Type Brush, calling it “Input”. This is a special property and would contain the input image. There lies a difference in the way we register the Dependency Property as well. Instead of the usual DependencyProperty.Register(), we would be using ShaderEffect.RegisterPixelShaderSamplerProperty() method.

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

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

One of the important points to note in the above code is the final parameter of ShaderEffect.RegisterPixelShaderSamplerProperty() method. The Integer value 0 corresponds to the S0 pixel shader register in our HLSL code. Here is the refered code from HLSL source.

sampler2D input: register(s0);

That’s all we need. We can now go back to our XAML and apply the effect.

Applying Shader

The update code is as follows.

<Grid Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Normal Button" HorizontalAlignment="Center"/>
<Button Grid.Column="0" Grid.Row="1" Content="Click Me"/>

<Label Grid.Column="1" Grid.Row="0" Content="Button with Shader" HorizontalAlignment="Center"/>
<Button Grid.Column="1" Grid.Row="1" Content="Click Me">
<Button.Effect>
<shadereffects:RedTintEffect/>
</Button.Effect>
</Button>
</Grid>

We have added our newly added custom Effect to one of the button. Let’s hit F5 and verify our changes.

As observed in the screenshot, the Button to which the Effect was applied has turned Red, while the other Controls remain the same. Do observe that even the Forecolor of Text within the button has turned Red. This is true to our definition of our Shader, since it returns Red irrespective of the input. We will look into more complex scenarios in later posts where we will introduce conditional color changes.

You can access the entire source code described in Part 1 and Part 2 of this series from my Github

Shaders with HLSL #01 : Shaders with HLSL for WPF Developers

Complete Tutorial Series

In this first part of our tutorial, we will briefly enlighten ourselves on key concepts involving shaders and why it is required. We will also write our first shader application, introducing ourself to the HLSL semantics.

What is a Shader ?

Shaders are small programs written to run on the GPU (Graphical Processing Unit) and describes how to render each pixel. Since running parallely on the GPU hardware, they typically produces faster output,compared to having it run on the CPU.
There are mainly two types of Shaders

  • Vertex Shader – Vertex Shader gets the first chance to work on Model, prior to even Rasterizer and is responsible for transforming vertex.
  • Pixel Shader – Pixel Shader comes into pipeline a little later, after Rasterizer and is responsible for determining the final color of a pixel

WPF and Shaders

The huge computing power of modern GPUs ensures complex and graphically rich applications have little impact on performance. Begining with .Net 3.5 SP1, Microsoft introduced hardware accelerated affects for WPF (Effects in WPF, for example Blur Effect and DropShadowEffect) utilizing the power of shaders. We could write our own custom shaders and use them in our WPF applications.

Evolution of HLSL/C

One of the earliest approaches for writing Shaders used assembly language, but soon it was evident that the inherient complexity of Assembly Language was not making it any easier. This lead to development of Cg (C for Graphics) by NVida and HLSL (High Level Shader Language) by Microsoft. Both languages follows a similiar syntax and is closely associated to C Language in syntax and compile shaders for DirectX.

Getting Started with HLSL

Let’s write some code first and then we will go in detail examining the code as well as how to use it in your WPF Application.

sampler2D input: register(s0);

float4 main(float2 uv:TEXCOORD) : COLOR
{
return float4(1.0f, 0.0f, 0.0f, 1.0f);
}

The first line of code represent our input parameters, stored in GPU registers.

sampler2D input: register(s0);

Pixel Shaders rely on GPU registers input, which are high speed storage areas. There are different types of registers, but most of our applications written in C# depends on S and C register1 types. The S registers, also known as Sampler Registers are mainly used for storing input images data2. If we need to pass another Image Source, we could pass it via the register s1. We could pass other arguements to our shaders using the C registers (floating point registers), but for the sake of our first example, we will keep it simple with just a single input parameter, which is our Image data.

The first line of code specifies an input variable input and the register to use for the purpose. It also specifies the input type, which in this case is sampler2D and refers to the image it is being applied to.

The main as one can easily guess, represent the entry point of the shader code. The incoming parameter uv represents the incoming texture cordinates, while the return type float4 represents an RGBA color.

float4 main(float2 uv:TEXCOORD) : COLOR

The body of our first Shader application is pretty simple, it returns a color representing Red.

return float4(1.0f, 0.0f, 0.0f, 1.0f);

Let’s save the above code with extension “hlsl”. In the next part of tutorial we will examine how to compile our HLSL code to ps format, which would be later consumed by our WPF application.


  1. More on Registers : Source : Microsoft 
  2. Image, in this context refers to visual representation of the target control.