MVVM обращается к моделям через ViewModels и ChildViews - PullRequest
0 голосов
/ 04 февраля 2020

Я работал над некоторыми примерами и применял то, что узнал, чтобы создать приложение для импорта WPF. То, что у меня сейчас есть, работает должным образом, но я стараюсь максимально следовать шаблону MVVM. Я открываю другое окно нажатием кнопки и привязываю значение, выбранное в ListBox, к экземпляру модели, которая будет использоваться в следующем окне. Пока я работал с примерами, я задал вопрос о том, что ViewModel должна быть моделью, когда я пытался реализовать это, я не мог заставить его работать, какие-либо предположения?

Это мой HomeView, это представление содержит ListBox и кнопка. Пользователь выберет значение из ListBox, и кнопка перейдет в новое окно и будет использовать выбранное значение, чтобы применить информацию к новому окну

<Window x:Class="DICOM_Importer.Views.HomeView"
        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" ItemsSource="{Binding Studies}" SelectedItem="{Binding SelectedStudy}">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="Study">
                    <TextBlock Text="{Binding StudyTitle}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

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

    </Grid>
</Window>

Вот HomeViewModel, который содержит ссылку на childView , StudyImporterView и StudyImporterViewModel. Когда происходит навигация, значения применяются к StudyImporterViewModel для привязки к элементам в StudyImporterView

using DICOM_Importer.Commands;
using DICOM_Importer.Models;
using DICOM_Importer.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Xml.Linq;

namespace DICOM_Importer.ViewModels
{
    public 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

        public ObservableCollection<Studies> Studies { get; set; }
        private StudyImporterViewModel importViewModel;

        private Studies selectedStudy;

        public HomeViewModel()
        {
            NavigateCommand = new SelectStudyToImport(this);
            importViewModel = new StudyImporterViewModel();

            string path_to_debug_folder = Directory.GetCurrentDirectory();
            string path = Path.GetFullPath(Path.Combine(path_to_debug_folder, @"..\..\")) + @"Data\Studies.xml";

            var xmlRootElement = XElement.Load(path);
            List<Studies> studies = xmlRootElement.Descendants("Study")
                .Select(CreateModelFromStudyXmlNode)
                .ToList();
            this.Studies = new ObservableCollection<Studies>(studies);


        }


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

            return newStudy;
        }

        public Studies SelectedStudy
        {
            get { return selectedStudy; }
            set { selectedStudy = value; }
        }

        public ICommand NavigateCommand
        {
            get;
            set;
        }


        public void Navigate()
        {
            StudyImporterView view = new StudyImporterView();
            view.DataContext = importViewModel;
            importViewModel.ImporterTitle = SelectedStudy.StudyTitle;
            importViewModel.ImporterPath = SelectedStudy.ImportDirectory;
            view.ShowDialog();
        }

    }
}

Вот StudyModel, который использует вышеуказанный класс

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 class for study titles and import directories for active studies
    /// </summary>
    /// 

    //When using the MVVM pattern approach all classes should inherit from the INotifyPropertyChanged interface
    //this is what allows the two-way binding functionality 
    public class Studies : BaseModel
    {
        //every property in the Model Object should have a private variable outside of the Object
        private string studyTitle;
        private string importDirectory;

        //this is a data object with a single property of studyTitle
        //it return the private variable in Get and updates that variable through the OnPropertyChanged event 
        public string StudyTitle
        {
            get { return studyTitle; }
            set
            {
                studyTitle = value;
                OnPropertyChanged("StudyTitle");
            }
        }

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



    }
}

Вот класс это вопрос StudyImporterViewModel, он выглядит как модель. Есть ли способ, которым я могу создать StudyImporterModel, а затем использовать его в StudyImporterViewModel, чтобы сохранить мой код более согласованным?

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

namespace DICOM_Importer.ViewModels
{
    public class StudyImporterViewModel : INotifyPropertyChanged
    {
        private string importerTitle;
        private string importerPath;

        /// <summary>
        /// Gets the study information from the HomeView
        /// </summary>
        public String ImporterTitle
        {
            get { return importerTitle; }
            set
            {
                importerTitle = value;
                OnPropertyChanged(ImporterTitle);
            }
        }

        public String ImporterPath
        {
            get { return importerPath; }
            set
            {
                importerPath = value;
                OnPropertyChanged(ImporterPath);
            }
        }

        #region PropertyChangedEventHandler
        public event PropertyChangedEventHandler PropertyChanged;

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

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

Вот StudyImporterView

<Window x:Class="DICOM_Importer.Views.StudyImporterView"
        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="Importer" Height="450" Width="800">
    <Grid Style="{StaticResource gridBackground}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="125" />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="280" />
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
            <Label Style="{Binding Source={StaticResource studyTitle}}" Content="Study:" />
            <Label Style="{Binding Source={StaticResource studyTitle}}" Name="StudyImportViewStudyText" Content="{Binding ImporterTitle}" />
        </StackPanel>

        <StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Orientation="Horizontal" >
            <Label Style="{Binding Source={StaticResource studyTitle}}" Content="Import Directory" />
            <Label Style="{Binding Source={StaticResource studyTitle}}" Content="{Binding ImporterPath}" />
        </StackPanel>

        <Button Grid.Column="2" Grid.Row="1" Style="{Binding Source={StaticResource buttonStyleImport}}" Content="Submit" />

        <TextBox Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="2" x:Name="TestBox" />

    </Grid>
</Window>

Я вернул свой код обратно в рабочее состояние, но раньше, когда я пытался реализовать использование модели вместо использования существующего StudyImporterViewModel I продолжал получать пустое исключение в методе Navigate в HomeViewModel при попытке применить importViewModel.ImporterTitle = SelectedStudy.StudyTitle;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...