首页 > 解决方案 > ViewModel 中的 XML 到 LINQ 以填充模型

问题描述

我有一个要用于填充模型的 XML 文件。I have multiple studies within my XML and my goal is to populate a list with all the study names and when the study name is selected and submit button is pressed then the application will navigate to a new window but carry the rest of the XML data from选定的研究。

XML

<?xml version="1.0" encoding="utf-8" ?>
<Studies>
  <Study>
    <StudyTitle>SPIROMICS2</StudyTitle>
    <ImportDirectory>Z:\SPIROMICS\Human_Scans\Dispatch_Received\NO_BACKUP_DONE_HERE\IMPORT</ImportDirectory>
  </Study>
  <Study>
    <StudyTitle>BLF</StudyTitle>
    <ImportDirectory>Z:\BLF\Human Scans\Dispatch Received\IMPORT</ImportDirectory>
  </Study>
</Studies>

模型.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DICOM_Importer.Models
{
    /// <summary>
    /// Model for all active studies within the APPIL lab
    /// </summary>
    public class Studies : INotifyPropertyChanged, IDataErrorInfo
    {
        private string studyTitle;
        private string importDirectory;

        public string StudyTitle
        {
            get { return studyTitle; }
            set
            {
                studyTitle = value;
                OnPropertyChanged("StudyTitle");
            }
        }

        public string ImportDirectory
        {
            get { return importDirectory; }
            set
            {
                importDirectory = value;
                OnPropertyChanged("ImportDirectory");
            }
        }



        #region PropertyChangedEventHandler
        //Create a PropertyChangedEventHandler variable that you can use to handle data changing
        public event PropertyChangedEventHandler PropertyChanged;
        //create the method that will handle the updating of data if a property is changed.
        private void OnPropertyChanged(string propertyChanged)
        {
            //bring in the event handler variable that you created and assign it to this methods 'handler' variable 
            PropertyChangedEventHandler handler = PropertyChanged;

            //if the the handler is not null, which means the property has been changed, then hand in the new value to the handler to be changed
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyChanged));
            }
        }
        #endregion

        #region DataErrorInfo
        /// <summary>
        /// Getter and Setter for the Error message that we setup through the IDataErrorInfo interface
        /// </summary>
        public string Error
        {
            get;
            set;
        }

        //the column name passed in will be a property on the Studies Object that we want to validate
        //this validatation is looking at the StudyTitle property. If the StudyTitle property is 'null' or just white space then we are going to add the error message
        //"Study Title cannot be null or empty" Otherwise if StudyTitle is fine the error message will be 'null'
        public string this[string columnName]
        {
            get
            {
                if (columnName == "StudyTitle")
                {
                    if (String.IsNullOrWhiteSpace(StudyTitle))
                    {
                        Error = "Study Title cannot be null or empty";
                    }
                    else
                    {
                        Error = null;
                    }

                }
                return Error;
            }
        }
        #endregion
    }
}

视图模型.cs

using DICOM_Importer.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace DICOM_Importer.ViewModels
{
    internal class HomeViewModel
    {
        //setting up some variable that will be use through this class
        // the private read only is setting up a Studies data type that will have a studies object attached to it
        private readonly Studies studies;
        public string studyTitle;


        /// <summary>
        /// Initializes a new instance of the CustomerViewModel class
        /// </summary>
        public HomeViewModel()
        {
            string path_to_debug_folder = Directory.GetCurrentDirectory();
            string path = Path.GetFullPath(Path.Combine(path_to_debug_folder, @"..\..\")) + @"Data\Studies.xml";

            //the 'path' variable is the path to the XML file containing all Studies 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 Studies object that we created a 'private readonly' variable for. 
            if (File.Exists(path))
            {
                XElement xe = XElement.Load(path);

                var x = xe.Elements();
                foreach (var tag in x)
                {
                    studyTitle = tag.FirstNode.ToString();
                }
            }

        }

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

    }
}

视图.xaml

        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:DICOM_Importer.Views"
        mc:Ignorable="d"
        Background="Gold"
        Title="DICOM Importer" Height="385" Width="600">
    <Grid Style="{StaticResource gridBackground}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Label Grid.Column="1" Grid.Row="0" Style="{StaticResource homeTitle}">DICOM IMPORTER</Label>

        <Image x:Name="appil_logo" Grid.Column="0" Grid.Row="0" Grid.RowSpan="4" Source="/assets/appil_logo.png"/>

        <Border Grid.Column="1" Grid.Row="0" Style="{StaticResource studyListHeader}" Width="Auto">
            <Label Style="{StaticResource studyListText}">Active Studies</Label>
        </Border>
        <ListBox Name="activeStudiesListBox" Grid.Column="1" Grid.Row="2" >
            <Label Content="{Binding Studies.StudyTitle}" />
        </ListBox>

        <Button Grid.Row="3" Grid.Column="1" Style="{StaticResource buttonStyle}">Select</Button>

    </Grid>
</Window>

我知道我的 ViewModel 不正确,这主要是我的问题所在。谁能告诉我如何使用这个 XML 文件和模型在我的视图中使用 studyTitle 填充我的列表框。我希望能够向 XML 文件添加一项新研究,并将其包含在视图的列表框中。

标签: c#xmlwpflinqmvvm

解决方案


由于您的模型类的名称在Studies某种程度上令人讨厌(因为它映射到StudyXML 对象),所以我将其重命名为Study.

我还建议实施INotifyDataErrorInfo替代(示例)。它取代了旧的一个过时的IDataErrorInfo

视图模型.cs

// If proeprty values are expected to change
// and this changes are required to propagate to the view
// this class must implement INotifyPropertyChanged
public class ViewModel
{  
  private void ReadXml(string filePath)
  {
    var xmlRootElement = XElement.Load(path);
    List<Studies> studies = xmlRootElement.Decendants("Study")
      .Select(CreateModelFromStudyXmlNode)
      .ToList();
    this.Studies = new ObservableCollection<Study>(studies);
  }

  private Study CreateModelFromStudyXmlNode(XElement studyXmlNode)
  {
    var newStudy = new Study();
    newStudy.StudyTitle = studyXmlNode.Element("StudyTitle").Value;
    newStudy.ImportDirectory = studyXmlNode.Element("ImportDirectory").Value;
    return newStudy;
  }

  public ObservableCollection<Study> Studies { get; set; }
}

研究.cs

public class Study
{
  public string StudyTitle { get; set; }
  public string ImportDirectory { get; set; }
}

主窗口.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <ListBox ItemsSource="{Binding Studies}" >
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type Study}">
        <TextBlock Text="{Binding StudyTitle}" />
      </DataTemplate>
    <ListBox.ItemTemplate>
  </ListBox>
</Window>

推荐阅读