如何将模板中的datagridcells绑定到嵌套对象

时间:2015-10-29 13:35:24

标签: c# wpf mvvm data-binding

MVVM应用程序的真正插件基于共享DropBox中的csv文件。其中一列(在下面的简化示例“B”中)可能具有需要使用图像进行解释的单元格值。因此,如果需要,我们在DropBox中添加一个文件夹,以包含图像的单元格值(示例中为“B1”)命名。最终这些图像应成为缩略图,如果选择缩略图则为全屏图像。但是现在,如果添加的列可以显示属于相邻单元格中列“B”中的单元格值的文件路径,那将是很好的。然而,经过数天的研究和反复试验,结果是:
Cell in front of A0 and A1 both show D:\Temp\B1\test0.jpg
虽然A0前面的单元格应该是空的,但在A1前面的单元格应该是:
d:\ TEMP \ B1 \ test0.jpg
d:\ TEMP \ B1 \ test1.jpg

我有下一个问题:

  1. 为什么我必须再次为ListBox和for设置DataContext TextBlock?它已经为Window设置了。他们不在吗? 逻辑树?
  2. 为什么只显示第一个文件以及原因     列的所有单元格?
  3. 我做错了什么?
  4. 观点:

    <Window x:Class="WpfApplication3.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:myViewModel="clr-namespace:WpfApplication3" 
            Title="MainWindow" Height="120" Width="200">
        <Window.Resources>
            <myViewModel:testConverter x:Key="myTestConverter"/>
        </Window.Resources>
        <Window.DataContext>
            <myViewModel:ViewModel/>
        </Window.DataContext>
        <Grid>
            <DataGrid x:Name="myXAMLtable" AutoGenerateColumns="True" CanUserAddRows="False"
                      ItemsSource="{Binding PropDataTable}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ListBox x:Name="folder" ItemsSource="{Binding MyImageFolderList,
                                         Converter={StaticResource myTestConverter}}">
                                    <ListBox.DataContext>
                                        <myViewModel:ViewModel/>
                                    </ListBox.DataContext>
                                    <ListBox.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding MyImageFolderList/MyImageList/MyImagePath}">
                                                <TextBlock.DataContext>
                                                    <myViewModel:ViewModel/>
                                                </TextBlock.DataContext>
                                            </TextBlock>
                                        </DataTemplate>
                                    </ListBox.ItemTemplate>
                                </ListBox>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    (我使用虚拟转换器来测试绑定,这是我在stackoverflow中学到的一个技巧)
    ViewModel:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    
    namespace WpfApplication3
    {
        class ViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            public void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
            //private Model _Model; //for clarity left out
            private DataTable propDataTable;
            public DataTable PropDataTable
            {
                get { return propDataTable; }
                set
                {
                    propDataTable = value;
                    NotifyPropertyChanged("PropDataTable");
                }
            }
            private List<MyImageFolder> myImageFolderList;
            public List<MyImageFolder> MyImageFolderList
            {
                get { return myImageFolderList; }
                set
                {
                    myImageFolderList = value;
                    NotifyPropertyChanged("MyImageFolderList");
                }
            }
            public ViewModel()
            {
                DataTable tempPropDataTable = new DataTable();
                tempPropDataTable.Columns.Add("A", typeof(string));
                tempPropDataTable.Columns.Add("B", typeof(string));
                DataRow row0 = tempPropDataTable.NewRow();
                DataRow row1 = tempPropDataTable.NewRow();
                row0[0] = "A0";
                row0[1] = "B0";
                row1[0] = "A1";
                row1[1] = "B1";
                tempPropDataTable.Rows.Add(row0);
                tempPropDataTable.Rows.Add(row1);
                PropDataTable = tempPropDataTable;
    
                MyImageFolderList = new List<MyImageFolder>();
                //in D:\Temp\B1 there are two filesP test0.jpg and test1.jpg
                string B0 = "D:\\Temp\\B1\\test0.jpg";
                string B1 = "D:\\Temp\\B1\\test1.jpg";
                MyImageFolder mif = new MyImageFolder("B1");
                MyImage mi0 = new MyImage(B0);
                MyImage mi1 = new MyImage(B1);
                mif.MyImageList = new List<MyImage>();//did you forget this???
                mif.MyImageList.Add(mi0);
                mif.MyImageList.Add(mi1);
                MyImageFolderList.Add(mif);
            }
        }
    }
    

    MyImageFolder类:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    
    namespace WpfApplication3
    {
        public class MyImageFolder : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
            private string myImageFolderPath = "";
            public string MyImageFolderPath
            {
                get { return myImageFolderPath; }
                set
                {
                    myImageFolderPath = value;
                    NotifyPropertyChanged("MyImageFolderPath");
                }
            }
            private List<MyImage> myImageList = new List<MyImage>();
            public List<MyImage> MyImageList
            {
                get { return myImageList; }
                set
                {
                    myImageList = value;
                    NotifyPropertyChanged("MyImageList");
                }
            }
            public MyImageFolder(string fp)
            {
                this.MyImageFolderPath = fp;
            }
        }
    }
    

    MyImage类:

    using System;
    using System.ComponentModel;
    
    namespace WpfApplication3
    {
        public class MyImage : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
            private string myImagePath = "";
            public string MyImagePath
            {
                get { return myImagePath; }
                set
                {
                    myImagePath = value;
                    NotifyPropertyChanged("MyImagePath");
                }
            }
            //constructor
            public MyImage(string ip)
            {
                MyImagePath = ip;
            }
        }
    }
    

1 个答案:

答案 0 :(得分:0)

你到处都写着像

这样的东西
<ListBox.DataContext>
    <myViewModel:ViewModel/>
</ListBox.DataContext>

您正在创建ViewModel类的新实例,并将其设置为UI对象后面的DataContext。这几乎不是你想要的,因为最终结果是这样的(注意ViewModel对象的许多实例):

<DataGridRow DataContext=ViewModel1.PropDataTable[0]>
    <ListBoxItem DataContext=ViewModel2.MyImageFolderList[0]>
        <TextBlock DataContext=ViewModel3.MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel2.MyImageFolderList[1]>
        <TextBlock DataContext=ViewModel4.MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel2.MyImageFolderList[2]>
        <TextBlock DataContext=ViewModel5.MyImageFolderPath />
    </ListBoxItem>
</DataGridItem>

<DataGridRow DataContext=ViewModel1.PropDataTable[1]>
    <ListBoxItem DataContext=ViewModel6.MyImageFolderList[0]>
        <TextBlock DataContext=ViewModel7.MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel6.MyImageFolderList[1]>
        <TextBlock DataContext=ViewModel8.MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel6.MyImageFolderList[2]>
        <TextBlock DataContext=ViewModel.MyImageFolderPath />
    </ListBoxItem>
</DataGridRow>

DataContext继承自父控件,因此您应该使用该假设为您编写绑定。

你想要的是ViewModel对象的一个​​副本,它是一个名为PropDataTable的属性,它是一个对象集合。 PropDataTable中的每个对象都应该有一个名为MyImageFolderList的对象集合,MyImageFolderList中的每个项都应该有一个名为MyImageFolderPath的属性。

因此,如果您有以下内容(请注意,缺少将.DataContext分配给ViewModel的新实例)

<DataGrid ItemsSource="{Binding PropDataTable"}>
    ...
    <ListBox ItemsSource="{Binding MyImageFolderList"}>
        ...
        <TextBlock Text="{Binding MyImageFolderPath}" />
        ...
    </ListBox>
</DataGrid>

它实际上是按照以下方式呈现的:

<DataGridRow DataContext=ViewModel1.PropDataTable[0]>
    <ListBoxItem DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[0]>
        <TextBlock DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[0].MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[1]>
        <TextBlock DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[1].MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[2]>
        <TextBlock DataContext=ViewModel1.PropDataTable[0].MyImageFolderList[2].MyImageFolderPath />
    </ListBoxItem>
</DataGridItem>

<DataGridRow DataContext=ViewModel1.PropDataTable[1]>
    <ListBoxItem DataContext=ViewModel1.PropDataTable[1].MyImageFolderList[0]>
        <TextBlock DataContext=ViewModel1.PropDataTable[1].MyImageFolderList[0].MyImageFolderPath />
    </ListBoxItem>
    <ListBoxItem DataContext=ViewModel1.PropDataTable[1].MyImageFolderList[1]>
        <TextBlock DataContext=ViewModel1.PropDataTable[1].MyImageFolderList[1].MyImageFolderPath />
    </ListBoxItem>
</DataGridRow>

我在我的博客上有一篇文章,如果您也在努力理解它,可以更详细地介绍一下DataContext。 What is this "DataContext" you speak of?