MVVM добавляет консоль в окно - PullRequest
0 голосов
/ 07 февраля 2020

Я пишу приложение, которое подключается к серверу через SSH и запускает скрипт для обработки файлов. Бэкенд на сервере работает нормально, но мне бы хотелось, чтобы в моем приложении WPF было окно консоли, чтобы я мог следить за процессом. Я делаю все возможное, чтобы следовать шаблону MVVM. Я достиг этого раньше в WPF приложении, но я использовал код, чтобы иметь возможность получить дескриптор элемента TextBox, который был в представлении. Я не могу сделать это таким образом, следуя MVVM, так как вы не должны иметь ничего в своем коде позади.

Вот мой StudyImporterView.xaml

<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" Command="{Binding ImportCommand}" Style="{Binding Source={StaticResource buttonStyleImport}}" Content="Submit" />

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

    </Grid>
</Window>

Вот StudyImporterViewModel.cs

using DICOM_Importer.Commands;
using DICOM_Importer.Views;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace DICOM_Importer.ViewModels
{
    public class StudyImporterViewModel : INotifyPropertyChanged, IDataErrorInfo
    {
        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");
            }
        }

        ConsoleOutputStream outputter;

        public StudyImporterViewModel()
        {
            ImportCommand = new ActivateImport(this);
            outputter = new ConsoleOutputStream(ImportConsole);
        }

        public ICommand ImportCommand
        {
            get;
            private set;
        }

        public void Import()
        {
            Console.WriteLine("Testing Console");
        }

        #region Error Model
        public string Error
        {
            get;
            set;
        }
        #endregion

        #region Error Definition
        public string this[string columnName]
        {
            get 
            {
                if (columnName == "ImporterTitle")
                {
                    if (String.IsNullOrWhiteSpace(ImporterPath))
                    {
                        Error = "There is no selected study to import";
                    }
                    else 
                    {
                        Error = null;
                    }
                }
                return Error;
            }
        }
        #endregion

        #region PropertyChangedEventHandler
        public event PropertyChangedEventHandler PropertyChanged;

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

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


    }
}

это мой класс, который создает поток вывода для консоли ConsoleOutputStream.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace DICOM_Importer.Commands
{
    class ConsoleOutputStream : TextWriter
    {
        TextBox textBox = null;


        public ConsoleOutputStream(TextBox ouput)
        {
            textBox = ouput;
        }

        public override void Write(char value)
        {
            base.Write(value);
            textBox.Dispatcher.BeginInvoke(new Action(() => {
                textBox.AppendText(value.ToString());
            }));
        }

        public override Encoding Encoding
        {
            get { return System.Text.Encoding.UTF8; }
        }
    }
}

Я полагаю, если нет хорошего способа сделать это и следовать MVVM, я просто сломаю MVVM и использую код позади.

1 Ответ

0 голосов
/ 07 февраля 2020

Практика MVVM предполагает, что ничто вне вашего View не должно иметь ссылки на части вашего View - т.е. вы не должны писать в ваш TextBox где-либо, кроме как внутри вашего View. Ничто за пределами View не должно знать напрямую о TextBox (или любом другом элементе управления).

В настоящее время в вашей ViewModel есть ссылка на TextBox (ImportConsole), и я не уверен, как это скомпилируется .

Что вы хотите сделать, так это чтобы ConsoleOutputStream и ViewModel имели дело только со строками (данными), а не с элементами управления TextBox.

Вы можете связать данные строкой, представляющей все содержимое TextBox. но это, вероятно, не будет работать хорошо (хотя в этом нет ничего плохого - производительность может и не быть проблемой).

В качестве альтернативы, вы можете вызвать событие из вашей ViewModel и из вашего ConsoleOutputStream, которое сигнализирует о новой строке текст, который был получен. View может прослушивать это событие (в выделенном фрагменте кода - да, это прекрасно) и добавлять текст в TextBox.

ConsoleOutputStream вызывает событие, которое перехватывается ViewModel. Аргументы события содержат новую строку для добавления. ViewModel вызывает то же событие, которое перехватывается представлением, а представление добавляет текст в TextBox.

Это означает, что вы можете модульно протестировать ваши ViewModel и ConsoleOutputStream в изоляции от вашего View, который одна из основных целей MVVM.

Еще одна альтернатива - использовать ListBox, который привязан к данным для ObservableCollection строк, которые добавляются при получении данных. Это вообще не требует никакого кода (хотя, опять же, само по себе это не должно быть строгой целью при использовании MVVM).

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