Luker
Luker

Reputation: 71

ItemsControl displaying class name

This app is displaying the class name of a collection instead of a text-box as desired. I've read other issues with this, but cannot figure out what I'm missing. I have a datacontext, I'm bound to the collection as an itemsource, and I've added a single item. All I want is to bind the collection 'Boxes' in my view model 'DrawBoxViewModel' to an item source, and have it display a single item as a text box. All help is appreciated.

First my XAML:

<Page
x:Class="BoxMaker2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BoxMaker2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:BoxMaker2.ViewModels"
mc:Ignorable="d">

<Page.Resources>
    <vm:DrawBoxViewModel x:Key="DrawBoxViewModel"/>
</Page.Resources>
<Canvas DataContext="{Binding Source={StaticResource DrawBoxViewModel}}">
    <ItemsControl ItemsSource="{Binding Boxes}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Width="350" Height="600" Background="AliceBlue"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Resources>
            <DataTemplate x:DataType="vm:Box" x:Key="test">
                <VariableSizedWrapGrid>
                    <TextBox Background="White" 
                             Text="{x:Bind Data}"
                             Width="100"
                             Height="100"/>
                    <VariableSizedWrapGrid.RenderTransform>
                        <TranslateTransform X="{Binding LeftCanvas}" Y="{Binding TopCanvas}"/>
                    </VariableSizedWrapGrid.RenderTransform>
                </VariableSizedWrapGrid>
            </DataTemplate>
        </ItemsControl.Resources>
    </ItemsControl>
</Canvas>

And now my viewmodel:

namespace BoxMaker2.ViewModels
{
public class DrawBoxViewModel
{
    #region fields

    private ObservableCollection<Box> _boxes;

    #endregion

    #region properties

    public ObservableCollection<Box> Boxes { get { return this._boxes; } }

    #endregion

    #region constructors

    public DrawBoxViewModel()
    {
        this._boxes = new ObservableCollection<Box>();
        _boxes.Add(new Box() { Data = "hello!", LeftCanvas = 200, TopCanvas = 200 });
    }

    #endregion

}

public class Box : INotifyPropertyChanged
{
    private int _generation;
    public int Generation
    {
        get { return _generation; }
        set { _generation = value; OnPropertyChanged("Generation"); }
    }

    private int _childNo;
    public int ChildNo
    {
        get { return _childNo; }
        set { _childNo = value; OnPropertyChanged("ChildNo"); }
    }

    private Box _parentBox;
    public Box ParentBox
    {
        get { return _parentBox; }
        set { _parentBox = value; OnPropertyChanged("ParentBox"); }
    }

    private List<Box> _childrenBox;
    public List<Box> ChildrenBox
    {
        get { return _childrenBox; }
        set { _childrenBox = value; OnPropertyChanged("ChildrenBox"); }
    }

    private string _data;
    public string Data
    {
        get { return _data; }
        set
        {
            _data = value;
            OnPropertyChanged("Data");
        }
    }

    private double _topCanvas;
    public double TopCanvas
    {
        get { return _topCanvas; }
        set
        {
            _topCanvas = value;
            OnPropertyChanged("TopCanvas");
        }
    }

    private double _leftCanvas;
    public double LeftCanvas
    {
        get { return _leftCanvas; }
        set
        {
            _leftCanvas = value;
            OnPropertyChanged("LeftCanvas");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

}

Upvotes: 1

Views: 614

Answers (2)

bleepzter
bleepzter

Reputation: 10015

Your Items control doesn't know which data template to use. Currently your view model has a template associated to it via the x:DataType="vm:Box" which is defined as a resource in the items control.

The problem is that Universal Windows Platform doesn't recognize templates associated to data types. So even though there is a template, the control doesn't know how to find it when it is rendering the collection of view models.

Automatic resolving of templates based on bound types was a function of WPF which is not available in UWP.

What that means is that in WPF you could associate a data template to a class/object via the x:DataType="Object Type" attribute of the data template (which is what you did). When the collection is bound, the rendering engine would auto-magically match the the individual items in the collection to their respective templates.

This was very powerful because if your collection had many different types of boxes for example (or things inheriting from DrawBoxViewModel) you could render each item type differently by simply defining a template. Well this is no more. Microsoft destroyed that feature in UWP.

So long story short - move the template to the page resource collection. Give it a key such as:

<Page.Resources>
   <vm:DrawBoxViewModel x:Key="DrawBoxViewModel"/>
   <DataTemplate x:Key="test">
      <VariableSizedWrapGrid>
          <TextBox Background="White" 
              Text="{x:Bind Data}"
              Width="100"
              Height="100"/>
             <VariableSizedWrapGrid.RenderTransform>
                <TranslateTransform X="{Binding LeftCanvas}" Y="{Binding TopCanvas}"/>
             </VariableSizedWrapGrid.RenderTransform>
        </VariableSizedWrapGrid>
     </DataTemplate>
</Page.Resources>

Reference the template in your items control as follows: <ItemsControl ItemsSource="{Binding Boxes} ItemTemplate={StaticResource test} ">

Upvotes: 1

Justin XL
Justin XL

Reputation: 39006

I am not exactly sure what you are trying to achieve but here's a few issues I have found in your code.

  1. You should assign your VM to the DataContext of the Page directly.

    <Page.DataContext>
        <vm:DrawBoxViewModel />
    </Page.DataContext>
    

    After doing so, you can now remove DataContext="{Binding Source={StaticResource DrawBoxViewModel}}" from your Canvas.

  2. Replace <ItemsControl.Resource> with <ItemsControl.ItemTemplate> and remove x:Key="test", assuming you want to show multiple TextBoxes on the UI. The DataTemplate within the Resource you defined won't do anything until you reference it by its key. I don't think you really want that here though.
  3. You should use x:Bind for your X & Y binding

    <TranslateTransform X="{x:Bind LeftCanvas}"
                        Y="{x:Bind TopCanvas}" />
    
  4. Your Boxes collection can be simplified as following

    #region properties
    
    public ObservableCollection<Box> Boxes { get; } = new ObservableCollection<Box>();
    
    #endregion
    
    #region constructors
    
    public DrawBoxViewModel()
    {
        Boxes.Add(new Box() { Data = "hello!", LeftCanvas = 0, TopCanvas = 200 });
    }
    
    #endregion
    

Hope this helps!

Upvotes: 2

Related Questions