Jan 242012
 

Reusability is one of the most underrated aspects of software development. The focus on meeting functional requirements in a product as quickly as possible often overshadows the reusability and maintainability part. Code duplication, poor adherence to design are often symptoms of such development which increase costs in the long term in favour of short term benefits.

WPF encourages creation of reusable controls, primarily by its support for creation of “lookless controls” where the client of the control is not only able to reuse the control, but also completely alter its appearance. The control in essence is just a bundle of logic with a default template. Its upto the client whether to accept this default skin or override it with one of his own. Though this concept looks similar to themes and skins in other technologies, its extremely powerful that you can alter the visual appearance of the control at a granular level. If the default template defines a button and a textbox, that can easily be changed to an filled rectangle with a Linear Gradient and a TextBlock to display the data.

Difference between User Controls and Custom Controls.

Custom controls arent the only type of control library that WPF offers. There is also the User Control library. The difference between the two lies in our end purpose. If we are simply looking to bundle a few controls together and provide basic customization to the end user, then User Controls are the way to go. They are also much more easier to use than Custom Controls. However, if your aim is to provide full customization capability to the developer who consumes your control, then Custom Controls are much better.

Dependency Properties

Dependency Properties are quite different from the conventional properties which we use in C#, and are exclusive to WPF. They do not belong to any particular class and their value can be set sources other than just the class itself. The values could come from their default values, styles, themes, callbacks etc. They also support Data binding so your UI elements can directly bind to them and update whenever the value changes, like how properties in classes implementing INotifyPropertyChanged events behave.

Custom Control Example

The custom control example I build is a filled rectangle with a slider. As you increase the slider the fill percentage of the rectangle increases as well. Here is an illustration

The control is quite simple. There are two main properties here which are exposed to the outer world – The FillColor and the EmptyColor denoting the colors of the rectangle. The third property is the Value of slider which is used within the control, but that too could be exposed out of the control. Lets see the code for the control. ( Notice the absence of any UI stuff)

public class FilledBarControl : Control
    {
        public FilledBarControl()
        {
            DataContext = this;
        }
        
        static FilledBarControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(FilledBarControl), new FrameworkPropertyMetadata(typeof(FilledBarControl)));
          
        }

        public static readonly DependencyProperty EmptyColorProperty = DependencyProperty.Register("EmptyColor", typeof(Color), typeof(FilledBarControl), new UIPropertyMetadata((Color)Colors.Transparent));

        public Color EmptyColor
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Color)GetValue(EmptyColorProperty);
            }
            set
            {
                SetValue(EmptyColorProperty, value);
            }
        }
        

        public static readonly DependencyProperty FillColorProperty = DependencyProperty.Register("FillColor", typeof(Color), typeof(FilledBarControl), new UIPropertyMetadata((Color)Colors.Red));

        public Color FillColor
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Color)GetValue(FillColorProperty);
            }
            set
            {
                SetValue(FillColorProperty, value);
            }
        }
    }

As you can see, there are two steps to declaring a dependency property – First is the registering of the property using the DependencyProperty.Register method and the second is the definition of the getter and setter methods. The naming convention for the DependencyObject is to append “Property” after the name of the Dependecy Property. Hence here the object becomes FillColorProperty. You can also define the default values for the Property in the Register method. It needs to be passed inside the constructor of the PropertyMetadata object. I used Red Color here, so if the developer doesnt pass any value for the FillColor, the control automatically chooses Red.

Like written before, a custom control is simply defined in code, it doesn’t necessarily need a UI to exist – the UI can be supplied by the developer using the control. The default look and feel for the control is defined in a separate file – Generic.xaml inside the themes folder. Here is the xaml for the filled Bar control.

<Style TargetType="{x:Type local:FilledBarControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:FilledBarControl}">
                    <UniformGrid Columns="1">
                        <Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" >                           
                            <Rectangle Height="{TemplateBinding Height}"
                            Width="{TemplateBinding Width}">
                            <Rectangle.Fill>
                                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                                    <GradientStop Color="{Binding Path=FillColor}"
                        Offset="0"/>
                                    <GradientStop Color="{Binding Path=FillColor}"
                        Offset="{Binding ElementName=slider, Path=Value}"/>
                                    <GradientStop Color="{Binding Path=EmptyColor}" 
                        Offset="{Binding ElementName=slider, Path=Value}"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                        </Border>

                        <Slider x:Name="slider" Width="200" Height="50" 
            Minimum="0" Maximum="1" Value="0.2"/>
                    </UniformGrid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

The XAML is just a control template containing a Uniform Grid. One of the rows is the Rectangle and the other a Slider for the value. The rectangle’s fill tag is a LinearGradientBrush with two phases. One from the Start Point (0,0) to the Value (bound to the Slider’s value) and another which starts from the Value to the end point. This gradient gives the impression of a filled rectangle whose fill percentage changes as the slider is dragged.

Now, how can a host application change the appearance of the control? There are two ways – one using the Dependency Properties and other completely overriding the Control Template itself. As you can recall, there were two dependency Properties defined in the FilledBar.cs – The FillColor and the EmptyColor. Both these properties appear in the Intellisense while defining the control in the XAML. An example of such customization would be

         <FilledBar:FilledBarControl HorizontalAlignment="Center" 
                                    VerticalAlignment="Center"  
                                    Height="100" Width="200" 
                                    BorderThickness="2" BorderBrush="#003300" 
                                    FillColor="Maroon" EmptyColor="LightGreen" />

This is how the control now looks. Note that both colours have changed as per our definition

The second form of customization is what makes Custom Controls so much more powerful than ordinary User Controls. Lets assume that a rectangular fill bar doesnt suit my requirement and my application would look better with a FilledCircle rather than a FilledBar. Rather than writing the entire control again just for one change, I could just swap out the Rectangle and substitute it with an Ellipse whose Fill is done by a RadialGradientBrush. These changes do not require any changes to the original control itself – a style can be created in the Resource Dictionary and referred to by the code.

            <Style TargetType="{x:Type FilledBar:FilledBarControl}" x:Key="FilledCircle">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type FilledBar:FilledBarControl}">
                            <UniformGrid Columns="1" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" >

                                <Ellipse Height="100" Width="100" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}"
                            >
                                        <Ellipse.Fill>
                                            <RadialGradientBrush >
                                                <GradientStop Color="{Binding Path=FillColor}"
                        Offset="0"/>
                                                <GradientStop Color="{Binding Path=FillColor}"
                        Offset="{Binding ElementName=slider, Path=Value}"/>
                                                <GradientStop Color="{Binding Path=EmptyColor}" 
                        Offset="{Binding ElementName=slider, Path=Value}"/>
                                            </RadialGradientBrush>
                                        </Ellipse.Fill>
                                    </Ellipse>
                              

                            <Slider x:Name="slider" Width="200" Height="50" 
            Minimum="0" Maximum="1" Value="0.2"/>
                        </UniformGrid>
                        </ControlTemplate>

                    </Setter.Value>
                </Setter>
                
            </Style>

The only changes in the above style tag are the changing to ellipse and RadialGradientbrush. Note that instead of using the Border, we just bind the BorderBrush and BorderThickness properties to the Stroke and StrokeThickness properties of the Ellipse. This allows setting of the properties just as we did with the unchanged controls and is much more easier to read. The control is declared in xaml with the style tag referring to our user dictionary which overrides the default style written in the Generic.xaml

        <FilledBar:FilledBarControl HorizontalAlignment="Center" VerticalAlignment="Center" 
                                    Grid.Row="3" Grid.Column="1"  
                                    Style="{StaticResource ResourceKey=FilledCircle}"  
                                    Height="200" Width="200" 
                                    BorderThickness="2" BorderBrush="Brown" 
                                    FillColor="Blue" EmptyColor="Transparent">

Here is how the control looks now.

We can have multiple instance of the same control using different styles and values for Dependency Property. An example of the Host Application

Aug 142011
 

Testing the user interface has to be the most boring part of testing software. Not only is it not fun, it can be horribly repetitive as well with you having to check the same functionality over and over again. The time spent on it could be put to use to far better purposes. Automated Testing tools like NUnit and MS Test were developed in order to counter this. Along came processes like Test Driven development that emphasized the need to write test cases and write testable software. Though all this helped a lot, its benefits were confined mostly to the developer himself, it wasn’t of any use to the testers who again put in redundant effort in testing the UI.

It was then that the idea of automating UI testing starting getting thrown about and many such tools and frameworks appeared in the market. These frameworks essentially provide an API to programmatically access the user interface of Windows applications and gives the developer to simulate the end user’s way of accessing our software. One such framework which I found extremely useful was the UI Automation library from Microsoft. It works pretty well with any UI technology including relatively recent ones such as WPF.

At the core of the UI Automation library is the type AutomationElement – All UI elements in the application get wrapped into this AutomationElement and provide a common way of interacting with the UI elements. For e.g. The application window will be an AutomationElement just like a small button inside the window. Both support different set of Actions but share the commmon types, this makes writing code for UI automation extremely simple.

I wrote a simple calculator application and a console application which tests the calculator giving random values and verifying the output. The XAML Code for the WPF application is:-

    <grid x:Name="LayoutRoot">
        </grid><grid .RowDefinitions>
            <rowdefinition Height="1*" />
            <rowdefinition Height="1*" />
            <rowdefinition Height="1*" />
            <rowdefinition Height="4*" />
        </grid>
        <grid .ColumnDefinitions>
            <columndefinition Width="2*" />
            <columndefinition Width="4*" />
        </grid>
        <label Grid.Row="0" Grid.Column="0" Content="First Number" />
        <textbox Grid.Row="0" Grid.Column="1" AutomationProperties.AutomationId="txtFirstNumber" Text="{Binding FirstNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
        <label Grid.Row="1" Grid.Column="0" Content="Second Number" />
        <textbox Grid.Row="1" Grid.Column="1" AutomationProperties.AutomationId="txtSecondNumber" Text="{Binding SecondNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
        <label Grid.Row="2" Grid.Column="0" Content="Result" />
        <textbox Grid.Row="2" Grid.Column="1" AutomationProperties.AutomationId="txtResult" Text="{Binding Result,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
        
        <grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">
            </grid><grid .ColumnDefinitions>
                <columndefinition Width="1*" />
                <columndefinition Width="1*" />
                <columndefinition Width="1*" />
                <columndefinition Width="1*" />
            </grid>
            <button Grid.Column="0" AutomationProperties.AutomationId="btnAdd" Content="Add" Command="{Binding Add}" />
            <button Grid.Column="1" AutomationProperties.AutomationId="btnSubtract" Content="Subtract" Command="{Binding Subtract}" />
            <button Grid.Column="2" AutomationProperties.AutomationId="btnMultiply" Content="Multiply" Command="{Binding Multiply}" />
            <button Grid.Column="3" AutomationProperties.AutomationId="btnDivide" Content="Divide" Command="{Binding Divide}" />
   

This creates a simple calculator app with four functions Add, Subtract, Multiply and Divide. Here is a screenshot of the app

The viewmodel is pretty simple as well

    public class MainViewModel : ViewModelBase
    {
       
        private double _firstNumber;
        private double _secondNumber;
        private double _result;
        private RelayCommand _add;
        private RelayCommand _multiply;
        private RelayCommand _subtract;
        private RelayCommand _divide;

        public double FirstNumber
        {
            get { return _firstNumber; }
            set
            {
                _firstNumber = value;
                RaisePropertyChanged("FirstNumber");
            }
        }

        public double SecondNumber
        {
            get { return _secondNumber; }
            set
            {
                _secondNumber = value;
                RaisePropertyChanged("SecondNumber");
            }
        }

        public double Result
        {
            get { return _result; }
            set
            {
                _result = value;
                RaisePropertyChanged("Result");
            }
        }

        public ICommand Add
        {
            get { _add = _add ?? new RelayCommand(() => Result = FirstNumber + SecondNumber); return _add; }
        }

        public ICommand Subtract
        {
            get { _subtract = _subtract ?? new RelayCommand(() => Result = FirstNumber - SecondNumber); return _subtract; }
        }

        public ICommand Multiply
        {
            get { _multiply = _multiply ?? new RelayCommand(() => Result = FirstNumber * SecondNumber); return _multiply; }
        }

        public ICommand Divide
        {
            get { _divide = _divide ?? new RelayCommand(() => Result = FirstNumber / SecondNumber); return _divide; }
        }
  }

The project also contains a simple Console application which launches along with the project and automates the testing of the application by entering values in the textboxes and pressing the four buttons and verifying the result values.

namespace CalculatorTest
{
    class TestCalc
    {
        AutomationLibrary library = new AutomationLibrary();

        AutomationElement desktopElement;
        AutomationElement mainWindow;

        public TestCalc()
        {
            desktopElement = GetDesktop();
        mainWindow = library.GetElement(desktopElement, AutomationElement.NameProperty, "Simple Calculator");
        }
        internal void StartTest()
        {
            for (int i = 0; i < 25; i++)
            {
                Random r = new Random();
                double firstNumber = r.Next(1300);
                Thread.Sleep(30);
                double secondNumber = r.Next(1000);
                if (VerifyResult(firstNumber, secondNumber))
                    Console.WriteLine(" All Tests Have Passed for {0} & {1}",firstNumber,secondNumber);
                else
                    Console.WriteLine("Tests have failed for {0} & {1}", firstNumber, secondNumber);
            }

        }

        public bool VerifyResult(double firstNumber,double secondNumber)
        {
            bool allTestPass = true; 
            library.SetValueOnTextBox(mainWindow, AutomationElement.AutomationIdProperty, "txtFirstNumber", TreeScope.Descendants, firstNumber.ToString());
            library.SetValueOnTextBox(mainWindow, AutomationElement.AutomationIdProperty, "txtSecondNumber", TreeScope.Descendants, secondNumber.ToString());
            double addResult = PressOpButtonandGetValue("btnAdd");
            double subResult = PressOpButtonandGetValue("btnSubtract");
            double mulResult = PressOpButtonandGetValue("btnMultiply");
            double divResult = PressOpButtonandGetValue("btnDivide");

            if (addResult != (firstNumber + secondNumber))
                allTestPass = false;
            if (subResult != (firstNumber - secondNumber))
                allTestPass = false;
            if (mulResult != (firstNumber * secondNumber))
                allTestPass = false;
            if (Math.Round(divResult,5) != Math.Round((firstNumber / secondNumber),5))
                allTestPass = false;
            return allTestPass;
        }

        public double PressOpButtonandGetValue(string buttonID)
        {
            library.PressButtonOnWindow(mainWindow, AutomationElement.AutomationIdProperty, buttonID, TreeScope.Descendants);
            Thread.Sleep(200);
            string result = library.GetTextFromTextElement(mainWindow, AutomationElement.AutomationIdProperty, "txtResult", TreeScope.Descendants);
            double actualResult;
            if (double.TryParse(result, out actualResult))
                return actualResult;
            throw new ArgumentException("Wrong Value detected");
        }

        private AutomationElement GetDesktop()
        {
            Console.WriteLine("Getting Desktop");
            var desktopElement = library.GetRootElement();
            if (desktopElement == null)
            {
                Console.WriteLine("Unable to get the desktop Element, Exiting the application");
                throw new ElementNotAvailableException("Unable to get the desktop Element");
            }
            return desktopElement;
        }
    }
}

Directly interacting with the Microsoft UI Automation API can lead to extremely repetitive code since most of the functions are used again and again. So I wrote a small library to simply this. Here is the code. It is still largely uncommented and not refactored yet, will improve it in future blog posts.

class AutomationLibrary
    {
        public AutomationElement GetRootElement()
        {
            return AutomationElement.RootElement;
        }

        public AutomationElement GetElement(AutomationElement rootElement, AutomationProperty property, object value)
        {
            return GetElement(rootElement, property, value, TreeScope.Children);
        }

        public AutomationElement GetElement(AutomationElement rootElement, AutomationProperty property, object value, TreeScope searchScope)
        {

            AutomationElement aeMainWindow = null;

            int numWaits = 0;
            do
            {
                aeMainWindow = rootElement.FindFirst(searchScope, new PropertyCondition(property, value));
                ++numWaits;
                Thread.Sleep(200);
            } while (aeMainWindow == null && numWaits < 50);
            return aeMainWindow;
        }

        public AutomationElement GetElementWithoutWait(AutomationElement rootElement, AutomationProperty property, object value)
        {
            return GetElementWithoutWait(rootElement, property, value, TreeScope.Children);
        }

        public AutomationElement GetElementWithoutWait(AutomationElement rootElement, AutomationProperty property, object value, TreeScope searchScope)
        {
            AutomationElement aeMainWindow = rootElement.FindFirst(searchScope, new PropertyCondition(property, value));
            return aeMainWindow;
        }


        public bool PressButtonOnWindow(AutomationElement element, AutomationProperty property, object value)
        {
            return PressButtonOnWindow(element, property, value, TreeScope.Children);
        }

        public bool PressButtonOnWindow(AutomationElement element, AutomationProperty property, object value, TreeScope treeScope)
        {
            try
            {
                //var window = GetElementWithoutWait(element, property, value,treeScope);
                //if (window == null)
                //    window = GetElement(element,property,value,treeScope);
                //if(window==null)
                //    return false;
                //else
                //{
                var buttonInvoke = GetInvokePattern(element, property, value, TreeScope.Descendants);
                buttonInvoke.Invoke();
                return true;
                // }
            }
            catch
            {
                return false;
            }
        }

        public InvokePattern GetInvokePattern(AutomationElement window, AutomationProperty property, object value, TreeScope treeScope)
        {
            AutomationElement aeButtonElement;
            int numWaits = 0;
            do
            {
                aeButtonElement = window.FindFirst(treeScope, this.GetPropertyCondition(property, value));
                ++numWaits;
                Thread.Sleep(500);
            } while (aeButtonElement == null && numWaits < 75);
            object objPattern;
            InvokePattern invokePatternObj;
            if (aeButtonElement.TryGetCurrentPattern(InvokePattern.Pattern, out objPattern))
            {
                invokePatternObj = objPattern as InvokePattern;
                return invokePatternObj;
            }
            return null;
        }

        public ValuePattern GetValuePatternWithoutWait(AutomationElement window, AutomationProperty property, object value, TreeScope searchScope)
        {
            AutomationElement aeTextBoxElement = window.FindFirst(searchScope, GetPropertyCondition(property, value));
            if (aeTextBoxElement == null)
                throw new ElementNotAvailableException("TextBoxElement Element not Available");
            object objPattern;
            ValuePattern valuePatternObj;
            if (aeTextBoxElement.TryGetCurrentPattern(ValuePattern.Pattern, out objPattern))
            {
                valuePatternObj = objPattern as ValuePattern;
                return valuePatternObj;
            }
            else
                throw new ElementNotEnabledException("The Value Pattern was not retrieved from the element");
        }

        public InvokePattern GetInvokePatternWithoutWait(AutomationElement window, AutomationProperty property, object value, TreeScope searchScope)
        {
            AutomationElement aeButtonElement = window.FindFirst(searchScope, GetPropertyCondition(property, value));
            if (aeButtonElement == null)
                throw new ElementNotAvailableException("Button Element not Available. Try the GetInvokePattern method which has a wait");
            object objPattern;
            InvokePattern invokePatternObj;
            if (aeButtonElement.TryGetCurrentPattern(InvokePattern.Pattern, out objPattern))
            {
                invokePatternObj = objPattern as InvokePattern;
                return invokePatternObj;
            }
            else
                throw new ElementNotEnabledException("The Invoke Pattern was not retrieved from the element");
        }


        public ExpandCollapsePattern GetExpandCollapsePattern(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            AutomationElement aeExpanderElement;
            int numWaits = 0;
            do
            {
                aeExpanderElement = GetFirstChildNode(element, property, value, searchScope);
                ++numWaits;
                Thread.Sleep(300);
            } while (aeExpanderElement == null && numWaits < 75);
            object objPattern;
            ExpandCollapsePattern togPattern;
            if (true == aeExpanderElement.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out objPattern))
            {
                togPattern = objPattern as ExpandCollapsePattern;
                return togPattern;
            }
            else
                return null;

        }

        public void SetValueOnTextBox(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope, string valueToBeSet)
        {
            var valuePattern = GetValuePatternWithoutWait(element, property, value, searchScope);
            if (valuePattern != null)
                valuePattern.SetValue(valueToBeSet);
        }

        public ExpandCollapsePattern GetExpandCollapsePatternWithoutWait(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            try
            {
                AutomationElement aeExpanderElement = GetFirstChildNode(element, property, value, searchScope);
                if (aeExpanderElement == null)
                    throw new ElementNotAvailableException("Expander Element not available. Try the GetExpandCollapsePattern which has a wait");
                object objPattern;
                ExpandCollapsePattern togPattern;
                if (true == element.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out objPattern))
                {
                    togPattern = objPattern as ExpandCollapsePattern;
                    return togPattern;
                }
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }

        public SelectionItemPattern GetSelectionItemPattern(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            AutomationElement aeSelectionPattern;
            int numWaits = 0;
            do
            {
                aeSelectionPattern = GetFirstChildNode(element, property, value, searchScope);
                ++numWaits;
                Thread.Sleep(300);
            } while (aeSelectionPattern == null && numWaits < 75);
            object objPattern;
            SelectionItemPattern selectionItemPattern;
            if (true == element.TryGetCurrentPattern(SelectionItemPattern.Pattern, out objPattern))
            {
                selectionItemPattern = objPattern as SelectionItemPattern;
                return selectionItemPattern;
            }
            else
                return null;
        }

        public SelectionItemPattern GetSelectionItemWithoutWait(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            try
            {
                AutomationElement aeExpanderElement = GetFirstChildNode(element, property, value, searchScope);
                if (aeExpanderElement == null)
                    throw new ElementNotAvailableException("Expander Element not available. Try the GetSelectionItemPattern which has a wait");
                object objPattern;
                SelectionItemPattern togPattern;
                if (true == element.TryGetCurrentPattern(SelectionItemPattern.Pattern, out objPattern))
                {
                    togPattern = objPattern as SelectionItemPattern;
                    return togPattern;
                }
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }

        public TextPattern GetTextPatternWithoutWait(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            try
            {
                AutomationElement aeTextElement = GetFirstChildNode(element, property, value, searchScope);
                if (aeTextElement == null)
                    throw new ElementNotAvailableException("Text Element not available");
                object objPattern;
                TextPattern txtPattern;
                if (true == aeTextElement.TryGetCurrentPattern(TextPattern.Pattern, out objPattern))
                {
                    txtPattern = objPattern as TextPattern;
                    return txtPattern;
                }
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }

        public string GetTextFromTextElement(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            var textPattern = GetTextPatternWithoutWait(element, property, value, searchScope);
            if (textPattern != null)
                return textPattern.DocumentRange.GetText(-1);
            return null;

        }



        public PropertyCondition GetPropertyCondition(AutomationProperty property, object value)
        {
            return new PropertyCondition(property, value);
        }

        public AutomationElementCollection GetAllChildNodes(AutomationElement element, AutomationProperty automationProperty, object value, TreeScope treeScope)
        {
            var allChildNodes = element.FindAll(treeScope, GetPropertyCondition(automationProperty, value));
            if (allChildNodes == null)
                throw new ElementNotAvailableException("Not able to find the child nodes of the element");
            return allChildNodes;
        }

        public AutomationElement GetFirstChildNode(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            var firstchildNode = element.FindFirst(searchScope, GetPropertyCondition(property, value));
            if (firstchildNode == null)
                throw new ElementNotAvailableException("Not able to find the first child node of the element");
            return firstchildNode;
        }

        public bool FirstChildTextNodeContains(AutomationElement element, string toCheck)
        {
            var firstTextNode = this.GetFirstChildNode(element, AutomationElement.ControlTypeProperty, ControlType.Text, TreeScope.Children);
            if (firstTextNode.Current.Name == toCheck)
                return true;
            else
                return false;
        }

        public void SelectComboBoxItem(AutomationElement element, int indexToBeSelected, AutomationProperty property, object value, TreeScope searchScope)
        {
            var allChildren = this.ExpandComboBoxViewAndReturnChildren(element, property, value, searchScope);
            if (allChildren.Count < indexToBeSelected)
                throw new Exception("The combobox has fewer items than those that need to be selected");
            var itemToBeSelected = allChildren[indexToBeSelected];
            var selectPattern = this.GetSelectionItemPattern(itemToBeSelected, AutomationElement.ControlTypeProperty, ControlType.ListItem, TreeScope.Element);
            if (selectPattern != null)
                selectPattern.Select();
            var togglePattern = this.GetExpandCollapsePattern(element, property, value, TreeScope.Descendants);
            if (togglePattern != null)
                togglePattern.Collapse();
        }

        public AutomationElementCollection ExpandComboBoxViewAndReturnChildren(AutomationElement element, AutomationProperty property, object value, TreeScope searchScope)
        {
            try
            {
                var comboBox = GetFirstChildNode(element, AutomationElement.AutomationIdProperty, value, searchScope);
                var expandPattern = GetExpandCollapsePattern(comboBox, AutomationElement.ControlTypeProperty, ControlType.ComboBox, TreeScope.Element);
                if (expandPattern == null)
                    throw new ElementNotAvailableException("Couldnt Find Expand Pattern in combobox");
                expandPattern.Expand();
                return this.GetAllChildNodes(comboBox, AutomationElement.ControlTypeProperty, ControlType.ListItem, TreeScope.Children);
            }
            catch (Exception e1)
            {
                throw e1;
            }
        }

        public void WorkaroundPopulateControlTree(int x1, int y1)
        {
            uint x = (uint)x1;
            uint y = (uint)y1;
            NativeMethods.SetCursorPos(x1, y1);
            Thread.Sleep(200);
            NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF_LEFTDOWN | NativeMethods.MOUSEEVENTF_LEFTUP, x, y, 0, 0);
        }


        public bool WaitTillElementSelected(AutomationElement aeMainWindow, AutomationProperty automationProperty, object value, TreeScope treeScope)
        {
            var btnStartAnalysis = GetElement(aeMainWindow, automationProperty, value, treeScope);
            int numWait = 0;
            do
            {
                if (btnStartAnalysis.Current.IsEnabled == true)
                    return true;
                Thread.Sleep(2000);
            } while (btnStartAnalysis.Current.IsEnabled == false && numWait < 90);

            return false;
        }
    }

    class NativeMethods
    {

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);

        public const int MOUSEEVENTF_LEFTDOWN = 0x02;
        public const int MOUSEEVENTF_LEFTUP = 0x04;
        public const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        public const int MOUSEEVENTF_RIGHTUP = 0x10;

        /// Return Type: BOOL->int  
        ///X: int  
        ///Y: int  
        [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
        [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
        public static extern bool SetCursorPos(int X, int Y);

    }
}

Running the application launches the console application which enters random inputs and tests the result for all mathematical operations.

The code can be downloaded here.

Nov 302009
 

I didn’t know this was possible until a few days back when a user asked about it on one of the MSDN forums. The query was on how to integrate Winforms and WPF i.e. how to display WPF user controls in Winform application. This feature can be extremely useful because some elements and effects which are easy in WPF can make you sweat a lot in Winforms.

Also this might be useful when you are eventually looking to migrate your application GUI to WPF. So when you are able to create resuable user controls and reuse them in Winforms, it will allow you to port all your winform user controls to WPF one at a time.

The control in Winforms which can do this is the ElementHost which resides in the WindowsFormsIntegration assembly. The ElementHost is responsible for loading the WPF control and painting it. Lets see how to do it. First we will create a user control in WPF. The markup is

<UserControl x:Class="WpfApplication1.SampleWPF"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <StackPanel Margin="12,91,12,30">

            <TextBlock HorizontalAlignment="Center" Text="Sample WPF Control"
                       FontSize="20"
                       FontFamily="Verdana"
                       />

            <Button Name="btnClick" Click="btnClick_Click" Content="Click ME" HorizontalAlignment="Stretch" />
        </StackPanel>
    </Grid>
</UserControl>

Nothing really, just a StackPanel inside the grid with a TextBlock and a Button. Just to see a WPF Event we hookup an event handler to the Button.

public partial class SampleWPF : UserControl
{
    public SampleWPF()
    {
        InitializeComponent();
    }

    private void btnClick_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("WPF says Hello");
    }
}

Now that the WPF part is done, all we need to do is create a Winform Host for this user control. Create a new Winform project and add a reference to the following dlls:-

  • PresentationCore.dll
  • PresentationFramework.dll
  • WindowsBase.dll
  • WindowsFormsIntegration.dll
  • Your WPF DLL which you just created.

Now just to show interoperability between WPF and Winforms, drag a Winforms label and button and hook an event handler to it. In the Form Load handler we create an ElementHost. Since we added the WPF control’s assembly reference, we can create an object of the WPF user control. Of course we cant add the control to the Form’s control collection since the Form object has no idea how to draw the WPF control. This is where the ElementHost control comes in. The ElementHost takes in the usercontrol as its child control and is added to the Winfrom. The code is:-

private void Form1_Load(object sender, EventArgs e)
{
    ElementHost _host = new ElementHost();
    _host.Dock = DockStyle.Fill;
    WpfApplication1.SampleWPF _wpfUserControl = new WpfApplication1.SampleWPF();
    _host.Child = _wpfUserControl;
    this.Controls.Add(_host);
}

private void btnWinClick_Click(object sender, EventArgs e)
{
    MessageBox.Show("Windows Says Hello");
}

And this works great. Both the events for the WPF user control and the Winform controls work here. Some screenshots below:-


WinForm

wpfclick

winclick

The magic here is done by ElementHost and another class AvalonAdapter. Avalon as you would recall was WPF’s code name in its early stages of development. The Avalon Adapter is responsible for drawing the WPF application to the Windows form. The hWndSource object is responsible for creating the hWnd for the window. Using this you can actually make WPF interop with Win32 applications as well.

This method is not without its limitations though, you cant pretty much do anything which is possible with WPF, for e.g transparency

Nov 202009
 

I don’t have the Visual Studio 2008 IDE at work. To get around that I write WPF entirely though code in Notepad and compile it with the command line compiler installed with the Framework. Painful, yeah, but it makes for much better learning. The IDE makes things much easier through intellisense, code generation which makes for Rapid development, but makes developers lazier. So start coding with Notepad and learn faster!!!

But the biggest headache in using the command line compiler is adding a reference to the three WPF dlls which reside in the Program Files Folder. So I wrote a small batch file to make the referencing the creating the exe easier for me. Create two folders Codebase and Executables in the same directory that your .NET 3.5 folder resides.

Just save the following text as a batch file and save it in the same directory as your .NET installation. Save the code in the codebase folder and run it from the .NET 3.5 directory. For e.g. if your code file name is SampleCode.cs and batchfile name is runcsharp, then execute the command runcsharp SampleCode form .NET 3.5 directory. The exe will be created as SampleCode.exe in the Executables directory. Simple, yet makes things a lot easier for developers who don’t have the IDE installed.

csc.exe /out:..\execut~1\%1.exe ..\codebase\%1.cs /reference:”C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll” /reference:”C:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.0\PresentationFrameWork.dll” /reference:”C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll”

Here you can see the three dlls that are must for a WPF application.

  • PresentationCore.dll
  • PresentationFramework.dll
  • WindowsBase.dll
Nov 192009
 

Unlike conventional windows Forms applications, WPF supports Nested controls. i.e. one control can reside inside other controls, something that is not possible in Windows Forms where all the controls belong to one container. Lets see how this is Possible. Many controls in WPF has a content property. The Content property is what the control displays. Now the content property is of object type, so it can pretty much accomodate anything. You can set it to a String variable, or a DateTime.

Though it is of object type, a very important distinction is made when you set the Content property of a control. If the object derives from the WPF class FrameworkElement which in turn derives from UIElement, then it is displayed in the Control, else the ToString method is called and the string representation of the object is displayed. The UIElement is the key class for graphical controls in WPF and contains the OnRender method used to get a graphical representation.

So lets see how to create nested controls. Its quite simple really especially in XAML.

<Window x:Class="WpfNestedControls.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">
    <Grid>
        <StackPanel>
            <Button Name="myButton">
                <TextBlock Name="NestedtxtBlock"
                           Text="Click Me"
                           TextAlignment="Center" >
                    <Button Name="NestedButton"
                            Content='I am nested' />
                </TextBlock>
            </Button>
        </StackPanel>
    </Grid>
</Window>

If you see the XAML code the nested text block is between the myButton tags and there is one more button inside the TextBlock. Lets see how the code works for this. I will remove the other code for the window creation so we can focus on the important part.

            Button Button1 = new Button();
            Button1.Name = "myButton";
            StackPanel1.Children.Add(Button1);
 
            TextBlock TextBlock1 = new TextBlock();
            TextBlock1.Name = "NestedtxtBlock";
            TextBlock1.Text = "Click Me";
            TextBlock1.TextAlignment = TextAlignment.Center;
            Button1.Content = TextBlock1;
 
            Button Button2 = new Button();
            Button2.Name = "NestedButton";
            Button2.Content = "I am nested";
            TextBlock1.Inlines.Add(Button2);

As you can see the nested TextBlock1 is added as the Content for the button. But the second nested button is not added to the Content property of the TextBlock because it simply doesn’t have one. What it does have is an InlineCollection – a collection of the Inline properties. Now Inline doesn’t derive from FrameworkElement or UIElement, so there is no OnRender method. This OnRender method is provided by the TextBlock. So the Inline property depends on the TextBlock Control to render itself on the screen.

Now why doesn’t TextBlock have a content property – because it doesn’t derive from the Control class, instead it derives from FrameworkElement. I understand that this class hierarchy is confusing, but its one of the keys to understanding how WPF works. More about that  in later blogs.

OurApp

Here is a small sample on how the main button click event can be used to intercept events from child controls. Such events are called RoutedEvents and they can happen in two ways – From the originating control to the root or vice versa. The originalsource property of the EventArgs contains the actual control that raised the event and the Handled property makes it stop propagating to the parent control.

       private void myButton_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("This is a click event and originated in " + ((Button)e.OriginalSource).Name );
            e.Handled = true;
        }