将数据从Data类传递到View

时间:2013-11-25 19:52:12

标签: c# wpf mvvm view

我正在学习MVVM并正在为练习创建应用

我正在尝试创建一个应用程序,您可以添加并查看学生的一切正常,直到我尝试将Observable Collection与ViewModel的其余部分分开,并能够将其用于其他ViewModel。

我的问题是如何使用ObservableCollection或每个View中任何ViewModel之外的任何其他类型的数据持有者?

告诉我你是否需要代码?

这是包含数据的类 在解决方案资源管理器中,文件的路径是数据/数据库

public class StudentDatabase
{
    #region Defining and Populating an ObservableCollection
    // Defining an observable collection to hold the students
    private ObservableCollection<Student> studentData;

    // Populating the ObservableCollection
   public StudentDatabase()
    {
        studentData = new ObservableCollection<Student>()
        {
            new Student(){Name="John", Surname="Smith", Age=17},
            new Student(){Name="Barbara", Surname="Johnson", Age=16}
        };
    }

    // Defining and setting the field for the Observable collection
    public ObservableCollection<Student> StudentData
    {
        get { return studentData;  }
        set { RaisePropertyChanged("studentData"); }
    }
    #endregion
    #region RaisePropertyChange implementation
    /// <summary>
    /// INotifyPropertyChanged implementation:
    /// A property notifies something that it has changed 
    /// and the other object get's the new data
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

以下是我要展示的页面的XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="15*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <ListBox DataContext="{Binding Path=Data.StudentDatabase}"
             ItemsSource="{Binding StudentData}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock x:Name="Name"
                               Grid.Column="0"
                               Text="{Binding Name}"/>
                    <TextBlock x:Name="Surname"
                               Grid.Column="1"
                               Text="{Binding Surname}"/>
                    <TextBlock x:Name="Age"
                               Grid.Column="2"
                               Text="{Binding Age}"/>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

搜索窗口急剧代码

public partial class Search: Window
{
    public Search()
    {
        InitializeComponent();
    }
}

我在visual studio的输出窗口中出现此错误

System.Windows.Data Error: 40 : BindingExpression path error: 'Data' property not found on 'object' ''StudentDatabase' (HashCode=2650314)'. BindingExpression:Path=Data.StudentDatabase; DataItem='StudentDatabase' (HashCode=2650314); target element is 'ListBox' (Name=''); target property is 'DataContext' (type 'Object')

我希望列表框的更新是动态的,所以我不包含更新按钮

编辑:我已经更新了代码,但我认为我没有再做正确的事了,有人可以照亮我吗?

编辑:如果我在代码隐藏中设置DataContext一切正常但是如果我尝试在XAML中为特定控件设置DataContext onlt那么我没有得到列表不显示数据

这是我上传项目的Skydrive的链接

如果代码隐藏就是这一切

InitializeComponent();
DataContext = new Data.StudentDatabase();

但如果我不使用代码隐藏并在XAML中这样做就没有任何反应

DataContext="{Binding Path=Data.StudentDatabase}"

我显然在这里遗漏了一些东西

2 个答案:

答案 0 :(得分:1)

这是一个应用程序设计问题。如果要共享包含所有视图的集合,则需要将其放在应用程序视图模型中,例如MainViewModel。此外,您需要从所有视图中访问它,为此,您可以在要显示集合的每个视图的视图模型中引用MainViewModel。另一种方法是共享MainViewModel实例,您可以将其保存在应用程序资源中。如果您使用Galasoft MVVMLight Toolkit,则可以通过MainViewModel访问ViewModelLocator。希望这个想法有所帮助......


修改 添加代码后,我可以注意到您对ViewModel模式的理解不是很好。您的班级StudentObservableCollection应该是StudentViewModel,它应该只有学生属性。此外,您还需要一个类来管理StudentViewModel。在本课程中,您应该拥有StudentsDataObservableCollection<StudentViewModel>,在此课程中您还可以使用SelectedStudent类型StudentViewModel等其他属性。就像我在原始答案中所说的那样,如果学生的集合应该在应用程序中的几个视图中可用,您可以在全局视图模型中创建它。

答案 1 :(得分:0)

乔治,

如果您希望在整个应用程序中共享类似数据库的内容,可以在MainWindow中创建资源,并且在MainWindow可视树下定义的任何控件都可以访问该资源。

所以在MainWindow XAML中,执行:

<Window.Resources>
    <local:StudentDatabase x:Key="StudentDatabase"/>
<Window>Resources>

在此之后的任何控件中,您可以执行类似<ListBox DataContext="{StaticResource StudentDatabase}"/>的操作,并使用您的StudentDatabase。

我建议您使数据库保持静态,因为每次使用{StaticResource StudentDatabase}时,它都会尝试重新初始化该类。将其设置为静态,您将能够创建一个实例并在整个应用程序中使用它。

当您想要修改数据库时,在代码隐藏中,您可以在事件中调用类似StudentDatabase sDB = (DataContext as StudentDatabase);的内容,并且您将能够调用函数来修改数据库。

考虑一下:

<Window x:Class="Project.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Project"
    Title="Search" Height="300" Width="300">
<Window.Resources>
    <local:StudentDatabase x:Key="StudentDatabase"/>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>

    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>

    </Grid.ColumnDefinitions>
    <ListBox x:Name="StudentItemListBox"
             Grid.Row="5" Grid.ColumnSpan="3"
             DataContext="{StaticResource StudentDatabase}"
             ItemsSource="{Binding StudentData}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="{Binding Surname}"
                                   Grid.Column="1"/>
                    <TextBlock Text="{Binding Age}"
                                   Grid.Column="2"/>
                    <!--Here go some other info about the students-->
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <StackPanel>
        <Button x:Name="Exit"
                <!--TO-DO: Write an exit command-->/>
    </StackPanel>
</Grid>