Category Archives: Controls

Introduction to Data Templates (DataTemplate with ListBox) –Part3

Download the source code from here.

We have seen the Introduction to Templates–DataTemplate in the post here.

In Part2, we have seen the basics of binding data to a ListBox using Inline DataTemplates. It is more common to define it in the resources section so it can be a reusable object, as in the following example:

Creating the DataTemplate as a Resource
<StackPanel.Resources>
    <MyXAMLLocal:Errors x:Key="errorsobj"></MyXAMLLocal:Errors>
    <DataTemplate x:Key="myErrorDataTemplate">
        <StackPanel>
            <TextBlock Text="{Binding Path=PropErrorNumber}" ></TextBlock>
            <TextBlock Text="{Binding Path=PropErrorDescription}"></TextBlock>
            <TextBlock Text="{Binding Path=PropErrorPriority}"></TextBlock>
        </StackPanel>
    </DataTemplate>
</StackPanel.Resources>

 

Now you can use myErrorDataTemplate as a resource, as in the following example:

<ListBox Width="272" Margin="5" HorizontalAlignment="Stretch"
         ItemsSource="{Binding Source={StaticResource errorsobj}}" Height="232"
         ItemTemplate="{StaticResource myErrorDataTemplate}">
</ListBox>

 

Because myErrorDataTemplate is a resource, you can use it with other controls that have a property which takes a DataTemplate type. As shown above, for ItemsControl objects, such as the ListBox, it is the ItemTemplate property. For ContentControl objects, it is the ContentTemplate property.

Let’s improve on the presentation by adding a Border, a Grid, and some TextBlock elements that describe the data that is being displayed.

image

As you can see from the above application, we have displayed the errors in a presentable format and given color identity for the application types.

If the application is a client application type, then the border is in yellow color

If the application is a web application type, then the border is in red color

else by default, for mobile application type, it is blue.

First let us walk through the code that contains the error class information

public class CustomError : INotifyPropertyChanged
    {
        public CustomError()
        {
        }

        public CustomError(string errornumber, string errordescription, int errorpriority, enumapplicationtype apptype)
        {
            this.ErrorType = errornumber;
            this.ErrorDescription = errordescription;
            this.ErrorPriority = errorpriority;
            this.ErrorApplicationType = apptype;
        }

        private string ErrorType;

        public string PropErrorType
        {
            get { return ErrorType; }
            set { ErrorType = value;
            OnPropertyChanged("PropErrorNumber");
            }
        }
        private string ErrorDescription;

        public string PropErrorDescription
        {
            get { return ErrorDescription; }
            set { ErrorDescription = value;
            OnPropertyChanged("PropErrorDescription");
            }
        }
        private int ErrorPriority;

        public int PropErrorPriority
        {
            get { return ErrorPriority; }
            set { ErrorPriority = value;
            OnPropertyChanged("PropErrorPriority");
            }
        }
        private enumapplicationtype ErrorApplicationType;

        public enumapplicationtype PropApplicationType
        {
            get { return ErrorApplicationType; }
            set { ErrorApplicationType = value;
            OnPropertyChanged("enumapplicationtype");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string info)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(info));
            }
        }
    }

    public class Errors : ObservableCollection<CustomError>
    {
        public Errors() : base()
        {
            Add(new CustomError("Error", "Error1", 1, enumapplicationtype.Web));
            Add(new CustomError("Error", "Error2", 1, enumapplicationtype.Mobile));
            Add(new CustomError("Warning","Warning1",2,enumapplicationtype.Client));
            Add(new CustomError("Warning", "Warning2", 2, enumapplicationtype.Web));
            Add(new CustomError("Info", "Info1", 3, enumapplicationtype.Mobile));
            Add(new CustomError("Info", "Info2", 3, enumapplicationtype.Client));
        }
    }

    public enum enumapplicationtype
    {
        Web,
        Client,
        Mobile
    }

 

Like in the previous Part 1 and Part 2, we have Errors observable collection class and the application types are client, web and mobile. We created some default CustomError class values and assigned to the Errors observable collection class.

Let us add the DataTemplate to the Resources section of the Window

<DataTemplate x:Key="myErrorDataTemplate" >
            <Border Name="border" BorderBrush="Blue" BorderThickness="1" Padding="5" Margin="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Number:"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=PropErrorType}" />
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=PropErrorDescription}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=PropErrorPriority}"/>
                </Grid>
            </Border>

            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=PropApplicationType}" >
                    <DataTrigger.Value>
                        <MyXAMLLocal:enumapplicationtype>Web</MyXAMLLocal:enumapplicationtype>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=PropApplicationType}" >
                    <DataTrigger.Value>
                        <MyXAMLLocal:enumapplicationtype>Client</MyXAMLLocal:enumapplicationtype>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
                </DataTrigger>
            </DataTemplate.Triggers>
       </DataTemplate>

As you can see in the above code, we have defined a DataTemplate with key myErrorDataTemplate and inside the DataTemplate, we are using the customerror class properties and displaying them as per the required format.

we also added some triggers which act on the data at runtime and formats the data displayed as per the data values.

Use the DataTemplate we just created in the XAML code as follows

<StackPanel>
    
    <Label Content="Errors List" FontSize="20"></Label>
    <ListBox ItemsSource="{Binding Source={StaticResource errorsobj}}"
             ItemTemplate="{StaticResource myErrorDataTemplate}"
             IsSynchronizedWithCurrentItem="True"
             HorizontalContentAlignment="Stretch" Height="433" Width="375">
        
    </ListBox>
    <TextBlock FontSize="20" Text="Additional Information:"/>
    <ContentControl Content="{Binding Source={StaticResource errorsobj}}"
                ContentTemplate="{StaticResource myErrorDataTemplate}"/>
</StackPanel>

 

In the above code, we have a list box and its binded to the Errors observable collection class. But then it uses a ItemTemplate which points to the DataTemplate “myErrorDataTemplate” that we created earlier in the code. This formats the data that is presented in the UI.

The complete code of the cs file is as follows

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Introduction_to_DataTemplates_2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class CustomError : INotifyPropertyChanged
    {
        public CustomError()
        {
        }

        public CustomError(string errornumber, string errordescription, int errorpriority, enumapplicationtype apptype)
        {
            this.ErrorType = errornumber;
            this.ErrorDescription = errordescription;
            this.ErrorPriority = errorpriority;
            this.ErrorApplicationType = apptype;
        }

        private string ErrorType;

        public string PropErrorType
        {
            get { return ErrorType; }
            set { ErrorType = value;
            OnPropertyChanged("PropErrorNumber");
            }
        }
        private string ErrorDescription;

        public string PropErrorDescription
        {
            get { return ErrorDescription; }
            set { ErrorDescription = value;
            OnPropertyChanged("PropErrorDescription");
            }
        }
        private int ErrorPriority;

        public int PropErrorPriority
        {
            get { return ErrorPriority; }
            set { ErrorPriority = value;
            OnPropertyChanged("PropErrorPriority");
            }
        }
        private enumapplicationtype ErrorApplicationType;

        public enumapplicationtype PropApplicationType
        {
            get { return ErrorApplicationType; }
            set { ErrorApplicationType = value;
            OnPropertyChanged("enumapplicationtype");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string info)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(info));
            }
        }
    }

    public class Errors : ObservableCollection<CustomError>
    {
        public Errors() : base()
        {
            Add(new CustomError("Error", "Error1", 1, enumapplicationtype.Web));
            Add(new CustomError("Error", "Error2", 1, enumapplicationtype.Mobile));
            Add(new CustomError("Warning","Warning1",2,enumapplicationtype.Client));
            Add(new CustomError("Warning", "Warning2", 2, enumapplicationtype.Web));
            Add(new CustomError("Info", "Info1", 3, enumapplicationtype.Mobile));
            Add(new CustomError("Info", "Info2", 3, enumapplicationtype.Client));
        }
    }

    public enum enumapplicationtype
    {
        Web,
        Client,
        Mobile
    }
}

 

The complete code of the XAML file is as follows

<Window x:Class="Introduction_to_DataTemplates_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow"
        xmlns:MyXAMLLocal="clr-namespace:Introduction_to_DataTemplates_2" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot; xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot; d:DesignHeight="611" d:DesignWidth="426" SizeToContent="WidthAndHeight">
    <Window.Resources>
        <MyXAMLLocal:Errors x:Key="errorsobj"></MyXAMLLocal:Errors>
        <DataTemplate x:Key="myErrorDataTemplate" >
            <Border Name="border" BorderBrush="Blue" BorderThickness="1" Padding="5" Margin="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Number:"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=PropErrorType}" />
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=PropErrorDescription}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=PropErrorPriority}"/>
                </Grid>
            </Border>

            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=PropApplicationType}" >
                    <DataTrigger.Value>
                        <MyXAMLLocal:enumapplicationtype>Web</MyXAMLLocal:enumapplicationtype>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=PropApplicationType}" >
                    <DataTrigger.Value>
                        <MyXAMLLocal:enumapplicationtype>Client</MyXAMLLocal:enumapplicationtype>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
                </DataTrigger>
            </DataTemplate.Triggers>
       </DataTemplate>
    </Window.Resources>
    <StackPanel>
        
        <Label Content="Errors List" FontSize="20"></Label>
        <ListBox ItemsSource="{Binding Source={StaticResource errorsobj}}"
                 ItemTemplate="{StaticResource myErrorDataTemplate}"
                 IsSynchronizedWithCurrentItem="True"
                 HorizontalContentAlignment="Stretch" Height="433" Width="375">
            
        </ListBox>
        <TextBlock FontSize="20" Text="Additional Information:"/>
        <ContentControl Content="{Binding Source={StaticResource errorsobj}}"
                    ContentTemplate="{StaticResource myErrorDataTemplate}"/>
    </StackPanel>
</Window>

 

Download the source code from here.
Your imagination is your preview of life’s coming attractions. – Albert Einstein
Advertisements

Introduction to Data Templates (DataTemplate with ListBox) –Part2

Download the source code from here.

We have seen the Introduction to Templates–DataTemplate in the post here.

DataTemplate is about the presentation of data and is one of the many features provided by the WPF styling and templating model.

To demonstrate why DataTemplate is important, let’s walk through a data binding example. In this example, we have a ListBox that is bound to a list of CustomError objects. Each Error object has a ErrorNumber(int), a ErrorDescription (string), a ErrorPriority (int), and a property of type ApplicationType, which is an Enum with values MR(Material Requisition),PO(Purchase Order) and Inventory.

public class CustomError
{
    public CustomError()
    {
    }

    public CustomError(int errornumber, string errordescription, int errorpriority, enumapplicationtype apptype)
    {
        this.ErrorNumber = errornumber;
        this.ErrorDescription = errordescription;
        this.ErrorPriority = errorpriority;
        this.ErrorApplicationType = apptype;
    }

    public enum enumapplicationtype
    {
        MR,
        PO,
        Inventory
    };

    private int ErrorNumber;

    public int PropErrorNumber
    {
        get { return ErrorNumber; }
        set { ErrorNumber = value; }
    }
    private string ErrorDescription;

    public string PropErrorDescription
    {
        get { return ErrorDescription; }
        set { ErrorDescription = value; }
    }
    private int ErrorPriority;

    public int PropErrorPriority
    {
        get { return ErrorPriority; }
        set { ErrorPriority = value; }
    }
    private enumapplicationtype ErrorApplicationType;

    private enumapplicationtype PropApplicationType
    {
        get { return ErrorApplicationType; }
        set { ErrorApplicationType = value; }
    }
}

Let us create a collection of Error class as shown in the following code.

public class Errors : ObservableCollection<CustomError>
{
    public Errors() : base()
    {
        Add(new CustomError(1, "Error1", 1, CustomError.enumapplicationtype.MR));
        Add(new CustomError(2,"Error2",2,CustomError.enumapplicationtype.PO));
        Add(new CustomError(3, "Error3", 3, CustomError.enumapplicationtype.Inventory));
    }
}

Refer to the namespace in which the Errors class is present in XAML as shown below.

<Window x:Class="Introduction_to_DataTemplates_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow" Height="339" Width="333"
        xmlns:MyXAMLLocal="clr-namespace:Introduction_to_DataTemplates_2"
        >

Let us assign a static resource to the Errors observable collection in XAML as shown in the following code.

<StackPanel.Resources>
    <MyXAMLLocal:Errors x:Key="errorsobj"></MyXAMLLocal:Errors>
</StackPanel.Resources>

Let us bind the static resource that we created which is an object representation of the observable collection class Errors in XAML code as shown below.

    <Label Content="Errors List" FontSize="20"></Label>
    <ListBox Width="272" Margin="5" HorizontalAlignment="Stretch" ItemsSource="{Binding Source={StaticResource errorsobj}}" Height="232"></ListBox>
</StackPanel>

If we run the application; without a DataTemplate, our ListBox currently looks like this:

image

What’s happening is that without any specific instructions, the ListBox by default calls ToString when trying to display the objects in the collection. Therefore, if the Errors object overrides the ToString method, then the ListBox displays the string representation of each source object in the underlying collection.

For example, let us override the ToString method in Errors class as shown below.

public override string ToString()
{
    return ErrorNumber.ToString() + " "+ ErrorDescription + " "+ ErrorPriority.ToString() + " "+ ErrorApplicationType.ToString();
}

If we run the application, our ListBox currently looks like this:

image

However, that is limiting and inflexible. Also, if you are binding to XML data, you wouldn’t be able to override ToString. Most controls have some type of content, and that content often comes from data that you are binding to. In this example, the data is the error list . In WPF, you use a DataTemplate to define the visual representation of data. Basically, what you put into a DataTemplate determines what the data looks like in the rendered application. For the error information to appear in the listbox, you create a DataTemplate as shown below: We are giving instructions that each item appears as four TextBlock elements within a StackPanel. Each TextBlock element is bound to a property of the Error class. The underlying data is a collection of CLR objects.

<ListBox Width="272" Margin="5" HorizontalAlignment="Stretch"
         ItemsSource="{Binding Source={StaticResource errorsobj}}" Height="232">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=PropErrorNumber}"></TextBlock>
                <TextBlock Text="{Binding Path=PropErrorDescription}"></TextBlock>
                <TextBlock Text="{Binding Path=PropErrorPriority}"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

In the above code, we are setting the ItemTemplate property of the ListBox to a DataTemplate using inline mechanism.

It is more common to define it in the resources section so it can be a reusable object, which I will discuss in the next post.

The complete cs file code is as follows

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace Introduction_to_DataTemplates_2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class CustomError
    {
        public CustomError()
        {
        }

        public CustomError(int errornumber, string errordescription, int errorpriority, enumapplicationtype apptype)
        {
            this.ErrorNumber = errornumber;
            this.ErrorDescription = errordescription;
            this.ErrorPriority = errorpriority;
            this.ErrorApplicationType = apptype;
        }

        private int ErrorNumber;

        public int PropErrorNumber
        {
            get { return ErrorNumber; }
            set { ErrorNumber = value; }
        }
        private string ErrorDescription;

        public string PropErrorDescription
        {
            get { return ErrorDescription; }
            set { ErrorDescription = value; }
        }
        private int ErrorPriority;

        public int PropErrorPriority
        {
            get { return ErrorPriority; }
            set { ErrorPriority = value; }
        }
        private enumapplicationtype ErrorApplicationType;

        private enumapplicationtype PropApplicationType
        {
            get { return ErrorApplicationType; }
            set { ErrorApplicationType = value; }
        }
    }

    public class Errors : ObservableCollection<CustomError>
    {
        public Errors() : base()
        {
            Add(new CustomError(1, "Error1", 1, enumapplicationtype.MR));
            Add(new CustomError(2,"Error2",2,enumapplicationtype.PO));
            Add(new CustomError(3, "Error3", 3, enumapplicationtype.Inventory));
        }
    }

    public enum enumapplicationtype
    {
        MR,
        PO,
        Inventory
    }
}

The complete XAML code is as follows

<Window x:Class="Introduction_to_DataTemplates_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow" Height="339" Width="333"
        xmlns:MyXAMLLocal="clr-namespace:Introduction_to_DataTemplates_2"
        >
    <StackPanel>
        <StackPanel.Resources>
            <MyXAMLLocal:Errors x:Key="errorsobj"></MyXAMLLocal:Errors>
        </StackPanel.Resources>
        <Label Content="Errors List" FontSize="20"></Label>
        <ListBox Width="272" Margin="5" HorizontalAlignment="Stretch"
                 ItemsSource="{Binding Source={StaticResource errorsobj}}" Height="232">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Path=PropErrorNumber}"></TextBlock>
                        <TextBlock Text="{Binding Path=PropErrorDescription}"></TextBlock>
                        <TextBlock Text="{Binding Path=PropErrorPriority}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Window>

Download the source code from here.

 

Every great dream begins with a dreamer. Always remember, you have within you the strength, the patience, and the passion to reach for the stars to change the world. – Harriet Tubman

Introduction to Templates–DataTemplate

Download the source code from here.

Most controls have some type of content, and that content often comes from data that you are binding to. In this sample, the data is the name and id of the student. In WPF, you use a DataTemplate to define the visual representation of data. Basically, what you put into a DataTemplate determines what the data looks like in the rendered application. For student name and id to be displayed  and highlighted, you create a DataTemplate as a resource:

            <DataTemplate DataType="{x:Type localXAMLClass:student}" >
                <Border BorderBrush="AliceBlue" BorderThickness="2">
                    <Grid Margin="10">
                        <TextBlock>
                            <Run Text="The Name of the student is "> </Run>
                            <TextBlock Background="LightBlue" Text="{Binding name}"></TextBlock>
                            <Run Text=" And his ID is "></Run>
                            <TextBlock Background="LightBlue" Text="{Binding id}" ></TextBlock>
                        </TextBlock>
                    </Grid>
                </Border>
            </DataTemplate>

Notice that the DataType property is very similar to the TargetType property of the Style. If your DataTemplate is in the resources section, when you specify the DataType property to a type and not assign it an x:Key, the DataTemplate is applied whenever that type appears. You always have the option to assign the DataTemplate with an x:Key and then set it as a StaticResource for properties that take DataTemplate types, such as the ItemTemplate property or the ContentTemplate property.

Before we dive into the data template used in the WPF application let’s see how a simple example works. First take a look at a simple class which represents a student:

namespace IntroductiontoTemplates1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class student
    {
        private string Name;
        private int Id;

        public string name
        {
            get{return Name;}
            set{Name = value;}
        }

        public int id
        {
            get { return Id;}
            set { Id = value; }
        }
    }
}

 

The student class contains two properties name and id. If we were to display an instance of this class in a ContentControl, the XAML would be something like this:

  1. <Window x:Class="IntroductiontoTemplates1.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4.         Title="MainWindow" Height="350" Width="525"
  5.         xmlns:localXAMLClass="clr-namespace:IntroductiontoTemplates1" >
  6.     <Grid>
  7.         <Grid.Resources>
  8.             <localXAMLClass:student x:Key="studentXAMLObject" name="kishore1021" id="1"></localXAMLClass:student>
  9.         </Grid.Resources>
  10.         <ContentControl
  11.             Content="{StaticResource studentXAMLObject}" >
  12.         </ContentControl>
  13.     </Grid>
  14. </Window>

 

The UI created by that XAML looks like the following:

image

That certainly isn’t too impressive. What you see above is what gets displayed as a result of calling ToString() on the student object referenced by the ContentControl. Let’s take a moment to examine what is going on here.

The example above creates an instance of the student class, which represents the student kishore1021. It also creates a ContentControl which is told to display that student object somehow. The ContentControl examines the student object and tries to figure out how to render it, but since student does not derive from UIElement it has no way of knowing how to do so. Once ContentControl is devoid of options, it ends up calling ToString() on the student object and then displays that text.

Override the inherited member Object.ToString() implementation.

        public override string ToString()
        {
            return "Returned from ToString() of student class";
        }

 

image

Now that we’ve seen how boring a student object looks in the absence of data templates, it’s time to add one into the mix. Here’s the same XAML used before, only this time there is a template for the student class:

            <DataTemplate DataType="{x:Type localXAMLClass:student}" >
                <Border BorderBrush="AliceBlue" BorderThickness="2">
                    <Grid Margin="10">
                        <TextBlock>
                            <Run Text="The Name of the student is "> </Run>
                            <TextBlock Background="LightBlue" Text="{Binding name}"></TextBlock>
                            <Run Text=" And his ID is "></Run>
                            <TextBlock Background="LightBlue" Text="{Binding id}" ></TextBlock>
                        </TextBlock>
                    </Grid>
                </Border>
            </DataTemplate>

 

Essentially, the DataTemplate in the above example defines that whenever there is a student class object, it should appear as the text “The name of the student is <xyz> And his ID is <322323>. With this DataTemplate, our application now looks like this:

 

image

That is how the XAML renders to show the proper data content: The student object is displayed much more intelligently when a data template is applied. The information held within it is now displayed as part of a sentence, and that sentence is wrapped in a curved blue border. Keep in mind that the rendition of a student object seen above is completely arbitrary. It can be displayed in whatever way is considered appropriate for the application in which it exists.

The complete code of the .cs file is as follows

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IntroductiontoTemplates1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class student
    {
        private string Name;
        private int Id;

        public string name
        {
            get{return Name;}
            set{Name = value;}
        }

        public int id
        {
            get { return Id;}
            set { Id = value; }
        }

        public override string ToString()
        {
            return "Returned from ToString() of student class";
        }

    }

}

The complete XAML code is as follows

<Window x:Class="IntroductiontoTemplates1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow" Height="350" Width="525"
        xmlns:localXAMLClass="clr-namespace:IntroductiontoTemplates1" >
    <Grid>
        <Grid.Resources>
            <DataTemplate DataType="{x:Type localXAMLClass:student}" >
                <Border BorderBrush="Red" BorderThickness="2">
                    <Grid Margin="10">
                        <TextBlock>
                            <Run Text="The Name of the student is "> </Run>
                            <TextBlock Background="LightBlue" Text="{Binding name}"></TextBlock>
                            <Run Text=" And his ID is "></Run>
                            <TextBlock Background="LightBlue" Text="{Binding id}" ></TextBlock>
                        </TextBlock>
                    </Grid>
                </Border>
            </DataTemplate>

            <localXAMLClass:student x:Key="studentXAMLObject" name="kishore1021" id="1"></localXAMLClass:student>
        </Grid.Resources>
        
        <ContentControl
            Content="{StaticResource studentXAMLObject}" >
        </ContentControl>
    </Grid>
</Window>

 

Download the source code from here.

 

All men dream but not equally. Those who dream by night in the dusty recesses of their minds wake in the day to find that it was vanity; but the dreamers of the day are dangerous men, for they may act their dream with open eyes to make it possible. – T.E. Lawrence

Introduction to Multi Binding and Multi value converter using IMultiValueConverter & MultiBinding

For Introduction to Value Converters, see the post here.

For more information on mode property, see the the post on mode here.

MultiBinding allows us to bind a binding target property to a list of source properties and then apply logic to produce a value with the given inputs. This example demonstrates how to use MultiBinding. The following example produces a TextBox that adds the numbers in TextBox1 and TextBox2 and puts the result into TextBox3. Here we have two source properties and the one target property. When you enter numbers in the TextBox1 and TextBox2 , the converter comes into play and automatically adds them and puts the result into TextBox3. The values of the Mode and UpdateSourceTrigger properties determine the functionality of the MultiBinding and are used as the default values for all the bindings in the collection unless an individual binding overrides these properties. For example, if the Mode property on the MultiBinding object is set to TwoWay, then all the bindings in the collection are considered TwoWay unless you set a different Mode value on one of the bindings explicitly.

In the following code, AddConverter implements the IMultiValueConverter interface. AddConverter takes the values from the individual bindings and stores them in the values object array. The order in which the Binding elements appear under the MultiBinding element is the order in which those values are stored in the array.

 

Figure1: Important Classes hierarchy.

Figure2: Binding classes hierarchy

The XAML for the above mentioned logic is as follows.

<Window x:Class="DataConversion.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow" Height="350" Width="525"
        xmlns:mylocalXAMLnamespace="clr-namespace:DataConversion">
    <StackPanel>
        <StackPanel.Resources>
            <mylocalXAMLnamespace:AddConverter x:Key="XAMLResourceAddConverter" />
        </StackPanel.Resources>

        <TextBox Name="TextBox1" Text="10"></TextBox>
        <TextBox Name="TextBox2" Text="20"></TextBox>
        <Label Content="Sum of above two values:"></Label>
        <TextBox Name="textBox3">
            <TextBox.Text>
                <MultiBinding Converter="{StaticResource XAMLResourceAddConverter}">
                    <Binding ElementName="TextBox1" Path="Text"/>
                    <Binding ElementName="TextBox2" Path="Text"/>
                </MultiBinding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>
</Window>

 

The MultiBinding tag takes binding as a parameter. See the following C# code that does the binding.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Markup;

namespace DataConversion
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

    }

    public class AddConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            int result = Int32.Parse((string)values[0]) + Int32.Parse((string)values[1]);
            return result.ToString();
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException("Cannot convert back");
        }
    }

}

 

You can specify multiple bindings in a MultiBinding object. When you use the MultiBinding object with a converter, it produces a final value for the binding target based on the values of those bindings. For example, color might be computed from red, blue, and green values, which can be values from the same or different binding source objects. When a value moves from the target to the sources, the target property value is translated to a set of values that are fed back into the bindings.

Download the Source Code from here

If it’s a good idea, go ahead and do it. It is much easier to apologize than it is to get permission. Admiral Grace Hopper

DataContext in WPF

DataContext

DataContext is one of the most fundamental concepts in Data Binding. The Binding object needs to get its data from somewhere, and there are a few ways to specify the source of the data like using Source property directly in the Binding, inheriting a DataContext from the nearest element when traversing up in the tree, setting the ElementName and RelativeSource properties in the Binding object.

User interface elements in WPF have a DataContext dependency property. That property has the aforementioned "value inheritance" feature enabled, so if you set the DataContext on an element to a Student object, the DataContext property on all of its logical descendant elements will reference that Student object too. This means that all data bindings contained within that root element’s element tree will automatically bind against the Student object, unless explicitly told to bind against something else. This feature is extremely useful while designing WPF applications. In the following example, we are setting the DataContext in the code and all the elements in the window gets to access the Student object.

Exposing the entire object as DataContext from the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DataContextSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Student objstudent = new Student();
            objstudent.StudentName = "Kishore1021";
            this.DataContext = objstudent;
        }

        public class Student
        {
            private string studentname;

            public string StudentName
            {
                get
                {
                    return studentname;
                }
                set
                {
                    studentname = value;
                }
            }

        }
    }
}

 
XAML for the above code
  1. <Window x:Class="DataContextSample.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4.         Title="MainWindow" Height="350" Width="525">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="*"> </RowDefinition>
  8.         </Grid.RowDefinitions>
  9.         <TextBox Name="TextBox1" Text="{Binding Path=StudentName}"></TextBox>
  10.     </Grid>
  11. </Window>

 
Download the source code from Microsoft here
 

Exposing just a single property value from the code:

In the Student class, I added one more property Address. In the MainWindow, an Employee object is created and initialized with some values and we are setting just the address as the datacontext using this.DataContext = objstudent.Address; in line28

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14.  
  15. namespace DataContextSample
  16. {
  17.     /// <summary>
  18.     /// Interaction logic for MainWindow.xaml
  19.     /// </summary>
  20.     public partial class MainWindow : Window
  21.     {
  22.         public MainWindow()
  23.         {
  24.             InitializeComponent();
  25.             Student objstudent = new Student();
  26.             objstudent.StudentName = "Kishore1021";
  27.             objstudent.Address = "Washington DC";
  28.             this.DataContext = objstudent.Address;
  29.         }
  30.  
  31.         public class Student
  32.         {
  33.             private string studentname;
  34.             private string address;
  35.  
  36.             public string Address
  37.             {
  38.                 get { return address; }
  39.                 set { address = value; }
  40.             }
  41.  
  42.             public string StudentName
  43.             {
  44.                 get
  45.                 {
  46.                     return studentname;
  47.                 }
  48.                 set
  49.                 {
  50.                     studentname = value;
  51.                 }
  52.             }
  53.  
  54.  
  55.         }
  56.     }
  57. }

 

In the XAML code, created two textblocks and assigned the address. Keep a watch on how I am using the DataContext values. Since we are just setting the Address as DataContext, TextBlock1 binding doesn’t know where to find in that address property. TextBlock2 binds to whatever is there in the datacontext, which is just the address property..

  1. <Window x:Class="DataContextSample.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4.         Title="MainWindow" Height="350" Width="525">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="*"> </RowDefinition>
  8.             <RowDefinition Height="*"></RowDefinition>
  9.         </Grid.RowDefinitions>
  10.         <TextBlock Grid.Row="0" Name="TextBlock1" Text="{Binding Path=Address}"></TextBlock>
  11.         <TextBlock Grid.Row="1" Name="TextBlock2" Text="{Binding Path=.}"></TextBlock>
  12.     </Grid>
  13. </Window>

 

Download the source code from here

To Understand the above concept better, see the following code. I have an employee class and one more class called AnotherClass and used employee class as a property inside that class. In the mainwondow class, we are creating an employee class object and initializing it with some values. Then we are creating an anotherclass object and initializing with the previously created employee object as well as one more property of anotherclass “State” and then we are assigning this anotherclass object as dataconext. There are several ways to specify the binding source object. Using the DataContext property on a parent element is useful when you are binding multiple properties to the same source. However, sometimes it may be more appropriate to specify the binding source on individual binding declarations.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace Bindingtoclasses
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Employee P = new Employee("Hello World");
            P.State = "MD";
            AnotherClass c = new AnotherClass();
            c.EmployeeNameTest = P;
            c.AnotherField = "Another Value";
            this.DataContext = c;
        }
    }

    public class AnotherClass : INotifyPropertyChanged
    {

        private string anotherfield;
        private Employee emp;

        public string AnotherField
        {
            get { return anotherfield; }
            set
            {
                anotherfield = value;
                OnPropertyChanged("AnotherField");
            }
        }

        public Employee EmployeeNameTest
        {
            get { return emp; }
            set
            {
                emp = value;
                OnPropertyChanged("EmployeeNameTest");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        //// Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        public override string ToString()
        {
            return string.Format("My ToString implementation of AnotherClass");
        }

    }

    // This class implements INotifyPropertyChanged to support one-way and two-way bindings
    // (such that the UI element updates when the source has been changed dynamically)
    public class Employee : INotifyPropertyChanged
    {
        private string name;
        private string state;
        // Declare the event
        public event PropertyChangedEventHandler PropertyChanged;

        public Employee()
        {
        }

        public Employee(string value)
        {
            this.name = value;
        }

        public string EmployeeName
        {
            get { return name; }
            set
            {
                name = value;
                // Call OnPropertyChanged whenever the property is updated
               OnPropertyChanged("EmployeeName");
            }
        }

        public string State
        {
            get { return state; }
            set
            {
                state = value;
                OnPropertyChanged("State");
            }
        }

        //// Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }

}

 

In the XAML: Even though we assigned the entire anotherclass object as datacontext, I am just assigning the EmployeeNameTest object in it as the datacontext for the grid using the code <Grid DataContext="{Binding Path=EmployeeNameTest}"> .

<Window x:Class="Bindingtoclasses.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:src="clr-namespace:Bindingtoclasses"

        Title="MainWindow" Height="350" Width="525">
    <Grid DataContext="{Binding Path=EmployeeNameTest}">
        <Grid.Resources>
                <src:Employee x:Key="myDataSource" EmployeeName="Kishore1021"/>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Name="TextBox1" Grid.Row="0" Text="{Binding Source={StaticResource myDataSource}, Path=EmployeeName, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Name="TextBox2" Grid.Row="1" Text="{Binding Source= {StaticResource myDataSource}, Path=EmployeeName}"></TextBox>
        <TextBox Name="TextBox3" Grid.Row="2" Text="{Binding Path=EmployeeName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        <TextBox Name="TextBox4" Grid.Row="3" Text="{Binding Path=EmployeeName}"></TextBox>
        <TextBlock Name="TextBlock1" Grid.Row="4" Text="{Binding Path=EmployeeName}"/>
        <TextBlock Name="TextBlock2" Grid.Row="5" Text="{Binding Path=AnotherField}"/>
        
    </Grid>
</Window>

 

Download the source code from here

 

Priority of Source VS DataContext:
Code Snippet
<Window x:Class="DataBinding_Source_Property.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
       Title="MainWindow" Height="350" Width="525"
       xmlns:kishore="clr-namespace:DataBinding_Source_Property">
   <Window.Resources>
       <kishore:Person x:Key="PersonXAMLDataSource" FullName="Kishore1021"/>
        <kishore:Person x:Key="Person1" FullName="Person1Name"/>
    </Window.Resources>
   
    <Grid DataContext="{StaticResource Person1}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"> </RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
       
        <TextBox Grid.Row="0" Text="{Binding Source={StaticResource PersonXAMLDataSource}, Path=FullName}"></TextBox>
        <TextBox Grid.Row="1" Text="{Binding Path=FullName}"/>
    </Grid>
</Window>

The first TextBox inherits the DataContext from the parent Grid as well as has a Source set in the Binding object too. In this case, Source takes priority, causing the TextBox to bind to the Name property of the resource with key “PersonXAMLDataSource” – this displays “kishore1021”. The second inherits the DataContext from the parent Grid causing the TextBox to bind to the Name property of the resource with key “Person1”, causing it to display “Person1Name”. WPF will search up the element tree until it encounters a DataContext object if a Source or RelativeSource is not used. Once it finds a non-null DataContext, that object is used for binding. Useful for binding several properties to the same object.

Most data bound applications tend to use DataContext much more heavily than Source. Use DataContext only when you need to bind more than one property to a particular source. When binding only one property to a source, always use Source. The reason for this is ease of debugging – We can see all the information about the Binding in one place than search for the nearest DataContext to understand what is going on. Other than setting the DataContext property on an element directly, inheriting the DataContext value from an ancestor , and explicitly specifying the binding source by setting the Source property on the Binding, you can also use the ElementName property or the RelativeSource property to specify the binding source. The ElementName property is useful when you are binding to other elements in your application, such as when you are using a slider to adjust the width of a button. The RelativeSource property is useful when the binding is specified in a ControlTemplate or a Style.

"We must be silent before we can listen. We must listen before we can learn. We must learn before we can prepare. We must prepare before we can serve. We must serve before we can lead." – William Arthur Ward