Browsing the list displays only blank lines. WPF MVVM

I am trying to bind sqlite datatable to list view. The problem is displaying the correct number of rows in the database, but as empty rows. Therefore, if the data count is five, it displays five blank dates. Below is the code for my entire solution.

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

}

      

And below is the code for my viewmodel. It includes a fuction function to create and select from a table

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

    }

}

      

I think the error might be in my viewmodel, but I just cannot name it for the last 3 days. Below is my code file and xaml file.

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

      

And finally my xaml file

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

      

Note that I tried to use ItemsSource="{Binding Path=MethodName}" and

DisplayMemberBinding = "{Binding FirstName}" 'for example.

+3


source to share


4 answers


A is DataView

not a good container for your data. You would be better off declaring a custom class with properties displayed in yours ListView

so that you can use those named properties for data binding.

At the moment your XAML is confusing ... yours is ListView

looking in a property StudentDetails

for elements, and this DataView

, but then yours is GridViewColumn.DisplayMemberBinding

pointing to properties in StudentViewModel

that are not relevant to elements in DataView

.



Instead, create your own class with these name properties, then create a collection of that type in the class StudentViewModel

and bind data to that collection property. Then yours GridViewColumn.DisplayMemberBinding

should work.

0


source


Think about it from a different angle.

Your ViewModel should contain a set of students, as opposed to 1 student per 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.
    }

    ...
}

      



Then on your View, you can instantiate the ViewModel and bind to the student collection. Like this:

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

      

Don't forget that you will need to define your ViewModel's namespace in the xmlns declarations at the top of the XAML view. The implementation on the view is just an example of how you can achieve binding.

0


source


Unless you change the DataContext property of the control that contains this ListView, you already have an instance of the viewmodel class in the DataContext of the ListView, so you do not need to set this property. Try the following:

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

      

This page will help you better understand how data binding works: An overview of data binding

It might help if you change the Student class like this:

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

    //...
}

      

So, you have a ViewModel class you can have two properties:

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

      

And in the list, you can do something like this:

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

      

Anyway, I am using one of the frameworks created to apply this pattern in Silverlight, WPF, Windows Phone, Windows 8 and Xamarin applications, such as MVVM Light Toolkit

0


source


While there are some good answers to solve the moment problem, you have a concept error that I think is at the heart of your confusion. Namely, you are using StudentViewModel for two different models and they are in conflict. Each answer chooses one of these models as its primary and tries to modify your code to give it priority. I think a better choice would be to modify the StudentViewModel a bit to make the main model clearer.

To me, it looks like you primarily think of the StudentViewModel as a sample student, not a set of students that you will eventually have to bind to your ListView. If so, then you need to remove StudentDetails from the ViewModel and provide a static method to get a list of valid StudentViewModel objects based on the data in the database. This more clearly describes your intent with the StudentViewModel and makes it clear that the student details are not part of the class itself (since it can be called without instantiating the class). So StudentViewModel will look like this:

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

}

      

I would be better off with it if you were using ObservableCollection and not IEnumerable, but that's just me.

From there, you need a CollectionViewSource on your form and fill it with 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...
    }  
 }

      

0


source







All Articles