0

I am new to WPF and I am trying to figure out how to create an "Update" button that will show all the contents of a row on a table, allowing the user to change any category and save the new data.

What the main window would look like : What the main window would look like

Then when the update button is clicked, a window looking like this would pop up. : a window looking like this would pop up.

How would you make the second window? Down below is what I have to show the main window without an "update" button.

**MainWindowxaml.cs**

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Collections;
using System.IO;

namespace Sort_a_list
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public class Student
        {
            public string name
            {
                get;
                set;
            }
            public int age
            {
                get;
                set;
            }
            public string gender
            {
                get;
                set;
            }
            public string major
            {
                get;
                set;
            }
            public string classification
            {
                get;
                set;
            }

        }
        public MainWindow()
        {
            InitializeComponent();
            List<Student> user = new List<Student>();
            try
            {
                using (StreamReader sr = new StreamReader(@"C:\Users\justi\Documents\2021 Self Study\WPF C#\Samples.txt"))
                {
                    string line;
                    char[] sep = { ',' };
                    int length;
                    ArrayList rows = new ArrayList();

                    while ((line = sr.ReadLine()) != null)
                    {
                        string[] words = line.Split(sep);
                        length = words.Length;
                        rows.Add(words);

                    }
                    string[] columns;
                    for(int i = 1; i < rows.Count; i++)
                    {
                        columns = (string[])rows[i];
                        user.Add(new Student() { name = columns[0], age = Int16.Parse(columns[1]), gender = columns[2], major = columns[3], classification = columns[4] }); 
                    }
                }
            }
            catch(Exception e)
            {
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }

            sort.ItemsSource = user;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

        }
    }
}

Please help! Thank you very much!

XouDo
  • 945
  • 10
  • 19
  • Have a look at this question: https://stackoverflow.com/questions/2796470/wpf-create-a-dialog-prompt. I strongly advise you look into data binding and using MVVM pattern also - libraries such as MVVM Light and Prism will help. But for your immediate question you need to create another WPF form and display it as a modal dialog in your button click handler. – auburg Jun 07 '21 at 08:13
  • Show the XAML of the Editing Window. – EldHasp Jun 07 '21 at 09:34

1 Answers1

0

You won't be able to access your Window1's DataGrid from Window2 directly. You also not have access to your source collection (List) from Window2 to modify it from there.

In that facts, you got 2 ways:

  • use external database;
  • create public fixed collection of Students.

First of all and in any way I strongly recommend you to add some ID field to your Student object to be able unify each student.

If use external database, you can simply call something like "UPDATE students s SET s.name = @name, s.age = @age... WHERE s.ID = @id" from your Window2 when Save button clicked. @name, @age ... is new modified values

If use public fixed collection, you can access it from any part of your program. When Window2 Save button clicked, you can search for student in it by student's ID and simply edit him. After Window2 closing you need just refresh DataGrid view by resetting ItemsSource. It may look like this:

Student class:

public class Student
{
    // Need to be unique for each student
    public int ID { get; private set; }

    public string Name { get; set; }
    public int Age { get; set; }
    public string Gender { get; set; }
    public string Major { get; set; }
    public string Classification { get; set; }

    public Student(int id)
    {
        ID = id;
        Name = "SomeName";
        Age = -1;
        Gender = "Unknown";
        Major = "Unknown";
        Classification = "None";
    }
    
    // May also be overloaded with id, id+name, id+name+age, id+name+age+gender etc
    public Student(int id, string name, int age, string gender, string major, string classification)
    {
        ID = id;
        Name = name;
        Age = age;
        Gender = gender;
        Major = major;
        Classification = classification;
    }

    // If you need some kind of string representation of Student
    public override string ToString()
    {
        return $"{ID},{Name},{Age},{Gender},{Major},{Classification}";
    }
} 

Fixed and public collection class to store Students:

public static class DB
{
    // Your actual Database, where Students would be stored
    private static readonly List<Student> students = new List<Student>();

    // You can get it by DB.Students from anywhere
    public static List<Student> Students { get { return students; } }

    // As I see you use a file as source for item collection.
    // You can add some Save() method to rewrite you existing
    // file with items from "database":
    // public static void Save()
    // {
    //    StringBuilder sb = new StringBuilder();
    //
    //    foreach (Student student in students)
    //    {
    //        sb.AppendLine(student.ToString());
    //    }
    //
    //    File.WriteAllText("PathToYourFile", sb.ToString());
    // }
    //
    // You should also override ToString method in Student class
    // with something like (in format you wish):
    // public override string ToString()
    // {
    //     return $"{ID},{Name},{Age},{Gender},{Major},{Classification}";
    // }
    } 
} 

Update button click handler at Window1:

private void ButtonUpdate_Click(object sender, RoutedEventArgs e)
{
    // There should be selected only 1 item or use dataGrid.SelectedItems[0]
    if (dataGrid.SelectedItem is Student student)
    {
        // Pass selected Student to Window2
        // where user will edit it
        Window2 w2 = new Window2(student);

        // Subscribe to Closed window to refresh DataGrid view after Student editing
        w2.Closed += (s, a) =>
        {
            // Refresh the view of DataGrid by resetting its ItemsSource property
            dataGrid.ItemsSource = null;
            dataGrid.ItemsSource = DB.Students;
        };

        // Show update window
        w2.ShowDialog();
    }
}

And some kind of Window2:

public partial class Window2 : Window
{
    // Here would be a copy of Student object from Window1 
    // with which we would manipulate in Window2
    // while editing in its fields
    private readonly Student editableStudent = null;

    public Window2(Student student)
    {
        InitializeComponent();

        // Copy student object to store it in our Window2 
        // and be able to modify it not only from Constructor
        editableStudent = student;

        // Fill Window2 fields with existing Student values
        tbName.Text = student.Name;
        tbAge.Text = student.Age.ToString();
        tbGender.Text = student.Gender;
        tbMajor.Text = student.Major;
        tbClassification.Text = student.Classification;

        // Easy "on-the-fly" subscribe to each field TextChanged handlers to check, 
        // whether some data was changed/edited or not
        // and save it to editableStudent
        tbName.TextChanged += (s, a) => 
        {
            // Comparing value in field with existing Student's value
            if (tbName.Text != student.Name) // May also check for not empty / or for min input length
            {
                // Saving new value to a copied object of our Student
                editableStudent.Name = tbName.Text;
            } 
        };
        // Don't forget to check for integer value 
        // and convert in back to int: 
        // Convert.ToInt32(tbAge.Text)/int.Parse(tbAge.Text)/int.TryParse(tbAge.Text, out int age)
        tbAge.TextChanged += (sender, args) => { /* Do same as with Name */ };
        tbGender.TextChanged += (sender, args) => { /* Do same as with Name. */  };
        tbMajor.TextChanged += (sender, args) => { /* Do same as with Name */  };
        tbClassification.TextChanged += (sender, args) => { /* Do same as with Name */ };
    }

    private void ButtonSave_Click(object sender, RoutedEventArgs e)
    {
        // Find index of Student element in our "Database" (List)
        int index = DB.Students.FindIndex(x => x.ID == editableStudent.ID);

        if (index != -1)
        {
            // Update record in a collection if it was found
            DB.Students[index].Name = editableStudent.Name;
            DB.Students[index].Age = editableStudent.Age;
            DB.Students[index].Gender = editableStudent.Gender;
            DB.Students[index].Major = editableStudent.Major;
            DB.Students[index].Classification = editableStudent.Classification;

            // Instead of use 'editableStudent' field with copied in
            // constructor student's object, you can create it here
            // and fill with values from TextBoxes in Window2.
            // Or not create new Student object and fill new values
            // directly in "database" by index.
            // But you anyway need to store at least student's ID from
            // student's object, passed from Window1, to be able to 
            // find him in "database"
            // Student modifiedStudent = new Student(...);
            // modifiedStudent.Name = tbName.Text;
            // if (int.TryParse(tbAge.Text, out int age))
            //     modifiedStudent.Age = age;
            // ... 
            // DB.Students[index].Name = modifiedStudent.Name;
            // DB.Students[index].Age = modifiedStudent.Age;   
            // ...
            // or
            // DB.Students[index].Name = tbName.Text;
            // if (int.TryParse(tbAge.Text, out int age))
            //     DB.Students[index].Age = age;
            // ...         
        }
        else
        {
            // Or message user if not found
            MessageBox.Show("Student not found in Database (in a List of Students).");
        }

        // Close window after updating record in "Database"
        // DataGrid view we will refresh from Window1 after
        // Window2 close
        Close();
    }

    private void ButtonCancel_Click(object sender, RoutedEventArgs e)
    {
        // Do nothing with Student, just close
        Close();
    }
}
Auditive
  • 1,607
  • 1
  • 7
  • 13