-1

I am learning the MVVM pattern when designing a WPF application. I am going through a tutorial that is pretty good so far, but they Model data is getting populated within the constructor of my ViewModel. In the tutorial it is mentioned that in the real world that data would be provided from a database or XML file. I would really like to know how to use the XML file for populating the Model data because I hate having hard coded values within my code.

Here is my Model

namespace MVVM_Basics.Models
{
    using System;
    using System.ComponentModel;
    //need to implement the interface INotifyPropertyChange, because this is how WPF does it's data binding.
    public class Customer : INotifyPropertyChanged
    {
        /// <summary>
        /// Initializing a new instance of the Customer class
        /// </summary>
        //this is a default constructor for the Customer class
        public Customer(String customerName)
        {
            Name = customerName;
        }

        private string _Name;
        /// <summary>
        /// Gets or Sets the Customers Name
        /// </summary>
        public String Name
        {
            get{ return _Name;}
            set 
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName) 
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null) 
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Here is the ViewModel

using MVVM_Basics.Commands;
using MVVM_Basics.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MVVM_Basics.ViewModels
{
    internal class CustomerViewModel
    {
        /// <summary>
        /// Initializes a new instance of the CustomerViewModel class
        /// </summary>

        //creating the constructor for the class
        //setting a Name to a Customer instance
        //initializing the UpdateCommand with command we created in the CustomerUpdateCOmmand class, the constructor takes in a ViewModel and since we are
        //creating it here, we can reference CustomerViewModel by using the 'this' keyword. 
        public CustomerViewModel()
        {
            _Customer = new Customer("David"); //this is the hard coded value that I would like to be populated with a XML file
            UpdateCommand = new CustomerUpdateCommand(this);
        }

        /// <summary>
        /// Gets or Sets a Boolean value to indicating whether the Customer can be updated
        /// </summary>
        public bool CanUpdate 
        {
            get
            {
                if(Customer == null)
                {
                    return false;
                }
                return !String.IsNullOrWhiteSpace(Customer.Name);
            } 

        }
        //without adding logic to CanUpdate, the button that it is binding to will always be disabled because the default value for a boolean is false
        //we added logic that only returned false if there was not a name in the text box.

        /// <summary>
        /// creates an instance of a Customer
        /// </summary>
        private Customer _Customer;
        public Customer Customer
        {
            get{ return _Customer; }
        }

        /// <summary>
        /// Gets the UpdateCommand for the ViewModel
        /// </summary>
        public ICommand UpdateCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// Saves changes made to the Customer instance
        /// </summary>
        public void SaveChanges()
        {
            //because this is just a simple demo I am using the Debug property to display dialog. Normally this would save back to your actual dataset
            Debug.Assert(false, String.Format("{0} was updated.", Customer.Name));
        } 

    }
}

Here is the View

<Window x:Class="MVVM_Basics.Views.CustomerView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVM_Basics.Views"
        mc:Ignorable="d"
        Title="Customer Update" Height="350" Width="520">

    <Grid VerticalAlignment="Top" Margin="15">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>


        <Label Grid.Column="0" Content="Customer Name" />
        <TextBox Grid.Column="1" Text="{Binding Customer.Name, UpdateSourceTrigger=PropertyChanged}"  />
        <Button Grid.Column="2" Command="{Binding UpdateCommand}" Content="Update" />

    </Grid>

</Window>

any help would be much appreciated, thanks

  • Maybe `XmlDataProvider`? More info: https://stackoverflow.com/questions/1866942/how-to-bind-xmldataprovider-source-to-mvvm-property – Mr. Squirrel.Downy Jan 10 '20 at 02:43
  • Does this answer your question? [How to Deserialize XML document](https://stackoverflow.com/questions/364253/how-to-deserialize-xml-document) – Zach R Jan 10 '20 at 02:55
  • This seems like two questions in one: 1) How to pass a Model into the ViewModel, 2) How to populate a Model from XML (which is really "How to read XML", yes?). – andrew Jan 11 '20 at 02:16

1 Answers1

0

I developed a class for serializing and gave it a Deserialize method and a Serialize method

namespace MVVM_Basics.Commands
{
    //the following class is used to retrieve data that is in an XML document. The document is name Customer.xml and is located in the data folder.
    //We are using the Deserialize feature to grab info needed from the XML doc and then data bind it to parts of a View through that View's ViewModel
    public class Serializer
    {
        /// <summary>
        /// populate a class with xml data 
        /// </summary>
        /// <typeparam name="T">Object Type</typeparam>
        /// <param name="input">xml data</param>
        /// <returns>Object Type</returns>
        public T Deserialize<T>(string input) where T : class
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));

            using (StringReader sr = new StringReader(input))
            {
                return (T)ser.Deserialize(sr);
            }
        }

        /// <summary>
        /// convert object to xml string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ObjectToSerialize"></param>
        /// <returns></returns>
        public string Serialize<T>(T ObjectToSerialize)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());

            using (StringWriter textWriter = new StringWriter())
            {
                xmlSerializer.Serialize(textWriter, ObjectToSerialize);
                return textWriter.ToString();
            }
        }
    }
}

then in my ViewModel I created a customer variable and variable that contain the path to my XML. I then applied the deserialized data to the Customer creating my Customer object.

//setting up some variable that will be use through this class
        // the private read only is setting up a Customer data type that will have a customer object attached to it
        private readonly Customer customer;
        string path = Directory.GetCurrentDirectory() + @"\Customer.xml"; 
        string xmlInputData = string.Empty;

        public CustomerViewModel()
        {
            //we are defining that we are using the CustomerUpdateCommand with this ViewModel on construction of 'this' ViewModel
            UpdateCommand = new CustomerUpdateCommand(this);

            //the 'path' variable is the path to the XML file containing all Customer data, we first just check the file does exist, we then create 
            //an instance of the Serializer class and use that class on the XML file using the Deserialize method from the class. Then attached the data to an
            //instance of a Customer object that we created a 'private readonly' variable for. 
            if (File.Exists(path))
            {
                xmlInputData = File.ReadAllText(path);
            }
            Serializer ser = new Serializer();
            customer = ser.Deserialize<Customer>(xmlInputData);


        }