Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataTemplate fails for Shape types

I'm trying to set up a WPF DataTemplate that will be used for Line (System.Windows.Shapes.Line) objects.

From a default .NET 4 WPF Application, I set my Window xaml to:

<Window x:Class="WpfTestDataTemplates.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type system:String}" >
            <TextBlock>It's a string</TextBlock>
        </DataTemplate>
        <DataTemplate DataType="{x:Type Line}" >
            <TextBlock>It's a line</TextBlock>
        </DataTemplate>    
    </Window.Resources>

    <ListView ItemsSource="{Binding MyItems}" />
</Window>

And the code behind is:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Shapes;

namespace WpfTestDataTemplates
{
    public partial class MainWindow : Window
    {
        public List<object> MyItems {get; set; }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            MyItems = new List<object>();
            MyItems.Add("The first string");
            MyItems.Add(new Line { X1 = 0, Y1 = 0, X2 = 5, Y2 = 5 });
            MyItems.Add("The second string");
            MyItems.Add(new Rectangle { Height = 5, Width = 15 });
            MyItems.Add(42);
        }
    }
}

The resulting window looks like this: Resulting window

I expect the second entry to read: It's a line, but instead it seems the DataTemplate for the Line type is not found. For types without an explicit DataTemplate, I expect the default rendering to be the .ToString() member of the object, but that's not what is happening either. So I'd expect the fourth entry to read: System.Windows.Shapes.Rectangle

Why is the {x:Type Line} type not being recognized, and what DataTemplate is being applied to Shape objects?

like image 592
Govert Avatar asked Oct 06 '22 09:10

Govert


1 Answers

A DataTemplate is what you use to put UI on data objects that aren't themselves UIElements and have no concept of rendering themselves on-screen. Line and Rectangle however are UIElements - they know how to render themselves, and don't need a DataTemplate to tell them how.

If you give your Line and Rectangle some color, you'll see that they disregard the well-meaning DataTemplate and show up in the list as a line and a rectangle:

MyItems.Add(new Line { X1 = 0, Y1 = 0, X2 = 5, Y2 = 5,
                       Stroke = Brushes.Lime, StrokeThickness = 2 });
...
MyItems.Add(new Rectangle { Height = 5, Width = 15, Fill = Brushes.Blue });

ListView objects

To change the appearance of UIElements, you'd typically use Style (if it is a FrameworkElement) and/or ControlTemplate (if it is a Control).

Edit:

If, instead of a Line, you have your own data class representing a line (let's call it LineData), you can use a DataTemplate to render that class any way you'd like:

public class LineData
{
    public LineData(Point start, Point end)
    {
        this.Start = start;
        this.End = end;
    }

    public Point Start { get; private set; }
    public Point End { get; private set; }

    public double XLength
    {
        get { return this.End.X - this.Start.X; }
    }
    public double YLength
    {
        get { return this.End.Y - this.Start.Y; }
    }
}

...

MyItems.Add(new LineData(new Point(10, 10), new Point(60, 30)));

..and the DataTemplate..

<DataTemplate DataType="{x:Type vm:LineData}" >
    <StackPanel Orientation="Horizontal" SnapsToDevicePixels="True" >
        <TextBlock>It's a line:</TextBlock>
        <Grid>
            <Rectangle Stroke="Black" StrokeThickness="1" 
                        Width="{Binding Path=XLength}" Height="{Binding Path=YLength}" />
            <Line Stroke="Red" StrokeThickness="2"
                    X1="{Binding Path=Start.X}" Y1="{Binding Path=Start.Y}" 
                    X2="{Binding Path=End.X}" Y2="{Binding Path=End.Y}" />
        </Grid>
    </StackPanel>
</DataTemplate>

..gives us:

ListView line

like image 157
Sphinxxx Avatar answered Oct 10 '22 02:10

Sphinxxx