This example will show you how to implement IDataTemplate
in your own class to have full control over your DataTemplates
. Other than WPF, Avalonia does not have a [DataTemplateSelector
]. Therefore you will learn in this sample how you can mimic it’s behavior in Avalonia, if needed.
You should already have a basic knowledge of DataTemplates
.
[IDataTemplate
] is an interface which you can implement into your own class in order to be used as a DataTemplate
. It has the following members:
- bool Match(object?)
-
Use this function to return if the given object matches to your
DataTemplate
. - Control Build(object?)
-
Use this function to build the control which represents the given object.
We will create a convenient way to select a matching DataTemplate
based on an enum
value. The possible DataTemplates
can be defined in XAML
which should feel familiar to most developers.
ℹ️
|
Also if we use an enum in this sample, it can be any object in your implementation.
|
Create a new Project using the Avalonia MVVM Project
template.
Inside the Models
directory add an enum
called ShapeTypes.cs
:
// Feel free to add more shapes based on your needs.
public enum ShapeType
{
RedCircle,
RedSquare,
BlueCircle,
BlueSquare
}
In the file ViewModels ► MainWindowViewModel.cs
we will add a Property SelectedShape
and an Array
of available shapes called AvailableShapes
:
public class MainWindowViewModel : ViewModelBase
{
private ShapeType _SelectedShape;
/// <summary>
/// Gets or sets the selected ShapeType
/// </summary>
public ShapeType SelectedShape
{
get { return _SelectedShape; }
set { this.RaiseAndSetIfChanged(ref _SelectedShape, value); }
}
/// <summary>
/// Gets an array of all available ShapeTypes
/// </summary>
public ShapeType[] AvailableShapes { get; } = Enum.GetValues<ShapeType>();
}
ℹ️
|
To get the array of available enum members we can use Enum.GetValues . Learn more in the [Microsoft Docs].
|
Add a new directory called DataTemplates
. Inside this directory add a new file called ShapesTemplateSelector.cs
. This class should implement IDataTemplate
. We will use a Dictionary
where the keys
are a string
and the values are of type IDataTemplate
. This dictionary will be decorated with the [ContentAttribute
] which will make it easy to add several items in the XAML
later.
We need to implement IDataTemplate in this class. Inside the Match
-function we will check if the provided item is inside our Dictionary
. If that is the case can use Build
to build the control. As the values of our Dictionary
are IDataTemplates
on their own, we can just call their Build
-function and return the result.
// Remember to add the needed usings
using Avalonia.Controls.Templates;
using Avalonia.Controls;
using Avalonia.Metadata;
using System;
using System.Collections.Generic;
using IDataTemplateSample.Models;
public class ShapesTemplateSelector : IDataTemplate
{
// This Dictionary should store our shapes. We mark this as [Content], so we can directly add elements to it later.
[Content]
public Dictionary<string, IDataTemplate> AvailableTemplates { get; } = new Dictionary<string, IDataTemplate>();
// Build the DataTemplate here
public Control Build(object? param)
{
var key = param?.ToString(); // Our Keys in the dictionary are strings, so we call .ToString() to get the key to look up
if (key is null) // If the key is null, we throw an ArgumentNullException
{
throw new ArgumentNullException(nameof(param));
}
return AvailableTemplates[key].Build(param); // finally we look up the provided key and let the System build the DataTemplate for us
}
// Check if we can accept the provided data
public bool Match(object? data)
{
// Our Keys in the dictionary are strings, so we call .ToString() to get the key to look up
var key = data?.ToString();
return data is ShapeType // the provided data needs to be our enum type
&& !string.IsNullOrEmpty(key) // and the key must not be null or empty
&& AvailableTemplates.ContainsKey(key); // and the key must be found in our Dictionary
}
}
Open the file Views ► MainWindow.axaml
and create a new ShapesTemplateSelector
inside Window.DataTemplates
:
<!-- Remember to add the needed namespaces
xmlns:model="using:IDataTemplateSample.Models"
xmlns:dataTemplates="using:IDataTemplateSample.DataTemplates" -->
<Window.DataTemplates>
<dataTemplates:ShapesTemplateSelector>
<DataTemplate x:Key="RedCircle" DataType="model:ShapeType">
<Ellipse Width="50"
Height="50"
Fill="Red"
Stroke="DarkRed"
StrokeThickness="2" />
</DataTemplate>
<DataTemplate x:Key="BlueCircle" DataType="model:ShapeType">
<Ellipse Width="50"
Height="50"
Fill="Blue"
Stroke="DarkBlue"
StrokeThickness="2" />
</DataTemplate>
<DataTemplate x:Key="RedSquare" DataType="model:ShapeType">
<Rectangle Width="50"
Height="50"
Fill="Red"
Stroke="DarkRed"
StrokeThickness="2" />
</DataTemplate>
<DataTemplate x:Key="BlueSquare" DataType="model:ShapeType">
<Rectangle Width="50"
Height="50"
Fill="Blue"
Stroke="DarkBlue"
StrokeThickness="2" />
</DataTemplate>
</dataTemplates:ShapesTemplateSelector>
</Window.DataTemplates>
ℹ️
|
Each item in the Dictionary must have an x:Key . This key must represent the name of the enum -member in our case.
|
ℹ️
|
You can also define the ShapesTemplateSelector in App.DataTemplates or any other DataTemplates section.
|
Now we can add the content we like to our Window
. In our case we just add a TextBlock
and a ComboBox
:
<StackPanel>
<TextBlock Text="Select a Shape" />
<ComboBox ItemsSource="{Binding AvailableShapes}"
SelectedIndex="0"
SelectedItem="{Binding SelectedShape}" />
</StackPanel>