Category Archives: ListBox

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