列表视图仅显示空行。 WPF MVVM

时间:2014-12-16 14:26:17

标签: c# wpf mvvm

尝试将sqlite数据表绑定到列表视图。问题是它在数据库中显示正确的行数,但是显示为空行。因此,如果数据计数为5,则显示五个空数据行。以下是我整个解决方案的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Data;
using System.Data.SQLite;
using WpfApplication_tutorial.Properties;

namespace WpfApplication_tutorial.Model
{
    public class Student : IDataErrorInfo
    {

        public Student(string firstName, string lastName)
        {
            this.FirstName = firstName;
            this.LastName = lastName;            
        }

        private Student() { }

        public string FirstName
        {
            get;
            set;
        }

        public string LastName
        {
            get;
            set;
        }

        string IDataErrorInfo.Error { get { return null; } }

        string IDataErrorInfo.this[string propertyName]
        {
            get { return this.GetValidationError(propertyName); }
        }

        public bool IsValid
        {
            get
            {
                foreach (string property in ValidatedProperties)
                    if (GetValidationError(property) != null)
                        return false;
                return true;
            }
        }

        static readonly string[] ValidatedProperties = 
       {
           "FirstName",
           "LastName"
       };

        string GetValidationError(string propertyName)
        {
            if (Array.IndexOf(ValidatedProperties, propertyName) < 0)
                return null;

            string error = null;

            switch (propertyName)
            {
                case "FirstName":
                    error = this.ValidateFirstName();
                    break;

                case "LastName":
                    error = this.ValidateLastName();
                    break;

                default:
                    Debug.Fail("Unknown property being validated on Student", propertyName);
                    break;
            }
            return error;
        }

        string ValidateFirstName()
        {
            if (IsStringMissing(this.FirstName))
            {
                return Strings.Student_MissingFirstName_Error;
            }
            return null;
        }

        string ValidateLastName()
        {
            if (IsStringMissing(this.LastName))
            {
                return Strings.Student_MissingLastName_Error;
            }
            return null;
        }

        static bool IsStringMissing(string value)
        {
            return
                String.IsNullOrEmpty(value) || value.Trim() == String.Empty;
        }
    }

}

以下是我的viewmodel的代码。它包括创建和选择表

的功能
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Security;
using System.Windows;
using System.Windows.Input;
using System.IO;
using System.Data;
using System.Data.SQLite;
using System.Windows.Media;
using System.Windows.Media.Animation;
using GalaSoft.MvvmLight;
using WpfApplication_tutorial.Model;
using WpfApplication_tutorial.View;
using WpfApplication_tutorial.UserControls;

namespace WpfApplication_tutorial.ViewModel
{
    public class StudentViewModel : ViewModelBase, IDataErrorInfo
    {
        readonly Student _student;
        private string firstName = string.Empty;
        private string lastName = string.Empty;
        private DataView studentDetails = null;


        // Command for registering new student
        private ICommand registerStudent;

        /// <summary>
        /// Initializes a new instance of the StudentViewModel class.
        /// </summary>
        public StudentViewModel()
        {
            _student = new Student(firstName, lastName);
            firstName = _student.FirstName;
            lastName = _student.LastName;
            FormOne();

        }

        public string FirstName
        {
            get { return _student.FirstName; }
            set
            {
                if (value == _student.FirstName)
                    return;
                _student.FirstName = value;
                OnPropertyChanged("FirstName");
            }
        }




///Please note that i tried this to
       public string FirstName
        {
           get { return firstNam; }
           set 
           { 
              firstName = value;
              OnPropertyChanged("FirstName");
           }
       }


        public string LastName
        {
            get { return _student.LastName; }
            set
            {
                if (value==_student.LastName)
                    return;
                _student.LastName = value;

                OnPropertyChanged("LastName");
            }
        }

        public DataView StudentDetails
        {
            get { return studentDetails; }
            set
            {
                if (value == studentDetails)
                    return;
                studentDetails = value;
                OnPropertyChanged("StudentDetails");
            }
        }


        public ICommand RegisterStudent
        {
            get
            {
                if (registerStudent == null)
                {
                    registerStudent = new CommandBase(i => this.CreateStudent(), null);
                }
                return registerStudent;
            }
        }

public void FormOne()
        {

            string databaseName = "Kwega.db3";
            SQLiteConnection connection = new SQLiteConnection("Data Source=" + databaseName + "; Version=3;");
            string students = "SELECT first_name, last_name FROM students";
            SQLiteDataAdapter adapter = new SQLiteDataAdapter(students, connection);
            connection.Open();
            adapter.SelectCommand.CommandTimeout = 120;
            DataSet ds = new DataSet();
            adapter.Fill(ds, "students");
            StudentDetails = ds.Tables["students"].DefaultView;
            connection.Close();

        }


        /// <summary>
        /// Method to create new student and creating a new student table if
        /// it doesnt exist in the database
        /// </summary>
        private void CreateStudent()
        {
            if (_student.IsValid)
            {
                string databaseName = "Kwega.db3";
                var connection = new SQLiteConnection("Data Source=" + databaseName + "; Version=3;");
                connection.Open();
                var createStudentTable =
                    "CREATE TABLE IF NOT EXISTS students (student_id INTEGER PRIMARY KEY, first_name TEXT(255), last_name TEXT(255))";

                var createCommand = new SQLiteCommand(createStudentTable, connection);
                createCommand.ExecuteNonQuery();

                string insert_student = "INSERT INTO students(first_name, last_name) VALUES (" +
                                        "'" + _student.FirstName + "', '" + _student.LastName + "')";

                var insert_CMD = new SQLiteCommand(insert_student, connection);
                insert_CMD.ExecuteNonQuery();
                connection.Close();
            }
            else
            {
                MessageBox.Show("Student details weren't saved", "Invalid student!", MessageBoxButton.OK, MessageBoxImage.Information);
            }

        }

        string IDataErrorInfo.Error
        {
            get { return (_student as IDataErrorInfo).Error; }
        }

        string IDataErrorInfo.this[string propertyName]
        {
            get
            {
                string error = (_student as IDataErrorInfo)[propertyName];
                return error;
            }
        }

    }

}

我认为错误可能在我的viewmodel中,但我在最近3天内无法调用它。下面是我的codebehind文件和xaml文件。

using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfApplication_tutorial.Model;
using WpfApplication_tutorial.ViewModel;

namespace WpfApplication_tutorial.UserControls
{
    /// <summary>
    /// Interaction logic for FormOneDataControl.xaml
    /// </summary>
    public partial class FormOneDataControl : UserControl
    {        
        public StudentViewModel ViewModel;

        public FormOneDataControl()
        {
            InitializeComponent();                    
            StudentViewModel studentViewModel = new StudentViewModel();            
            this.DataContext = studentViewModel;
        }  
     }
}          

最后是我的xaml文件

<ListView x:Name="FormOneView" ItemsSource="{Binding }" DataContext="{Binding StudentDetails}" >
           <ListView.View>
                                <GridView>
                                    <GridViewColumn Header="First Name" Width="90" DisplayMemberBinding="{Binding Path=FirstName}"  />
                                    <GridViewColumn Header="Last Name" Width="90" DisplayMemberBinding="{Binding Path=LastName}"  />
                                </GridView>
                            </ListView.View>
                        </ListView>

请注意,我尝试使用ItemsSource="{Binding Path=MethodName}" and DisplayMemberBinding =&#34; {Binding FirstName}&#34;`例如。

4 个答案:

答案 0 :(得分:0)

DataView不是您数据的好容器。您最好声明一个自定义类,其属性要显示在ListView中,以便您可以使用这些命名属性进行数据绑定。

目前,您的XAML已经混淆了......您的ListView正在StudentDetails属性中查找项目DataView,但随后是GridViewColumn.DisplayMemberBinding } s指向StudentViewModel中与DataView中的项目无关的属性。

相反,使用这些名称属性创建自定义类,然后在StudentViewModel类中创建该类型的集合,并将数据绑定到该集合属性。然后你的GridViewColumn.DisplayMemberBinding应该有效。

答案 1 :(得分:0)

让我们从不同的角度思考这个问题。

您的ViewModel应包含一系列学生,而不是每个ViewModel一名学生。

public class Student
{
    //Student memebers here
}

public class StudentViewModel
{
    public ObservableCollection<Student> Students { get; set; }

    public StudentViewModel()
    {
        this.Students = new ObservableCollection<Student>();

        //Call some method to load all students into the collection.
    }

    ...
}

然后在您的视图中,您可以创建ViewModel的实例,并绑定到Students集合。像这样:

<Window.Resources>
    <YourNamespace:StudentViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">
    <ListBox ItemsSource="{Binding Students}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                ...
                <TextBlock Text="{Binding FirstName}"/>
                ...
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

不要忘记您需要在XAML视图顶部的xmlns声明中定义ViewModel的命名空间。视图上的实现只是如何实现绑定的一个示例。

答案 2 :(得分:0)

如果不更改包含此ListView的控件的DataContext属性,则已在ListView的DataContext中拥有viewmodel类实例,因此不必设置该属性。试试这个:

<ListView x:Name="FormOneView" ItemsSource="{Binding StudentDetails}">

此页面可以帮助您更好地了解数据绑定的工作原理: Data Binding Overview

如果您以这种方式更改班级学生可能会有所帮助:

 public class Student : IDataErrorInfo, INotifyPropertyChanged
{

    public Student(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    private Student()
    {
    }

    private string firstName;
    public string FirstName
    {
        get { return firstName; }
        set
        {
            if (value == FirstName)
                return;
            firstName = value;
            OnPropertyChanged("FirstName");
        }
    }

    private string lastName;
    public string LastName
    {
        get { return lastName; }
        set
        {
            if (value == lastName)
                return;
           lastName = value;

            OnPropertyChanged("LastName");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    //...
}

因此,在ViewModel类中,您可以拥有两个属性:

public ObservableCollection<Student> StudentDetails {get;set;}
public Student SelectedStudent {get;set;}

在列表视图中,您可以执行以下操作:

 <Window.Resources>
<YourNamespace:StudentViewModel x:Key="StudentViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource StudentViewModel}">
    <ListView x:Name="FormOneView" ItemsSource="{Binding StudentDetails}" SelectedItem="{Binding SelectedStudent}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding FirstName}"/>
                    <TextBlock Text="{Binding LastName}"/>
                </StackPanel>        
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

无论如何,我最喜欢使用为在Silverlight,WPF,Windows Phone,Windows 8和Xamarin Android应用程序中应用此模式而创建的框架之一,例如MVVM Light Toolkit

答案 3 :(得分:0)

虽然已经有一些好的答案可以解决当下的问题,但你有一个概念上的错误,我认为这是你困惑的核心。也就是说,您正在将StudentViewModel用于两种不同的模型并且它们存在冲突。每个答案都是选择其中一个模型作为主要模型并尝试更改代码以使其具有首要性。我认为更好的选择是略微改变StudentViewModel以使主模型更清晰。

对我而言,您似乎主要将StudentViewModel设想为学生的模型,而不是您最终需要绑定到ListView的学生集合。如果是这种情况,那么您需要从ViewModel中删除StudentDetails,并提供一个静态方法,用于根据数据库中的对象获取有效StudentViewModel对象的列表。这更清楚地描述了您对StudentViewModel的意图,并清楚地表明学生的详细信息不是类本身的一部分(因为它可以在不实例化的情况下调用)。所以StudentViewModel看起来像这样:

public class StudentViewModel : ViewModelBase, IDataErrorInfo
{
    readonly Student _student;

    // Command for registering new student
    private ICommand registerStudent;

    /// <summary>
    /// Initializes a new instance of the StudentViewModel class.
    /// </summary>
    public StudentViewModel(string firstName, string lastName)
    {
        _student = new Student(firstName, lastName);
   }

    public string FirstName
    {
        get { return _student.FirstName; }
        set
        {
            if (value == _student.FirstName)
                return;
            _student.FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }

    public string LastName
    {
        get { return _student.LastName; }
        set
        {
            if (value==_student.LastName)
                return;
            _student.LastName = value;

            OnPropertyChanged("LastName");
        }
    }

    public ICommand RegisterStudent
    {
        get
        {
            if (registerStudent == null)
            {
                registerStudent = new CommandBase(i => this.CreateStudent(), null);
            }
            return registerStudent;
        }
    }

public static IEnumerable<StudentViewModel> GetStudents()
    {

        string databaseName = "Kwega.db3";
        SQLiteConnection connection = new SQLiteConnection("Data Source=" + databaseName + "; Version=3;");
        string students = "SELECT first_name, last_name FROM students";
        SQLiteDataAdapter adapter = new SQLiteDataAdapter(students, connection);
        connection.Open();
        adapter.SelectCommand.CommandTimeout = 120;
        DataSet ds = new DataSet();
        adapter.Fill(ds, "students");
        foreach (var student in ds.Tables["students"].DefaultView)
        {
            yield return new StudentViewModel(student[0], student[1]) // or whatever the fields actually are in the table
        }
        connection.Close();
    }

    /// <summary>
    /// Method to create new student and creating a new student table if
    /// it doesnt exist in the database
    /// </summary>
    private void CreateStudent()
    {
        if (_student.IsValid)
        {
            string databaseName = "Kwega.db3";
            var connection = new SQLiteConnection("Data Source=" + databaseName + "; Version=3;");
            connection.Open();
            var createStudentTable =
                "CREATE TABLE IF NOT EXISTS students (student_id INTEGER PRIMARY KEY, first_name TEXT(255), last_name TEXT(255))";

            var createCommand = new SQLiteCommand(createStudentTable, connection);
            createCommand.ExecuteNonQuery();

            string insert_student = "INSERT INTO students(first_name, last_name) VALUES (" +
                                    "'" + _student.FirstName + "', '" + _student.LastName + "')";

            var insert_CMD = new SQLiteCommand(insert_student, connection);
            insert_CMD.ExecuteNonQuery();
            connection.Close();
        }
        else
        {
            MessageBox.Show("Student details weren't saved", "Invalid student!", MessageBoxButton.OK, MessageBoxImage.Information);
        }

    }

    string IDataErrorInfo.Error
    {
        get { return (_student as IDataErrorInfo).Error; }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string error = (_student as IDataErrorInfo)[propertyName];
            return error;
        }
    }

}

如果您使用的是ObservableCollection而不是IEnumerable,我实际上对此感觉更舒服,但那只是我。

从那里开始,您需要在表单上使用CollectionViewSource并使用GetStudents填充它。

public partial class FormOneDataControl : UserControl
{        
    public StudentViewModel ViewModel;

    public FormOneDataControl()
    {
        InitializeComponent();
        myCollectionViewSource.DataSource = StudentViewModel.GetStudents(); // or myCollectionViewSource.ItemsSource? Crap, can't pull the CollectionViewSource properties from raw memory...
    }  
 }