WPF Diagramming. Lines pointing at the center of the element. Calculating angles for render transforms.


All my previous posts from "WPF Diagramming" series were presenting simple lines connected to the center of each shape object. For the first steps it is very convenient not to care about complex maths and just bind endpoints of the line to the centers of objects being connected. The rest will be triggered by WPF.

One day you will decide to implement the arrowheads for your connectors because it’s a mandatory requirement for any diagramming project. But how can you draw the arrowhead for the line that is pointing to the center of the shape? You won’t see that arrowhead in any way because your shape will overlap your connector endpoint. There are at least three ways of implementing this requirement to your project.

Changing layers

You simply change the ZIndex of your geometry to bring the connectors one level upper than your shapes. This will make the arrowheads to be seen but this also make the whole diagramming layout so ugly that I no more dwell on this approach.

Sticking to shape border

Guess this can be regarded as the most common approach when developing diagramming tools or controls as I’ve looked though a variety of implementations. You always know the size of the shape at runtime so you always can get all the border sizes and positions of it. When the connection between two shapes is being established the start point of the line is bound to the bottom border of source shape and the end point is simply bound to the upper border of the target shape. Upon performing a drag operations for both shape objects the line coordinates are recalculated to apply the scheme described. Of course this also influence the layers ordering because the connectors are to be placed one layer upper than the shapes are.

As for me I don’t like this approach at all. I’ve spent several days to perform the third approach that is more close to what I wanted for my article and for my home project.

Pointing at the center of the element

This means that whenever you drag your target shape source shape connector will always point to the center of it. It’s up to the layout design where to place the start point of the line (shape center or some border) but the end point with arrowhead will point at the center of the target shape thus not touching the shape visually. This is what I’m going to describe later on.

 

When the line is pointing at the center of some object it may seem visually, that all you need is to bind line endpoint to the center of the shape and then make it shorter a bit. But when you try to turn your "shortened" line into coordinates you encounter one simple question "What will be the endpoint coordinates and how I can calculate them dynamically?". Upon Googling you then find some answers than make you feel rather unhappy and willing to use the "Sticking to shape border" approach I mentioned before 🙂 The answer is that you have to use angles calculation, trigonometry, vector algebra, geometry and a lot of stuff do understand.

I’ve used Google heavily for a last week to get small and easy solution to be implement to my project and not to write this article at all 🙂 There were some interesting pieces of information on DirectX and XNA forums that were nearly close to what I wanted but not exactly the perfect and easy ones. By that time I’ve already understood what I needed to implement.

Some frightening theory…

First you have to get the angle between you source shape and the target shape. Then you imagine and abstract circle that is drawn around your source shape. After that you get the point on the circle by the angle known already by that time. This point will be exactly the required coordinate of your line endpoint.

As I’ve mentioned at the beginning of the article it took me several days to get the smallest code for the angles calculations possible for performing the process. I could simply give you the sample code to use but when I remembered my own feeling when looking on dozens of code implementations without any supplementary info I decided to spend some more time preparing the drawings and description of the whole process. This can be extremely useful when you’ll decide to customize the process or adopt it to your own needs.

For the sample we will be using a simple button element placed somewhere on the form. According to mouse position it will be rotated to face the cursor. Also one line will be drawn from the cursor position with arrowhead pointing to the center of the button.

 

Getting angle between two vectors

From the Vector Algebra books you can get all the required information on how the vectors are used and how the angles between them are calculated. Microsoft did a really huge piece of work towards this. Presentation Framework (WindowsBase assembly to be exact) contains all vector procedures you need to feel happy implementing this stuff. All you need is two vectors to be compared.

angles_1

You have vector a (0,100) and vector b (100, 100). Using the static method Vector.AngleBetween(b, a) you will get the exact angle between them in degrees. In our case vector a is based on our button location and vector be is created using the current mouse position on the canvas.

 

Turn element positions into vectors

Of course to get the angle between two vectors we need to have some vectors to be calculated. In a real life we have only the coordinates of the button element and can freely get the position of mouse cursor according to the canvas at runtime. Mouse can be placed anywhere like the mouse, so we don’t have much to do with zero coordinates and the image above. How this all can be converted to vectors?

Assume you have a button positioned with it’s center to point (100, 150). Just to note somewhere: we can get the center of element on the canvas by the following template

Point p1 = new Point(
                Canvas.GetLeft(button1) + button1.ActualWidth / 2,
                Canvas.GetTop(button1) + button1.ActualHeight / 2);

So let’s imagine again that our button center is at (100, 150) and the mouse cursor position (think of dragging the shape in this case or simply some another static shape position) is actually at point (200, 150). These two points are quite enough for us to get the rectangle covering our working area. We are taking the center of the button as a starting point of our coordinate system.

angles_2

From the picture given you can easily get the idea. You get the line a X(100, 150) – Y(100, 50) by the button element "Left" position and mouse "Top" position. You get the line b X(100, 150) – Y(200, 150) by center of the button and the mouse cursor position. So we have to lines looking like the vectors we exactly need from the first picture and we know the working area bounds as rectangle X1(100, 150) – Y1(100, 50) – X2(200, 50) – Y2(200, 150). Again to keep simplicity we have a 45 degree angle between them and both lines are of 100 length. To get the two vectors needed we simply get the differences between left and right side coordinates of our rectangle by Y axis.

Next issue we come across is that Microsoft uses left top orientation of coordinates while we require left bottom for your calculations. So additionally we have to reverse our rectangle positions upside down to meet the requirement. Finally as we want our button center to be the starting point for our coordinate system we have to align the rectangle to zero point removing the left button canvas offset from all the four points according to X axis.

So what we have after all is

Vector A = (100, 150) – (100, 50) by Y axis = (100, 100)

Vector B = (200, 150) – (200, 50) by Y axis = (200, 100)

and aligned to zero based positioning by 100

Vector A = (0, 100)

Vector B = (100, 100)

Looks like we have got our first picture describing vectors angle calculation 😉

Next issue you will get when you move the mouse to another three quarters of coordinate system drawn in the picture. According to the approach described you will have to deal with negative coordinates and so have something like on the picture

angles_3

Your internal coordinate system seems to be divided into 4 parts each receiving 0 to 90 or 0 to -90 degrees (see the picture). Additionally when you will move the mouse to the X or Y axis you will get the 0 degrees instead of 0, 90, 180 and 360 degrees. What is happening?

Well this is quite expected behavior as upon mouse cursor moving to to negative coordinates our rectangle and so the line a endpoint goes to negative position either line b. To fix the behavior we need functionality similar to common clock. Line a should always be static: being positive and laying on the same place according to Y axis. Math.Abs() method will help you much in doing the calculations 😉

I intended to write the smallest code possible for the calculations needed. The quickest approach I guess is splitting our coordinate system into two parts. Our 360 degrees rotation is simply performed by doing 180 and -180 degrees. There’s no visual difference but code behind checks become extremely small. So placing line a statically according to Y axis gives us the opportunity of getting +/- 180 degrees.

The only issue will arise is 90 degrees positioning. As two Y points of our line b and so vector lay on the same axis their subtraction will give you a zero value and the further angles calculation will give you 0 degrees respectively. We have to provide additional check for this case and restrict subtraction.

 

Calculating angle using C#

Here’s the source code for getting the angle between button on the canvas and mouse pointer. I’ve optimized the rectangle points usage and vector creation additionally. Also I’ve implemented the simple Y axis check.

Point p1 = new Point(
    Canvas.GetLeft(button1) + button1.ActualWidth / 2,
    Canvas.GetTop(button1) + button1.ActualHeight / 2);

Point p2 = e.GetPosition(this);

Vector a = new Vector(0, Math.Abs((p1.Y != p2.Y) ? p1.Y – p2.Y : p2.Y));           
Vector b = new Vector(p2.X – p1.X, p1.Y – p2.Y);
double angle = Vector.AngleBetween(b, a);

And that’s all 🙂 so much talking and so small to do. According to two points you have the angle in degrees between button1 element and mouse pointer position. You can use this code freely anywhere you need getting angles between two elements on the canvas 🙂

To rotate the button element to face the mouse pointer you can use the following sample

button1.RenderTransformOrigin = new Point(0.5, 0.5);
button1.RenderTransform = new RotateTransform(angle);

 

Positioning the line to point at the center of the element

As we have already calculated the angle between the button element and mouse pointer we can get the right position the line with arrowhead should be bound to.

First we must have an abstract circle around which our arrowhead will be moving in case of angle changed. The radius of circle should be large enough not to allow the arrowhead touch the element at any angle. This is up to you what values to provide, I’ve simply used the button’s "radiuses" according to X and Y axis

double radius = button1.ActualWidth/2 + button1.ActualHeight / 2;

according to the school course of geometry we know already how to find a point on a circle knowing the radius and angle

double x = radius * Math.Cos(angle);
double y = radius * Math.Sin(angle);

But here we also come across two issues. First is that .net Math.Cos and Math.Sin get the values in radians though Vector.AngleBetween returns a value in degrees. The second is that this well known formula for point evaluation on circle does calculations in counter-clockwise mode when we require exactly the clockwise result. To fix these two issues we transform the angle to the radians and invert the calculation formula to clockwise mode:

double x = radius * Math.Sin(angle * Math.PI / 180);
double y = radius * Math.Cos(angle * Math.PI / 180);

Note the inverted usage of Sin and Cos according to the traditional usage. Also you can increase the calculation performance by predefining the value of (PI/180) but this is up to you 😉

That’s all. You can use this coordinates to assign the value of the line endpoint with arrowhead.

 

Getting all together

Notes: For the line with arrowhead I used the sample by Charles Petzold. For a detailed information you really should refer to his own blog.

Window1.xaml

<Window x:Class="Rotation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
    <Canvas Name="myCanvas">
        <Button Canvas.Left="100" Canvas.Top="150" Height="20" Name="button1" Width="40">Button</Button>
        <CheckBox Canvas.Left="10" Canvas.Top="10" Panel.ZIndex="1" Height="15.554" Name="chButtonRotate" Width="119.988">Rotate button</CheckBox>
    </Canvas>
</Window>

Window1.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Petzold.Media2D;

namespace Rotation
{    
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        ArrowLine directionLine;

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.MouseMove += new MouseEventHandler(Window1_MouseMove);
            button1.RenderTransformOrigin = new Point(0.5, 0.5);

            directionLine = new ArrowLine();
            directionLine.Stroke = Brushes.Red;
            directionLine.StrokeThickness = 2;            
            myCanvas.Children.Add(directionLine);

            button1.RenderTransformOrigin = new Point(0.5, 0.5);
        }

        void Window1_MouseMove(object sender, MouseEventArgs e)
        {
            Point p1 = new Point(
                Canvas.GetLeft(button1) + button1.ActualWidth / 2,
                Canvas.GetTop(button1) + button1.ActualHeight / 2);

            Point p2 = e.GetPosition(this);

            Vector a = new Vector(0, Math.Abs((p1.Y != p2.Y) ? p1.Y - p2.Y : p2.Y));            
            Vector b = new Vector(p2.X - p1.X, p1.Y - p2.Y);
            double angle = Vector.AngleBetween(b, a); 
           
            this.Title = angle.ToString();

            if (chButtonRotate.IsChecked == true)
                button1.RenderTransformOrigin = new Point(0.5, 0.5);
                button1.RenderTransform = new RotateTransform(angle);

            double radius = button1.ActualWidth/2 + button1.ActualHeight / 2;

            double x = radius * Math.Sin(angle * Math.PI / 180);
            double y = radius * Math.Cos(angle * Math.PI / 180);

            Point touchPoint = new Point(p1.X + x, p1.Y - y);
            
            directionLine.X1 = p2.X;
            directionLine.Y1 = p2.Y;
            directionLine.X2 = touchPoint.X;
            directionLine.Y2 = touchPoint.Y;
        }
    }
}

 

What you get on the screen

_angles_1 _angles_2

Source code for the article

Using Reflection for binding classes to .ini files


I’m sure the title of the article will appear strange and weird for you. Why should I use ".ini" files nowadays having all the required configuration infrastructure and a pile of Microsoft patterns for doing the configuration stuff. I definitely need to provide some background…

2 of my recent jobs were tightly connected with gambling industry. Me and my partner Alex (you can jump to his space from my "Friends" space section) were working under enormous pressure developing software for video slots and roulette tables, hardware programming and Windows XP Embedded targeting. Guess I’ll dwell on it some other day when I’ll be in a good mood for speaking about XNA and OpenGL, maths, combinational and permutation algorithms, etc.

Once we were finishing another video slot machine. We’ve been working for a whole a day and a night in a non-stop mode and at last when all the tests were performed except one. The machine couldn’t survive the power failure test. All the settings were lost, settings files were damaged and registry hives remained unchanged. It took me several hours to find the solution for that and at the end I made a strong decision for  ".ini" files usage as the best approach for storing configuration settings when speaking of embedded systems.

Weak sides of the configuration approaches when getting power failure situation

1. Registry

I must state that registry really sucks when speaking about sensitive information. It’s caching driver mechanism lives it’s own life. It usually saves information on Windows "Reset" or "Shut Down" performed in a proper way. Try to configure desktop and plug off the power manually. You’ll see the old preferences and nothing will be saved. So registry takes the first place to be avoided.

2. Application Settings

When speaking .net application settings I must admit that the older framework becomes the harder it gets to understand what is going on with the lowest layers. It happens very often that you have to dig all the abstraction layers, providers, wrappers and services to find out "Where the hell is IO operations"? As always Reflector stays the best tool to find out the truth. This approach doesn’t survive the power failures either. Almost all the times you get the damaged data because of incomplete flush operations.

3. IO operations and file streams

The same as above. You can do everything you want with the file streams and use any classes from System.IO namespace but the result is the same as mentioned. The files damage from time to time because of incomplete flush operations though not so often as using Application Settings approach.

You may wonder why do I argue against the above approaches and what kind of code did I do that flushing was my horror. The answer is quite simple.

According to some strict hardware specifications my application had to control the work of bill acceptor, coin acceptor and several money counters. Information should be accepted processed and sent at least every 7-10 ms at a separate thread. So each 7-10 ms I had a strong need of dumping and caching to hard drive to protect machine from "power failure hacks". I’ve realized at the end of testing that the best way of solving the task that was keeping closer to kernel.

After testing the ".ini" via the kernel32 I found out the amazing thing that our machine survived all the power failures. We couldn’t damage our settings with that approach anymore.

Though I’ll give you not the same sample we used, guess you’ll get the main idea. I’ve made it more object oriented and more complicated to cover the reflection power as well.

 

Going unmanaged

The ".ini" processing is rather simple and has always the same declaration to remember

#region External API
[DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int GetPrivateProfileString(
  string lpAppName,
  string lpKeyName,
  string lpDefault,
  string lpReturnString,
  int nSize,
  string lpFilename);

[DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int WritePrivateProfileString(
  string lpAppName,
  string lpKeyName,
  string lpString,
  string lpFilename);        
#endregion

GetPrivateProfileStringW gets the value from file and WritePrivateProfileStringW writes a value to it.

So as you can see you can quickly save the required data to file from any part of code. But imagine that you have about 300 values to be processed all over the application. Even if you create one class for holding it, the process of loading and saving properties’ values will bring you crazy.

As I went crazy very quickly I’ve decided to automate the process using one custom attribute and all the power of reflection.

 

Moving params to custom attribute

You have to remember 4 strings to store any property to file: Filename (physical path of ".ini" file, will be created if not found), Application Name (anything that will be wrapped with "[ ]" brackets, don’t know why MS didn’t call it category or something like that), Key Name and Key Value. First 3 of them are usually static for an application. So the idea is very simple – why not to move them to a custom attribute.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IniFileKeyBinding : Attribute
{
    private string _Section;
    public string Section
    {
        get { return _Section; }
        set { _Section = value; }
    }

    private string _Key;        
    public string Key
    {
        get { return _Key; }
        set { _Key = value; }
    }

    private string _DefaultValue;
    public string DefaultValue
    {
        get { return _DefaultValue; }
        set { _DefaultValue = value; }
    }


    public IniFileKeyBinding(string section, string key)
    {
        _Section = section;
        _Key = key;            
    }
}

So now you can mark some properties in our class to be ready for saving/loading. Here comes my settings provider to hold this stuff. There will be no supplementary information because it is overcommented I think 🙂

 

IniFileSettingsProvider.cs

using System;
using System.Reflection;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;

namespace IniSettings
{
    public class IniFileSettingsProvider
    {
        #region External API
        [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpDefault,
          string lpReturnString,
          int nSize,
          string lpFilename);

        [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int WritePrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpString,
          string lpFilename);        
        #endregion

        /// <summary>
        /// This is a flag for denying data changes. It is mandatory setting public properties using reflection.
        /// </summary>
        protected bool locked;

        #region Local properties
        private string _FileName;
        public string FileName
        {
            get { return _FileName; }
            protected set { _FileName = value; }
        } 
        #endregion

        #region Constructors
        public IniFileSettingsProvider(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                throw new ArgumentNullException("filename");

            _FileName = filename;
            DataBind();
        } 
        #endregion

        /// <summary>
        /// Load and setup all the properties marked with IniFileKeyBinding custom attribute
        /// </summary>
        protected virtual void DataBind()
        {
            // Get current object type
            Type t = this.GetType();

            // Get our mandatory field for temporary locking of data changing
            FieldInfo f_locked = t.GetField("locked", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);

            // If there's no locking field we don't support data binding
            if (f_locked == null)
                throw new NotSupportedException("Data binding is not supported for objects that do not support property locking!");

            // Lock data changes by setting our lock to true
            f_locked.SetValue(this, true);

            // Get the type of our custom attribute
            Type aType = typeof(IniFileKeyBinding);
            // Get all the public propeties of the required type
            PropertyInfo[] properties = t.GetProperties(); 

            // Configure a flag determining whether to load default values for properties if requested ini file was not found
            bool defaultOnly = !File.Exists(_FileName);

            // Start looping the public properties
            foreach (PropertyInfo pi in properties)
            {
                // Skip processing readonly properties
                if (!pi.CanWrite) continue;

                // If the propety exposes our IniFileKeyBinding attribute...
                if (pi.IsDefined(aType, false))
                {
                    // cee1faffe2ebffe5ec efe5f0e5ece5ededf3fe e4ebff f5f0e0ede5ede8ff e7ede0f7e5ede8ff e8e7 ini f4e0e9ebe0
                    // String holder for ini file value (values are always got as string)
                    string iniValue;
                    // Object holder for the final value or the property
                    object propValue;                    

                    // Extract our IniFileKeyBinding attribute info for current property looped
                    IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];                    
                    
                    // If we don't require default value initialization
                    if (!defaultOnly)
                        // Load ini file value based on attribute info
                        iniValue = GetValue(this._FileName, attrInfo.Section, attrInfo.Key, attrInfo.DefaultValue);
                    else
                        // else set the predefined default value
                        iniValue = attrInfo.DefaultValue;
                    
                    // If we got an empty string... we get the empty string in 2 cases:
                    // 1. Value doesn't exist at all in the ini file
                    // 2. Value is supposed to be empty
                    // We must test this in order to avoid problems...
                    if (string.IsNullOrEmpty(iniValue))
                    {
                        // If current property is of string type
                        if (pi.PropertyType == typeof(string))
                            // If the default value is not defined in the custom attribute we set null value otherwise the predefined attribute value
                            propValue = (attrInfo.DefaultValue == null) ? null : attrInfo.DefaultValue;
                        else
                            // Otherwise we setup the possible default value of given type
                            propValue = @default(pi.PropertyType);
                    }
                    else
                        // Change the type of received value to the current propery type
                        propValue = Convert.ChangeType(iniValue, pi.PropertyType, CultureInfo.InvariantCulture);
                    
                    // Finally set the property value
                    pi.SetValue(this, propValue, null);
                }
            }

            // Unlock the data changing by removing the lock
            f_locked.SetValue(this, false);
        }
                
        /// <summary>
        /// Svae the property value to corresponding ini file. Property should be marked with IniFileKeyBinding attribute to be saved.
        /// </summary>
        /// <param name="propertyName">The name of the property to be saved.</param>
        protected virtual void SaveProperty(string propertyName)
        {
            // Skip the save procedure in case of lock enabled
            if (locked) return;

            if (string.IsNullOrEmpty(propertyName))
                throw new ArgumentNullException("propertyName");

            // Get the type for current object
            Type t = this.GetType();    

            // Extract all the neccessary information for the given property
            PropertyInfo pi = t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);

            if (pi == null)
                throw new NullReferenceException("Class does not contain property named '" + propertyName + "'.");

            // Get the type of our custom attribute
            Type aType = typeof(IniFileKeyBinding);

            if (!pi.IsDefined(aType, false))
                throw new NotSupportedException("Saving properties not marked with IniFileKeyBinding attribute is not supported by this method.");

            // Extract our custom attribute from the given property
            IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];
            // Get the property's value
            object propValue = pi.GetValue(this, null);
            // Convert the property value to string for saving to ini file
            string iniValue = (string)Convert.ChangeType(propValue, typeof(string), CultureInfo.InvariantCulture);
            // Finally save the string value to ini file
            WritePrivateProfileString(attrInfo.Section, attrInfo.Key, iniValue, _FileName);
        }

        /// <summary>
        /// Save all the properties marked with IniFileKeyBinding attribute to file.
        /// </summary>
        public virtual void Save()
        {
            Save(_FileName);
        }

        /// <summary>
        /// Save all the properties marked with IniFileKeyBinding attribute to file.
        /// </summary>
        /// <param name="filename">File to be used for saving the properties.</param>
        public virtual void Save(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                throw new ArgumentNullException("filename");

            Type t = this.GetType();
            Type aType = typeof(IniFileKeyBinding);
            PropertyInfo[] properties = t.GetProperties();

            foreach (PropertyInfo pi in properties)
            {
                if (pi.IsDefined(aType, false))
                {
                    IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];
                    object propValue = pi.GetValue(this, null);
                    string iniValue = (string)Convert.ChangeType(propValue, typeof(string), CultureInfo.InvariantCulture);
                    WritePrivateProfileString(attrInfo.Section, attrInfo.Key, iniValue, filename);
                }
            }
        }

        #region Utils
        // This method loads the value from the ini file
        private static string GetValue(string filename, string section, string key, string defaultValue)
        {
            string retval = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue, retval, 1024, filename);
            return retval.Split('')[0];            
        }

        // Get the default value for the type provided
        public static object @default(Type t)
        {
            if (!t.IsValueType)
                return null;
            else
                return Activator.CreateInstance(t);
        }
        #endregion
    }
}

This will be the base class for your own configuration holders. All is left to do for you is implementation of your own settings providers based on it. I’ll give you the basic sample.

MySettings.cs

using System;

namespace SomeMyProjectNamespace
{
    public class MySettings : IniFileSettingsProvider
    {
        public MySettings(string filename) : base(filename) { }

        private int _ScreenWidth;
        [IniFileKeyBinding("Graphics", "ScreenWidth", DefaultValue="1024")]
        public int ScreenWidth
        {
            get { return _ScreenWidth; }
            set 
            { 
                _ScreenWidth = value;
                SaveProperty("ScreenWidth");                
            }
        }

        private int _ScreenHeight;
        [IniFileKeyBinding("Graphics", "ScreenHeight")]
        public int ScreenHeight
        {
            get { return _ScreenHeight; }
            set 
            { 
                _ScreenHeight = value;
                SaveProperty("ScreenHeight");
            }
        }
    }
}

All the properties are automatically bound upon class creation. All your properties are automatically saved upon value changes. Values are being saved according to an attribute configuration. You should only take care of what to mark and what default values to provide.

This approach saved a lot of my time and hope will help someone too.

Getting Best of Generics for Type Conversion


Not a long time ago I worked hard with reflection and type conversion and decided to save the time getting some C# snippets for doing the type conversion. I don’t like the standard System.Convert to be honest so I was looking for some generics implementation. Imagine my surprise when Googling gave me nothing interesting towards my request. This is rather common stuff I guess and there was so much talking about generics that I would better think that Google was buggy that day 😉

I managed to find only one sample that was almost close to what I wanted. I won’t give the sources but in two words: the guy implemented a generic method that was getting a type. Inside he did the switch case or multiple if-else clauses to check it’s generic attributes and give the appropriate result. Good beginning and so stupid end 🙂

I’ve placed my approach to the couple of forums some months before I started to fill my space, and to be honest I didn’t check today whether any good solutions appeared, but I strongly want to put it here…

So meet the raw generics wrapper for System.Convert. I think this is the smallest implementation possible in this case.

public static class Converter
{
    public static T Convert<T>(object value) where T : IConvertible
    {
        return (T)System.Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture);
    }

    public static T Convert<T, K>(K value)
        where T : IConvertible
        where K : IConvertible
    {
        return (T)System.Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture);
    }
}

No multiple ifs and no switches 🙂

Hope it will save someone’s time one day…

WPF Diagramming. Drawing a connection line between two elements with mouse.


This post will be tightly connected to "WPF. Draggable objects and simple shape connectors" article I’ve posted earlier so you should reference it for a better understanding of changes made.

I’ve decided to start a series of articles prefixed with "WPF Diagramming" header. Each time I’ll be implementing more complex stuff and fresh ideas to get some king of business process diagramming tool at the end. I’ll try to keep all the samples as simple as possible including some additional information towards the WPF programming. Hope that all those who liked the article mentioned will enjoy the new series too.

Note: Until the VS 2008 release all my WPF samples will be prepared using VS 2008 TS beta 2.

Storing control templates in resource dictionaries

As you come across with control templating you may want to collect all your templates separately in one place thus getting access to it from any part of the application. Resource Dictionary is exactly what you will need in this case.

1. Right click your project item in the solution explorer and add a new folder for storing your resources. In my sample I called it "Templates".

2. Right click you "Templates" folder and choose "Add – Resource Dictionary". I called the dictionary "BasicShape.xaml"

Now you are ready to setup your template collection. I moved here my only control template used for Thumb appearance.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <ControlTemplate x:Key="BasicShape1">
        <StackPanel>
            <Image Name="tplImage" Source="/Images/user1.png" Stretch="Uniform" Width="32" Height="32" HorizontalAlignment="Center"/>
            <TextBlock Name="tplTextBlock" Text="User stage" HorizontalAlignment="Center"/>
        </StackPanel>
    </ControlTemplate>   
    
</ResourceDictionary>

The template is called "BasicShape1" and it still contains the default settings for image and text elements.

 

Attaching resource dictionaries to your application

You’ve already created your first resource dictionary as "Templates/BasicShape.xaml" and configured your first thumb template. This is a separate resource dictionary and it should be also included to the application to be accessed and used.

Open your application xaml (in my sample it is the most common "App.xaml" file) and define application resources like the following

<Application x:Class="ShapeConnectors.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">

    <Application.Resources>
        <ResourceDictionary Source="Templates/BasicShape.xaml"/>
    </Application.Resources>

</Application>

That’s all for the basic resources declaration. From now you will be able to access your "BasicShape1" template like this

Application.Current.Resources["BasicShape1"]

Of course you can define more than one resource dictionary but the access path will be as mentioned. That’s a perfect stuff I think. To get more information on resource dictionaries and merged resources usage you should really reference the MSDN library.

 

Optimizing geometry on the canvas

For the previous article on shape connectors I used a separate Path class placed to canvas for each LineGeometry object. According to this dummy approach I had to handle the layering stuff. As both start and end points of each line geometry were attached to the center of the opposite thumbs, Thumb objects had to be placed on the top layer (each time ZIndex was set to 1). I decided to eliminate this complexity and remove a lot of useless code by using the native WPF layout features.

Path class can receive a lot of different stuff if you dig MSDN a bit. For the optimization purposes I used exactly GeometryGroup. It is a collection of Geometry, so in our case it can simply be the collection LineGeometry. As our thumbs will concentrate on the starting and ending lines assigned to them rather that on paths, we can freely use only one Path object for holding the entire collection of lines placed on the canvas.

In this case we don’t have a strong need of creating a Path object at runtime. Declaring it on the window we also get rid of the layout problems I’ve mentioned above. Each time we will add a UIElement (Thumb class in our case) to the canvas it will be automatically placed above the Path object and so above all the line connectors it is holding. That’s perfect isn’t it? 😉

So the xaml for the window will be as following

<Window x:Class="ShapeConnectors.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:ShapeConnectors"
    Title="Window1" Height="376" Width="801" Loaded="Window_Loaded">
    
    <Canvas Name="myCanvas">
        <Path Stroke="Black" StrokeThickness="1">
            <Path.Data>
                <GeometryGroup x:Name="connectors"/>
            </Path.Data>
        </Path>        
        <my:MyThumb x:Name="myThumb1" Title="User 1" DragDelta="onDragDelta" Canvas.Left="270" Canvas.Top="63.75" Template="{StaticResource BasicShape1}"/>
        <my:MyThumb x:Name="myThumb2" Title="User 2" DragDelta="onDragDelta" Canvas.Left="270" Canvas.Top="212.5" Template="{StaticResource BasicShape1}"/>
        <my:MyThumb x:Name="myThumb3" Title="User 3" DragDelta="onDragDelta" Canvas.Left="430" Canvas.Top="212.5" Template="{StaticResource BasicShape1}"/>
        <my:MyThumb x:Name="myThumb4" Title="User 4" DragDelta="onDragDelta" Canvas.Left="430" Canvas.Top="63.75" Template="{StaticResource BasicShape1}"/>
        <Button Canvas.Left="15" Canvas.Top="16" Height="22" Name="btnNewAction" Width="75" Click="btnNewAction_Click">new action</Button>
        <Button Canvas.Left="15" Canvas.Top="47" Height="23" Name="btnNewLink" Width="75" Click="btnNewLink_Click">new link</Button>
    </Canvas>
</Window>

As you can see, I’ve named the geometry group "connectors" so that it can be freely used from code without touching the Path object itself. Again I’ve implemented 4 sample objects predefined. Thumb declaration is extended with custom properties, this will be discussed later on.

We will need two buttons for current functionality implementation. One button will allow us to add new "Action" object to the canvas. Second buttons will allow us to visually link two objects with a line using the mouse.

 

Optimizing our extended Thumb class

At previous article I’ve extended basic Thumb class to hold the lines information. We used two collections for storing start and end lines to have a possibility of correct updating line positions upon moving the object across canvas. All the linking functionality was placed in the main program and this is the point of optimization and encapsulation.

As for this time our Thumbs can be distinguished only by title and icon image, we extend "MyThumb" class for supporting two new properties "Title" and "ImageSource"

 #region Properties
        public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MyThumb), new UIPropertyMetadata(""));
        public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(string), typeof(MyThumb), new UIPropertyMetadata(""));
                        
        // This property will hanlde the content of the textblock element taken from control template
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // This property will handle the content of the image element taken from control template
        public string ImageSource
        {
            get { return (string)GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, value); }
        }

        public List<LineGeometry> EndLines { get; private set; }
        public List<LineGeometry> StartLines { get; private set; } 
#endregion

This is the most common way of implementing dependency properties, for more detailed information you should better refer to MSDN. I used this approach to be able of further binding and declaring the values for the properties in xaml.

As all our Thumbs will refer to the same or nearly the same control template we can extend the template applying logic to setup the template’s elements. Here’s what we do

// Upon applying template we apply the "Title" and "ImageSource" properties to the template elements.
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    // Access the textblock element of template and assign it if Title property defined
    if (this.Title != string.Empty)
    {
        TextBlock txt = this.Template.FindName("tplTextBlock", this) as TextBlock;
        if (txt != null)
            txt.Text = Title;
    }

    // Access the image element of our custom template and assign it if ImageSource property defined
    if (this.ImageSource != string.Empty)
    {
        Image img = this.Template.FindName("tplImage", this) as Image;
        if (img != null)
            img.Source = new BitmapImage(new Uri(this.ImageSource, UriKind.Relative));
    }
}

This is too simple and well commented I think to dwell on it anymore 😉

 

Concentrating on objects being connected

Though the purpose of the article is to show how to draw the connection lines manually using mouse we should be always concentrated on the Thumb objects as the main entities of our future business process. That’s why I’ve implement some additional helper stuff on setting the connection lines based on Thumb object.

For establishing a link we require two Thumbs. We always know the starting Thumb as we began to draw a line from it’s position and we define the end line to set the end of the connector. So the proper logic will be: "Link my current object to this one". Speaking C# our Thumb class will have the following

#region Linking logic
// This method establishes a link between current thumb and specified thumb.
// Returns a line geometry with updated positions to be processed outside.
public LineGeometry LinkTo(MyThumb target)
{
    // Create new line geometry
    LineGeometry line = new LineGeometry();
    // Save as starting line for current thumb
    this.StartLines.Add(line);
    // Save as ending line for target thumb
    target.EndLines.Add(line);
    // Ensure both tumbs the latest layout
    this.UpdateLayout();
    target.UpdateLayout();
    // Update line position
    line.StartPoint = new Point(Canvas.GetLeft(this) + this.ActualWidth / 2, Canvas.GetTop(this) + this.ActualHeight / 2);
    line.EndPoint = new Point(Canvas.GetLeft(target) + target.ActualWidth / 2, Canvas.GetTop(target) + target.ActualHeight / 2);
    // return line for further processing
    return line;
}

// This method establishes a link between current thumb and target thumb using a predefined line geometry
// Note: this is commonly to be used for drawing links with mouse when the line object is predefined outside this class
public bool LinkTo(MyThumb target, LineGeometry line)
{
    // Save as starting line for current thumb
    this.StartLines.Add(line);
    // Save as ending line for target thumb
    target.EndLines.Add(line);
    // Ensure both tumbs the latest layout
    this.UpdateLayout();
    target.UpdateLayout();
    // Update line position
    line.StartPoint = new Point(Canvas.GetLeft(this) + this.ActualWidth / 2, Canvas.GetTop(this) + this.ActualHeight / 2);
    line.EndPoint = new Point(Canvas.GetLeft(target) + target.ActualWidth / 2, Canvas.GetTop(target) + target.ActualHeight / 2);
    return true;
} 
#endregion

As you can see, the first method returns a LineGeometry object upon establishing connection. It returns us a line we can further process in any way required.

This is how I’ve setup the predefined connectors to be displayed for 4 Thumb objects on "Window Load" event

// Setup connections for predefined thumbs            
connectors.Children.Add(myThumb1.LinkTo(myThumb2));
connectors.Children.Add(myThumb2.LinkTo(myThumb3));
connectors.Children.Add(myThumb3.LinkTo(myThumb4));
connectors.Children.Add(myThumb4.LinkTo(myThumb1));

Very easy isn’t it? 🙂 From now we are dealing with our Thumb objects having the connection functionality encapsulated.

That’s all as for the preparation part to support easy connectors drawing. Complete sources can be found at the end of the article.

 

Drawing connection lines on the canvas manually

As any other mouse drawing support we basically need to handle three mouse states:

1. Mouse Down – define the start point of the line geometry and initialize the drawing procedure

2. Mouse Move – define the end point of the line geometry

3. Mouse Up – define the end point of the line geometry and finalize the drawing procedure

But we don’t intend to create a drawing tool we need to connect two definite objects. So we should allow starting to draw a connector line exactly from one Thumb object, and we should allow ending to draw exactly on another Thumb object. In all other ways drawing procedure is prohibited or finished.

As WPF mouse events allow us to quickly get the element that was clicked the implementation becomes rather trivial.

In the previous sample I’ve implemented the simple "Add new action" mode for placing Thumb objects to the canvas. Let’s define another mode "Add new link" for enabling the connector drawing mode. It will consist of two flags

// flag for enabling "New link" mode
bool isAddNewLink = false;
// flag that indicates that the drawing link with a mouse started
bool isLinkStarted = false;

We also need to temporary global variables for handling the starting Thumb and a line drawn

// variable to hold the thumb drawing started from
MyThumb linkedThumb;
// Line drawn by the mouse before connection established
LineGeometry link;

To enter the drawing mode we set the "isAddNewLink" value to "true" with the appropriate button click event.

Our "PreviewMouseLeftButtonDown" event handler for the Window is extended to have the following code

// Is adding new link and a thumb object is clicked...
if (isAddNewLink && e.Source.GetType() == typeof(MyThumb))
{                
    if (!isLinkStarted)
    {
        if (link == null || link.EndPoint != link.StartPoint)
        {
            Point position = e.GetPosition(this);
            link = new LineGeometry(position, position);
            connectors.Children.Add(link);
            isLinkStarted = true;
            linkedThumb = e.Source as MyThumb;
            e.Handled = true;
        }
    }
}

At first we check of course whether the clicked element is our Thumb object to start drawing. Then is we haven’t already started drawing we do the initial configuring. We initialize our temporary Line Geometry object and current selected thumb. To view the line on the canvas we should immediately add it to our Path object. But it cannot be the final connector because user can release the mouse button anywhere in unpredictable place. So our task will be to accept the coordinates of the line when the mouse button is up on the Thumb or exclude the line geometry object from the path and remove the line from the canvas. That’s what the "isLinkStarted" variable is defined for.

This is how I’ve implemented the "PreviewMouseLeftButtonUp" event handler for the window

 

// Handles the mouse up event applying the new connection link or resetting it
void Window1_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    // If "Add link" mode enabled and line drawing started (line placed to canvas)
    if (isAddNewLink && isLinkStarted)
    {
        // declare the linking state
        bool linked = false;
        // We released the button on MyThumb object
        if (e.Source.GetType() == typeof(MyThumb))
        {
            MyThumb targetThumb = e.Source as MyThumb;
            // define the final endpoint of line
            link.EndPoint = e.GetPosition(this);
            // if any line was drawn (avoid just clicking on the thumb)
            if (link.EndPoint != link.StartPoint && linkedThumb != targetThumb)
            {
                // establish connection
                linkedThumb.LinkTo(targetThumb, link);
                // set linked state to true
                linked = true;
            }
        }
        // if we didn't manage to approve the linking state
        // button is not released on MyThumb object or double-clicking was performed
        if (!linked)
        {
            // remove line from the canvas
            connectors.Children.Remove(link);
            // clear the link variable
            link = null;
        }
        
        // exit link drawing mode
        isLinkStarted = isAddNewLink = false;
        // configure GUI
        btnNewAction.IsEnabled = btnNewLink.IsEnabled = true;
        Mouse.OverrideCursor = null;
        e.Handled = true;
    }
    this.Title = "Links established: " + connectors.Children.Count.ToString();
}

And at last the most simple event handler in my sample is "PreviewMouseMove" event handler for the Window

// Handles the mouse move event when dragging/drawing the new connection link
void Window1_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (isAddNewLink && isLinkStarted)
    {
        // Set the new link end point to current mouse position
        link.EndPoint = e.GetPosition(this);
        e.Handled = true;
    }
}

Here we see the line dynamically changing it’s position and length upon mouse moves with a pressed left button.

Here’s some screens

connectors_1 connectors_2

All connection lines are automatically dragged with the thumbs. Guess you will like that 😉

 

Some of the features I’m going to prepare for the next article:

1. Implement Thumbs that can be connected predefined times to other objects (for example only one connection allowed)

2. Captions or icons for the connectors (placed always at the center of the line)

3. Different templates for Thumbs

4. Eliminate the possibility of multiple connections of two same thumbs

5. Deleting of connectors

and some other stuff

 

Source code for the article

Make a really dynamic plugin using multiple Application Domains


Note: This article assumes that you have basic knowledge of creating and using plugins. It won’t cover the whole process of plugin development. There are too many articles in the internet towards this topic to make another copy of sources 😉 I want to dwell on some specific sugar that could possibly help solving problems described below.

Yesterday I was playing with a windows service based application that was capable of dynamic loading and execution of .net assemblies. The loading and execution process was perfect except one ugly though common thing that can be found in many plugin based applications. After the assembly was loaded and all the required static methods executed successfully I couldn’t update it or delete. This is obvious behavior when loading assemblies in .net by means of "type loaders", usage of "Assembly.Load()" etc. and shouldn’t be regarded as bug.

The "Assembly.Load" function loads assembly to current application domain so you won’t be able to do a lot untill your plugin host application is unloaded. Event don’t think of hot changes of assemblies with your host running. To override this you should move from kid’s type loaders to multiple application domains 🙂 I’ll show how to do that.

Additionally you will see how the multiple AppDomains approach helps solving problems with single threaded applications like XNA games. XNA Framework based game can be easily converted to assembly, but it cannot be simply executed by the plugin host. It doesn’t assume being loaded from another thread, you cannot apply all the stuff from your favorite own plugin framework. It also needs the power of multiple AppDomains plus some minor changes in your game code.

So let’s start implementation part…

First we define some dummy IPlugin interface for external assemblies. It will be placed to a PluginLib.dll assembly common either for host application and external plugins.

IPlugin.cs

using System;

namespace PluginLib
{    
    public interface IPlugin : IDisposable
    {
        string Name { get; }
        void Run(object param);
    }
}

 

Here we have a name of the plugin for future UI integration purposes and execution call with some parameter. You can use your own existing IPlugin interface only don’t forget to inherit IDisposable interface.

Additionally we implement our own custom exception for future purposes. It can also be modified to suit your needs.

PluginException.cs

using System;

namespace PluginLib
{
    [Serializable]
    public class PluginException : Exception
    {
        public PluginException() { }
        public PluginException(Exception ex) : base("Plugin Exception", ex) { }
    }
}

The most important thing I want to dwell is the plugin host. The significant part of the projects will be placed in it. For the details for using "AppDomainManager" and "AppDomainSetup" you should refer to MSDN.

Note that your hosting logic class MUST inherit MarshalByRefObject so the host application can create an object of the PluginHost inside other AppDomains, also it should implement IDisposable to cleanup any resources.

We can definitely split the process of plugin execution in the folowing steps:

1. Create and setup new Application Domain for the required assembly binding a .config file if found

2. Add domain to the internal collection of loaded domains.

3. Load the required assembly from dedicated appdomain created

4. Initialize a plugin from assembly

5. Do whatever you want, in my sample I will call "Run(object param)" method to start doing something

 

1-2. Creating and setting up new Application Domain, loading domain to internal collection

Assume that we already defined a global collection variable for storing all the application domains loaded

// The list of loaded application domains
List<AppDomain> _appDomains = new List<AppDomain>(); 

Our host will contain only one public method called RunPlugin accepting one string argument as the physical path of the plugin assembly to load

 

/// <summary>
/// This function is what the Host Application is going to call.
/// </summary>
/// <param name="path">The plugin (*.dll) file</param>
public void RunPlugin(string path)
{
    //Creating the appdomain manager
    AppDomainManager manager = new AppDomainManager();
    //Check if there is any *.dll.config file
    string configFileName = string.Format("{0}.config", path);
    AppDomainSetup setup = new AppDomainSetup();
    //Enable shadow copying, so the Plugin files are not locked
    setup.ShadowCopyFiles = "true";
    setup.LoaderOptimization = LoaderOptimization.MultiDomain;
    //if the config file exists, load it into the appdomain
    if (File.Exists(configFileName))
        setup.ConfigurationFile = configFileName;
    //Creating the AppDomain & adding a reference to it in the _appDomains collection.
    AppDomain domain = manager.CreateDomain(String.Format("AD-{0}", _appDomains.Count), null, setup);
    _appDomains.Add(domain);
    /*
     * This important.
     * Here we are initiating an instance of PluginHost inside the new AppDomain.
     * this instance will give us control from the host appdomain to load & run the plugins inside the new appdomain
     */
    PluginHost remoteHost = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(typeof(PluginHost)).FullName, typeof(PluginHost).ToString()) as PluginHost;

    // Here we run every plugin in a separate thread
    ThreadPool.QueueUserWorkItem(
        delegate(object state)
        {
            try
            {
                //calling the PluginHost object created in the other appdomain.
                remoteHost.Launch(path, domain);
            }
            catch (Exception ex)
            {
                _appDomains.Remove(domain);
                AppDomain.Unload(domain);
            }
        });
} 

The code above is well commented, so I’d like to dwell on "Launch" method of our PluginHost class

 

3-5. Plugin execution from dedicated AppDomain

/// <summary>
/// this function will load the specified assembly file in the specified AppDomain, then it is going to run any class that implements IPlugin inside.
/// </summary>
/// <param name="assemblyPath">The assembly (*.dll) file which contains the plugin(s)</param>
/// <param name="domain">The appdomain in which the assembly is going to be loaded</param>
void Launch(string assemblyPath, AppDomain domain)
{
    //Loading the assembly file into the appdomain
    Assembly pluginAssembly = domain.Load(AssemblyName.GetAssemblyName(assemblyPath));
    domain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
    {
        AppDomain d = sender as AppDomain;
        string path = Path.Combine(@"C:PluginsReferences", args.Name.Split(',')[0] + ".dll");
        return d.Load(path);
    };

    RaiseEvent(String.Format("Hello from domain {0}", domain.FriendlyName));
    //Searching for Plugin types inside the loaded assembly
    foreach (Type type in pluginAssembly.GetTypes())
    {
        if (!type.IsClass) continue;

        if (type.FindInterfaces(delegate(Type t, object filter) { return t == filter as Type; }, typeof(IPlugin)).Length > 0)
        {
            //Using block will make sure that the plugins will run cleanup code after execution
            using (IPlugin plugin = pluginAssembly.CreateInstance(type.ToString()) as IPlugin)
            {
                plugin.Run("Here we place some params for the external plugin");
            }
        }
    }
}

This part should be definitly configured to suit you needs. For quick implementation purposes I’ve hardcoded some parts.

Upon loading our plugin assembly we might encounter references to third party libraries not known to our host application. This must be resolved somehow. We attach to AssemblyResolve event of our dedicated AppDomain to reference a folder where all the needed .dll files would be present. Assume that this is a separate folder for all the third party stuff. Here I’ve defined "C:PluginsReferences" for that purposes.

You can also implement some event raising functionality to inform your application about something. Here you can see the call of "RaiseEvent" method. It is as simple as possible:

event PluginEventHandler _pluginEvent;
public event PluginEventHandler PluginChange
{
    add { _pluginEvent += value; }
    remove { _pluginEvent -= value; }
}

private void RaiseEvent(string message)
{
    if (_pluginEvent != null)
        _pluginEvent(message);
}

And at last we use the old fashioned type loader as it is always advised in the internet 😉 creating instances and executing methods.

 

Disposing Plugin Host

We should also take care of releasing our AppDomain collection. The disposing is too boring to comment

#region IDisposable Members

public void Dispose()
{
    _appDomains.ForEach(
        delegate(AppDomain domain)
        {
            if (!domain.IsFinalizingForUnload())
                AppDomain.Unload(domain);
        });
}

#endregion

 

How it is used in your application

1. Preparing plugin. Turning application to plugin for testing purposes.

1. Prepare any simple Windows Forms Application, put some controls on it and so on…

2. Add reference to our PluginLib assembly

3. Open project properties and change the output type to "Class Library"

 

In the default Program.cs file you might see the following common picture

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

Let’s change it implementing some king of plugin loader

//static class Program
//{
//    /// <summary>
//    /// The main entry point for the application.
//    /// </summary>
//    [STAThread]
//    static void Main()
//    {
//        Application.EnableVisualStyles();
//        Application.SetCompatibleTextRenderingDefault(false);
//        Application.Run(new Form1());
//    }
//}

public class SimpleForm : IPlugin
{
    #region IPlugin Members

    public string Name
    {
        get { return "SimpleForm"; }
    }

    public void Run(object param)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
    }

    #endregion
}

It’s very simple, isn’t it? 🙂

2. Implementing simple Plugin Host Application

Create folder "C:Plugins" and put there your WindowsApplication1.dll (see code above) and PluginLib.dll

I’ll take the basic console application to dwell only on PluginHost implementation if you don’t mind 😉

Don’t forget to add reference to our common PluginLib assembly

public static void Main(string[] args)
{
    //Using block will make sure that the cleanup code of PluginHost is executed
    using (PluginHost host = new PluginHost())
    {
        host.PluginChange += new PluginEventHandler(host_PluginChange);
        host.RunPlugin(@"C:PluginsWindowsApplication1.dll");
        Console.ReadKey();
    }
}

static void host_PluginChange(string message)
{
    Console.WriteLine(message);
}
It's up you what the logic of your hosting application will be. 
Here you create a new host, link to the simple host event and finally execute the plugin. 
In this case you will see the application designed.
Note: When you plugin is executed and you see windows form you can freely delete the plugin, change it, update and so on.
Source code for the article

WPF. Simple adorner usage with drag and resize operations


After posting my previous post I realized that it would be much better to provide the working sample instead of pure idea of class changed to support canvas. So I created a dummy sample for resizing adorner. Additionally I’ve added support for dragging the elements on the canvas, element selection providing adorner for selected element.

Sample window contains two buttons and a textbox. Feel free to add something else. Upon clicking the element you see the selection adorner giving you ther possibility of resizing the element. I used the changed version of ResizingAdorner (see my previous post) to provide more "Visual Studio" look of resizing.

adorners_1 adorners_2

Unlike MSDN library Resizing Adorner sample, you each corner thumb resizes only it’s part of the element without affecting the position.

Note: This sample was prepared using Visual Studio Team System 2008 beta 2

Here’s the xaml for the window. As I’ve mentioned there are two buttons and a textbox.

Window1.xaml

<Window x:Class="adorners.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
    
    <Canvas Name="myCanvas">
        <Button Canvas.Left="49" Canvas.Top="21" Height="23" Name="button1" Width="75">Button1</Button>
        <Button Height="21" Canvas.Left="147" Canvas.Top="23" Width="75">Button2</Button>
        <TextBox Canvas.Left="64" Canvas.Top="60" Height="24" Name="textBox1" Width="128" />
    </Canvas>
    
</Window>

 

Again the changed resizing adorner class from my previous post

ResizingAdorner.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace adorners
{
    public class ResizingAdorner : Adorner
    {
        // Resizing adorner uses Thumbs for visual elements.  
        // The Thumbs have built-in mouse input handling.
        Thumb topLeft, topRight, bottomLeft, bottomRight;

        // To store and manage the adorner's visual children.
        VisualCollection visualChildren;

        // Initialize the ResizingAdorner.
        public ResizingAdorner(UIElement adornedElement)
            : base(adornedElement)
        {                
            visualChildren = new VisualCollection(this);

            // Call a helper method to initialize the Thumbs
            // with a customized cursors.
            BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
            BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);

            // Add handlers for resizing.
            bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
            bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
            topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
            topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
        }

        // Handler for resizing from the bottom-right.
        void HandleBottomRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);
        }

        // Handler for resizing from the top-right.
        void HandleTopRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
        }

        // Handler for resizing from the top-left.
        void HandleTopLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;
            Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));
            
            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
        }

        // Handler for resizing from the bottom-left.
        void HandleBottomLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;            
            Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));
        }

        // Arrange the Adorners.
        protected override Size ArrangeOverride(Size finalSize)
        {
            // desiredWidth and desiredHeight are the width and height of the element that's being adorned.  
            // These will be used to place the ResizingAdorner at the corners of the adorned element.  
            double desiredWidth = AdornedElement.DesiredSize.Width;
            double desiredHeight = AdornedElement.DesiredSize.Height;
            // adornerWidth & adornerHeight are used for placement as well.
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));

            // Return the final size.
            return finalSize;
        }

        // Helper method to instantiate the corner Thumbs, set the Cursor property, 
        // set some appearance properties, and add the elements to the visual tree.
        void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
        {
            if (cornerThumb != null) return;

            cornerThumb = new Thumb();

            // Set some arbitrary visual characteristics.
            cornerThumb.Cursor = customizedCursor;
            cornerThumb.Height = cornerThumb.Width = 10;
            cornerThumb.Opacity = 0.40;
            cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);

            visualChildren.Add(cornerThumb);
        }

        // This method ensures that the Widths and Heights are initialized.  Sizing to content produces
        // Width and Height values of Double.NaN.  Because this Adorner explicitly resizes, the Width and Height
        // need to be set first.  It also sets the maximum size of the adorned element.
        void EnforceSize(FrameworkElement adornedElement)
        {
            if (adornedElement.Width.Equals(Double.NaN))
                adornedElement.Width = adornedElement.DesiredSize.Width;
            if (adornedElement.Height.Equals(Double.NaN))
                adornedElement.Height = adornedElement.DesiredSize.Height;

            FrameworkElement parent = adornedElement.Parent as FrameworkElement;
            if (parent != null)
            {
                adornedElement.MaxHeight = parent.ActualHeight;
                adornedElement.MaxWidth = parent.ActualWidth;
            }
        }
        // Override the VisualChildrenCount and GetVisualChild properties to interface with 
        // the adorner's visual collection.
        protected override int VisualChildrenCount { get { return visualChildren.Count; } }
        protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
    }
}

 

And finally the code behind for the window.

Window1.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;

namespace adorners
{
    public partial class Window1 : Window
    {
        AdornerLayer aLayer;

        bool _isDown;
        bool _isDragging;
        bool selected = false;
        UIElement selectedElement = null;

        Point _startPoint;
        private double _originalLeft;
        private double _originalTop;

        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.MouseLeftButtonDown += new MouseButtonEventHandler(Window1_MouseLeftButtonDown);
            this.MouseLeftButtonUp += new MouseButtonEventHandler(DragFinishedMouseHandler);
            this.MouseMove += new MouseEventHandler(Window1_MouseMove);
            this.MouseLeave += new MouseEventHandler(Window1_MouseLeave);
            
            myCanvas.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(myCanvas_PreviewMouseLeftButtonDown);
            myCanvas.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragFinishedMouseHandler);
        }

        // Handler for drag stopping on leaving the window
        void Window1_MouseLeave(object sender, MouseEventArgs e)
        {
            StopDragging();
            e.Handled = true;
        }

        // Handler for drag stopping on user choise
        void DragFinishedMouseHandler(object sender, MouseButtonEventArgs e)
        {
            StopDragging();
            e.Handled = true;
        }

        // Method for stopping dragging
        private void StopDragging()
        {
            if (_isDown)
            {
                _isDown = false;
                _isDragging = false;
            }
        }
        
        // Hanler for providing drag operation with selected element
        void Window1_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isDown)
            {
                if ((_isDragging == false) &&
                    ((Math.Abs(e.GetPosition(myCanvas).X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance) ||
                    (Math.Abs(e.GetPosition(myCanvas).Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)))
                    _isDragging = true;

                if (_isDragging)
                {
                    Point position = Mouse.GetPosition(myCanvas);
                    Canvas.SetTop(selectedElement, position.Y - (_startPoint.Y - _originalTop));
                    Canvas.SetLeft(selectedElement, position.X - (_startPoint.X - _originalLeft));
                }
            }
        }        
                        
        // Handler for clearing element selection, adorner removal
        void Window1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {            
            if (selected)
            {
                selected = false;
                if (selectedElement != null)
                {
                    aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
                    selectedElement = null;
                }                
            }            
        }

        // Handler for element selection on the canvas providing resizing adorner
        void myCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Remove selection on clicking anywhere the window
            if (selected)
            {
                selected = false;
                if (selectedElement != null)
                {
                    // Remove the adorner from the selected element
                    aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);                    
                    selectedElement = null;
                }
            }

            // If any element except canvas is clicked, 
            // assign the selected element and add the adorner
            if (e.Source != myCanvas)
            {
                _isDown = true;
                _startPoint = e.GetPosition(myCanvas);

                selectedElement = e.Source as UIElement;

                _originalLeft = Canvas.GetLeft(selectedElement);
                _originalTop = Canvas.GetTop(selectedElement);

                aLayer = AdornerLayer.GetAdornerLayer(selectedElement);
                aLayer.Add(new ResizingAdorner(selectedElement));
                selected = true;
                e.Handled = true;
            }
        }
    }
}

 

The main plot of the post was to give the basic idea of implemeting adorners with such the common tasks as resizing and dragging functionality. Of cource it is obvious that code behind sources are far away from the real life application but it is rather easy play with further.

Thanks for paying attention to this post.

Source code for the article

The WPF Resizing Adorner for Canvas


MSDN library for Visual Studio 2008 beta 2 gives three samples of using adorners: "Simple Circle Adorner", "Add and Remove Adorners" and "Resizing Adorner". All the samples were designed to work with the Grid control so the behavior is not very good when we move to the Canvas or some other panel.

Here’s the code for the Canvas usage. No optimizations and other professional stuff, just the pure idea.

Red lines are exactly the quick patch for the original sample

ResizingAdorner.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace adorners
{
    public class ResizingAdorner : Adorner
    {
        // Resizing adorner uses Thumbs for visual elements. 
        // The Thumbs have built-in mouse input handling.
        Thumb topLeft, topRight, bottomLeft, bottomRight;

        // To store and manage the adorner’s visual children.
        VisualCollection visualChildren;

        // Initialize the ResizingAdorner.
        public ResizingAdorner(UIElement adornedElement)
            : base(adornedElement)
        {           
            visualChildren = new VisualCollection(this);

            // Call a helper method to initialize the Thumbs
            // with a customized cursors.
            BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
            BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);

            // Add handlers for resizing.
            bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
            bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
            topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
            topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
        }

        // Handler for resizing from the bottom-right.
        void HandleBottomRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it’s larger
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);
        }

        // Handler for resizing from the top-right.
        void HandleTopRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it’s larger
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height – args.VerticalChange, hitThumb.DesiredSize.Height);

            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height – args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old – (height_new – height_old));

        }

        // Handler for resizing from the top-left.
        void HandleTopLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it’s larger
            // than the width or height of an adorner, respectively.

            //adornedElement.Width = Math.Max(adornedElement.Width – args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height – args.VerticalChange, hitThumb.DesiredSize.Height
);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width – args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;
            Canvas.SetLeft(adornedElement, left_old – (width_new – width_old));
            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height – args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old – (height_new – height_old));
        }

        // Handler for resizing from the bottom-left.
        void HandleBottomLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it’s larger
            // than the width or height of an adorner, respectively.
            //adornedElement.Width = Math.Max(adornedElement.Width – args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width – args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;           
            Canvas.SetLeft(adornedElement, left_old – (width_new – width_old));

        }

        // Arrange the Adorners.
        protected override Size ArrangeOverride(Size finalSize)
        {
            // desiredWidth and desiredHeight are the width and height of the element that’s being adorned. 
            // These will be used to place the ResizingAdorner at the corners of the adorned element.
 
            double desiredWidth = AdornedElement.DesiredSize.Width;
            double desiredHeight = AdornedElement.DesiredSize.Height;
            // adornerWidth & adornerHeight are used for placement as well.
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            topRight.Arrange(new Rect(desiredWidth – adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight – adornerHeight / 2, adornerWidth, adornerHeight));
            bottomRight.Arrange(new Rect(desiredWidth – adornerWidth / 2, desiredHeight – adornerHeight / 2, adornerWidth, adornerHeight));

            // Return the final size.
            return finalSize;
        }

        // Helper method to instantiate the corner Thumbs, set the Cursor property,
        // set some appearance properties, and add the elements to the visual tree.

        void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
        {
            if (cornerThumb != null) return;

            cornerThumb = new Thumb();

            // Set some arbitrary visual characteristics.
            cornerThumb.Cursor = customizedCursor;
            cornerThumb.Height = cornerThumb.Width = 10;
            cornerThumb.Opacity = 0.40;
            cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);

            visualChildren.Add(cornerThumb);
        }

        // This method ensures that the Widths and Heights are initialized.  Sizing to content produces
        // Width and Height values of Double.NaN.  Because this Adorner explicitly resizes, the Width and Height
        // need to be set first.  It also sets the maximum size of the adorned element.

        void EnforceSize(FrameworkElement adornedElement)
        {
            if (adornedElement.Width.Equals(Double.NaN))
                adornedElement.Width = adornedElement.DesiredSize.Width;
            if (adornedElement.Height.Equals(Double.NaN))
                adornedElement.Height = adornedElement.DesiredSize.Height;

            FrameworkElement parent = adornedElement.Parent as FrameworkElement;
            if (parent != null)
            {
                adornedElement.MaxHeight = parent.ActualHeight;
                adornedElement.MaxWidth = parent.ActualWidth;
            }
        }
        // Override the VisualChildrenCount and GetVisualChild properties to interface with
        // the adorner’s visual collection.
        protected override int VisualChildrenCount { get { return visualChildren.Count; } }
        protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
    }
}

 

That’s all. Now resizing adorner works as it is supposed to work. The element is resized correctly.

Have a nice day.