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

本文关键字:WPF MVVM 显示 视图 列表 | 更新日期: 2023-09-27 18:34:25

我试图将 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;
        }
    }
}

下面是我的视图模型的代码。它包括创建和从表中选择的功能

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

我认为错误可能在我的视图模型中,但我只是无法在过去 3 天内调用它。下面是我的代码隐藏文件和 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="{Binding FirstName}"'。

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

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

目前,您的 XAML 已混淆...您的ListView正在StudentDetails属性中查找项目,这是一个DataView,但是您的GridViewColumn.DisplayMemberBinding指向StudentViewModel中的属性,这些属性与DataView中的项目无关。

相反,请使用这些名称属性创建自定义类,然后在 StudentViewModel 类中创建该类型的集合,并将数据绑定到该集合属性。那么你的GridViewColumn.DisplayMemberBinding应该可以工作了。

让我们从另一个角度来思考这个问题。

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

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 声明中定义视图模型的命名空间。视图上的实现只是如何实现绑定的一个示例。

虽然已经有一些很好的答案可以解决当下的问题,但你有一个概念上的错误,我认为这是你困惑的核心。也就是说,您正在将 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...
    }  
 }

如果不更改包含此 ListView 的控件的 DataContext 属性,则在 ListView 的 DataContext 中已有视图模型类实例,因此无需设置该属性。试试这个:

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

此页可帮助你更好地了解数据绑定的工作原理:数据绑定概述

如果您像这样更改班级学生可能会有所帮助:

 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