简体   繁体   中英

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));
    }
}

}

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 TextBox es 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!

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} ">

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM