Showing posts with label XAML. Show all posts
Showing posts with label XAML. Show all posts

Creating your own markup extensions in WPF XAML

If you do not know what markup extensions are in WPF and XAML then you have to first go through this MSDN link to understand what they are, here in this post i will be explaining how to create and implement your own Markup extensions.

To define a markup extension you have to first derive your class from the System.Windows.Markup.MarkupExtension abstract base class. This abstract class has a abstract method defined inside it abstract object ProvideValue(IServiceProvider serviceProvider) implement this this abstract class in your derived class as shown below.


C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension 
{
     public override object ProvideValue(IServiceProvider serviceProvider)
    {
       throw new NotImplementedException();
    }
}


Now if you see that we get a method with the name  public override object ProvideValue(IServiceProvider serviceProvider) , Now this is the method that is always called when the markup extensions are called for in XAML. This method should always return a VALUE which is depending on the context.

Now let me tell you the context of this markup extension, here we will return a string VALUE based on a KEY passed , the resolution will happen using a method within this class.

Lets add a dictionary to this class that will hold the KEY VALUE pairs.

C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<stringstring> _lookTable = new Dictionary<stringstring>();


    //This method will be called when the extension is called.
          public override object ProvideValue(IServiceProvider serviceProvider)
    {
       throw new NotImplementedException();
    }
}


This dictionary will hold the KEY VALUE pair, and the the key will be matched with the KEY that is passed to this markup extension and the  VALUE if exists will be returned or a default VALUE will be returned.

Lets add two constructors to this class , one will be a default constructor and the other will be take a string parameter which is the KEY , Also lets add a static constructor that will add entries to the dictionary.

C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<stringstring> _lookTable = new Dictionary<stringstring>();

    //Static constructor initializes the dictionary
    static CultureKeyResolver()
    {
       _lookTable.Add("en-us""Click Me");
       _lookTable.Add("Albanian""Kliko Mua");
       _lookTable.Add("Dutch""Klik op mij");
    }

    //Local field and property
    private string cultureKey = string.Empty;
    public string CultureKey
    {
       get { return cultureKey; }
       set { cultureKey = value; }
    }
    
    //Default constructor
    public CultureKeyResolver() 
    {
    }

    //This is a constructor with a parameter as the input key
    public CultureKeyResolver(string key)
    {
       cultureKey = key;
    }

          //This method will be called when the extension is called.
         public override object ProvideValue(IServiceProvider serviceProvider)
    {
       return this.GetValue(cultureKey);
    }
}



Now we will add a method to this class that will resolve this KEY for a VALUE, if that KEYexists in the dictionary the corresponding VALUE will be returned else a default VALUE will be returned
the method will look something like this.

C#

public string GetValue(string culture)
{
    //Make a check for the key
    if (_lookTable.ContainsKey(culture))
    {
       //If exists then return the value
       return _lookTable[culture];
    }
    //Return default value
    return _lookTable["en-us"];
}



Go Ahead and add this method into the class, Your class will look something like this now

C#


public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<string, string> _lookTable = new Dictionary<string, string>();

    //Static constructor initializes the dictionary
    static CultureKeyResolver()
    {
       _lookTable.Add("en-us", "Click Me");
       _lookTable.Add("Albanian", "Kliko Mua");
       _lookTable.Add("Dutch", "Klik op mij");
    }

    //Local field and property
    private string cultureKey = string.Empty;
    public string CultureKey
    {
       get { return cultureKey; }
       set { cultureKey = value; }
    }
    
    //Default constructor
    public CultureKeyResolver() 
    {
    }

    //This is a constructor with a parameter as the input key
    public CultureKeyResolver(string key)
    {
       cultureKey = key;
    }

    public string GetValue(string culture)
    {
       //Make a check for the key
       if (_lookTable.ContainsKey(culture))
       {
          //If exists then return the value
          return _lookTable[culture];
       }
       //Return default value
       return _lookTable["en-us"];
    }

          //This method will be called when the extension is called.
          public override object ProvideValue(IServiceProvider serviceProvider)
    {
       return this.GetValue(cultureKey);
    }
}


Your Markup extension class is ready now, lets call this in our XAML code.

XAML


<Window x:Class="WpfApplication1.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MyApp="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    
    <StackPanel>

        <Button Content="{MyApp:CultureKeyResolver Dutch}" Height="30" Width="100"></Button>
       
    </StackPanel>
</Window


Now in the above XAML code we see that we have made a reference to our Application namespace and given the alias as MyApp this is also used to call our markup extension as
Content="{MyApp:CultureKeyResolver Dutch}" Here if we look the KEY is passed as a value to the constructor , anything that follows the class name separated by space will be passed as a input to the constructor. For example if the constructor takes two or more parameters then your syntax should look something like this Content="{MyApp:CultureKeyResolver Dutch en-us}" here Dutch and en-us will be the two parameter values any value separated by space will be considered.

If you are wondering how it works? Internally this is how it is going to call your markup extension. 

if(CultureKeyResolver is MarkupExtension)
{
   MarkupExtension _extension=new CultureKeyResolver();
   object value= _extension.ProvideValue(argument);
   currentControl.Property=value
}   

The KEY can also be passed as a Property value to the Markup Extension. You can pass it the following way

XAML

<Window x:Class="WpfApplication1.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MyApp="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">   

<StackPanel>

<Button Content="{MyApp:CultureKeyResolver Dutch,  CultureKey=en-us}" Height="30" Width="100"></Button>

</StackPanel>

</Window


With that , you can see that now we have a property name coming up in yor XAML IDE , this is assigned a value.

This is how you implement your own markup extension in WPF XAML.


Creating a WPF User Interface from scratch using XAML

To create a WPF User Interface using XAML , first we will have to understand the what the XAML structure is, I am assuming you have an understanding of XML markup. XML is an internet technology and Microsoft has done a hard work here to bring this internet technology into the desktop user interface development pattern.

First we will understand the limitations of XML.

  • In XML , Explicit references are not allowed.
  • XML is content driven the XML markup cannot be declared and used else where as in our traditional coding technique.
  • XML always represents a state and not a behavior.
  • XML doesn't support inheritance.
Now if we look at the above points we get to know that with XML is content driven , but this leaves us with a very important question, How does the WPF framework validate this XML? 

Lets consider the below problems with using XML and their solutions. 
In my previous article about developing a WPF project from scratch , I had developed a WPF UI and attached it to the application without making any use of XAML. this seemed straight forward as it involved declaring and initializing the UI controls and Window using C# code and setting it as the content. This was straight forward for the WPF framework and the compiler as well since all it had to do was make the use of reflection and validate the structure of the controls and its properties. But now the question is How does the WPF framework achieve this when the UI controls are declared in the XML? 

Microsoft has introduced a special kind of parser for the XML now refereed as XAML where the parser makes use of the reflection technology and validate it to be of correct structure. But how does it refer to the correct DLLs to validate? since we know that the namespace reference we add in an XML document is of a special convention in the form of a URI, URL or a URN

The answer to this is that this is achieved with a special kind of a namespace resolution mechanism using the 
class XmlnsDefinitionAttribute This class makes the mapping between the .NET libraries (DLL) and the XML compliant namespaces.  

OK, now coming back. let me show you a simple example of a XAML window declaration 


XAML

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Window.Content>
    <StackPanel >
      <StackPanel.Children>
        <Button Content="Search" Height="30" Width="100"></Button>
        <TextBox Height="30" Width="100"></TextBox>
      </StackPanel.Children>
    </StackPanel>
  </Window.Content>
</Window>


Here in the above code we can see that we have a window tag , this represents a window and within which we have the name space defined as xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
Note that the namespace definition has begun with xmlns this tells us that a namespace is defined and its the default for this current window. the reason being that it is not aliased , if you want to provide an alias to this namespace then the xmlns has to be followed by a colon ":" and the alias name hence it will look something like this 
xmlns:myalias ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

We also know that in XML we can also provide attributes within each elements , if we look at the above XML structure we see that after the <Window> element we have its child element as <Window.Content>
you may be wondering what this is since we have not come across such naming conventions in XML. In case of XAML we can define the structure of the properties for the elements in XML by Attributes or as child Elements. 
But when do we know which one to choose? The answer to this is,  if the value for the property is complex then we have to go with a child element and if the value for the property is simple then we can define it as an Attribute. For example since the content property for the window is not simple and holds more than just a value its a complex and has been defined as a child element to the Window element. But this gives us another problem, how will the parser know which complex property value belongs to which element ? to resolve this the element name whose property has to be set is appended with its property name to form the child element name for example. the content property which is a complex property for the window has to be set the element now becomes <Window.Content> </Window.Content> this will tell the compiler that the Property named Content will belong to the Element Window.

We can also see that in  the <Button Content="Search" Height="30" Width="100"></Button> element the Content property is set as an attribute as we can see that only the text value "Search" is provided and hence this becomes a simple value for the attribute , it is the same for Height and Width as well.


With the above information lets create a sample project where the UI is developed using only xaml.

Lets begin.

Step 1 :  First create an empty project.
Step 2 :  Add the following DLL reference in your project.
              Presentation Framework
              Presentation Core
              Windows Base
              System.xaml

             This is better shown below. Click on it to enlarge.


Step 3 : Add two New classes to your project
            App.cs

Step 4 : Add a new XML file to your project and name it SearchWindow.xaml
             
Now open this XAML file and paste this code in it.

XAML


<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Window.Content>
    <StackPanel >
      <StackPanel.Children>
        <Button Content="Search" Height="30" Width="100"></Button>
        <TextBox Height="30" Width="100"></TextBox>
      </StackPanel.Children>
    </StackPanel>
  </Window.Content>
</Window>


Step 5 : Open your App.cs file and paste the below code in it.


C#

[STAThread]
static void Main()
{
     //Create an instance of a new Application
     System.Windows.Application _wpfApplication = new System.Windows.Application();

     //Set the start up URI as your XAML page 
     
_wpfApplication.StartupUri = new Uri("SearchWindow.xaml",UriKind.RelativeOrAbsolute);   

     //Run this Application by callinf the Run() method
     _wpfApplication.Run();
}


Step 6 : Now right click on the project the click on properties. Here in the Applications tab set the output type of the project to Windows Application  as shown below.


Step 7 : All done. you are good to go now. click on debug and you will see this below window.




This window is your own custom window. Congrats , you just created a WPF project from scratch..



"Connecting ... " screen for C# and WPF

Recently i came across a situation where i had to create a screen which showed a establishing connection , i searched the "interweb" for this but found no good resource that provided me with the code ready to use. hence i decided to create one by my own.


What did i do? 

Well i used a canvas and ellipses within that. This  did the job for me and below are the screen shots





How to do it? 

I have provided inline comments with the code below,

The XAML code is below 


<Window x:Class="EstablishConn.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        WindowState="Normal" WindowStyle="ToolWindow"
        Title="Connecting ..." Height="125" Width="525">
    <Grid>
        <Canvas Name="MyCanvas"> </Canvas>
    </Grid>
</Window>



Your code Behind will be as below

You will have to include the below namespaces


using System.Windows.Shapes;
using System.Windows.Threading;

namespace EstablishConn
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
    }
}

The Main Logic 

/*
Global declaration
*/
int ELLIPSE_COUNT = 25;
int CURRENT_ELLIPSE_ID = 0;
private List<Ellipse> ellipseList;
Brush DEFAULT_ELLIPSE_COLOR = Brushes.LightSteelBlue;
Brush ELLIPSE_COLOR = Brushes.Blue;
Brush LIGHT_ELLIPSE_COLOR = Brushes.SkyBlue;
Brush DARK_ELLIPSE_COLOR = Brushes.DarkBlue;

/*
The dispatch timer object will be a timer that will raise a tick event every specified interval given in timespan
*/
DispatcherTimer timer = new DispatcherTimer { Interval =   TimeSpan.FromSeconds(0.05) };

/*
Constructor
*/
public MainWindow()
{
   InitializeComponent();
   ellipseList = new List<Ellipse>();
   CreateEllipseList();
   /*
   assign an handler for the DispatchTimer
   then start the timer
   */
   this.timer.Tick += new EventHandler(timer_Tick);
   this.timer.Start();
}


/*
this method will create the Ellipse s and allocate it to the 
Canvas
The created ellipses will also be added to a list to be    handled later
*/
private void CreateEllipseList()
{
   double LEFT = 10;
   for (int i = 0; i < ELLIPSE_COUNT; i++)
   {
       Ellipse e = new Ellipse();
       e.Height = 10;
       e.Width = 10;
       e.Fill = DEFAULT_ELLIPSE_COLOR;
       Canvas.SetLeft(e, LEFT);
       Canvas.SetTop(e, 50);
       MyCanvas.Children.Add(e);
       ellipseList.Add(e);
       LEFT = LEFT + 20;
   }
}

/*
This is the event handler for the timer 
Here the actual animation takes place
*/
private void timer_Tick(object sender, EventArgs e)
{
   ellipseList[CURRENT_ELLIPSE_ID].Fill = LIGHT_ELLIPSE_COLOR;
   ellipseList[CURRENT_ELLIPSE_ID + 1].Fill = ELLIPSE_COLOR;
   if (!(CURRENT_ELLIPSE_ID >= ELLIPSE_COUNT - 2))
      ellipseList[CURRENT_ELLIPSE_ID + 2].Fill = DARK_ELLIPSE_COLOR;

   if (CURRENT_ELLIPSE_ID > 0)
      ellipseList[CURRENT_ELLIPSE_ID - 1].Fill = DEFAULT_ELLIPSE_COLOR;

   CURRENT_ELLIPSE_ID = CURRENT_ELLIPSE_ID + 1;
   if (CURRENT_ELLIPSE_ID >= ELLIPSE_COUNT - 2)
   {
      ellipseList[CURRENT_ELLIPSE_ID - 1].Fill    = DEFAULT_ELLIPSE_COLOR;
      ellipseList[CURRENT_ELLIPSE_ID].Fill        = DEFAULT_ELLIPSE_COLOR;
      ellipseList[CURRENT_ELLIPSE_ID + 1].Fill    = DEFAULT_ELLIPSE_COLOR;

      CURRENT_ELLIPSE_ID = 0;
   }
}

WPF: TreeView in WPF/XAML

How to use the TreeView Element and loading it through code.

The XAML is as below. We can have any number of nodes in a tree. there is no limit for that Each new TreeViewItem that is added is made into blue color for easy understanding.

<Page 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

<TreeView>

 <TreeViewItem>
<TreeViewItem.Header>
  <TextBlock Text ="Hello"></TextBlock>
</TreeViewItem.Header>
     <TreeViewItem Header="First element"/> 
     <TreeViewItem Header="Second element"/>
     <TreeViewItem Header="Third element"/> 
     <TreeViewItem Header="Fourth element">
<TreeViewItem>
            <TreeViewItem.Header>
<TextBlock Text ="Inside Fourth"></TextBlock>
            </TreeViewItem.Header>
            <TreeViewItem Header="Is there an end to this?"/>
</TreeViewItem>
  </TreeViewItem>
  <TreeViewItem Header="World"> 
<TreeViewItem Header="Again a first element"/> 
  </TreeViewItem>

</TreeView>

</Page>

The Output will be as follows:
Both the nodes are not expanded








First Node(Hello ) is expanded










The Node inside the node is expanded. (Fourth element)












The Node inside the node inside Node is expanded. (Inside Fourth Node)














The Tree is expanded entirely