列表视图仅显示空行.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}"'。
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