I am writing an application that updates files and then will eventually import the updated files to a database. I want to display a message about which file is being updated and when the process has finished. I want the message to come from the console because eventually the importer library I am using displays helpful messages through the console and I will want to display those too. I was able to do this before in a WPF app, but all of my code was in the code behind of the view, and I want to keep the MVVM pattern and separate the code into a ViewModel. My problem is I do not know how to get a reference to my TextBox that is in my View. Once I am able to get a hold of the TextBox in my ViewModel I will be able to send the Console Writes to the TextBox.
Here is my View
<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>
Here is the ViewModel
using DICOM_Importer.Commands;
using DICOM_Importer.Views;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace DICOM_Importer.ViewModels
{
public class StudyImporterViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private string importerTitle;
private string importerPath;
public DirectoryInfo[] directories;
GetIndividualSubjectDirectories subjectDirectories = new GetIndividualSubjectDirectories();
ConsoleOutputStream outputter;
/// <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");
}
}
public StudyImporterViewModel()
{
ImportCommand = new ActivateImport(this);
outputter = new ConsoleOutputStream(ImportConsole); //Here is where the error is
Console.SetOut(outputter);
}
public ICommand ImportCommand
{
get;
private set;
}
public void Import()
{
MessageBoxResult result = MessageBox.Show("This will import every series in the Import Directory. Are you sure you want to Import?", "Import Confirmation", MessageBoxButton.OKCancel);
switch (result)
{
case MessageBoxResult.OK:
if(importerTitle == "SPIROMICS2")
{
Console.WriteLine("Importing SPIROMICS2 Scans to Mifar");
directories = subjectDirectories.GetSubjectDirectories(importerPath);
subjectDirectories.GetSeriesDirectories(directories);
Console.WriteLine("Import Complete");
}
else if(importerTitle == "BLF")
{
Console.WriteLine("BLF");
}
else if(importerTitle == "PRECISE")
{
Console.WriteLine("PRECISE");
}
break;
case MessageBoxResult.Cancel:
MessageBox.Show("CANCEL", "Nope!");
break;
}
}
#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
}
}
Here is my ConsoleOutputStream Command
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; }
}
}
}
and here is the command for the button that will kick off all the file changes and imports
using DICOM_Importer.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace DICOM_Importer.Commands
{
/// <summary>
/// Starts the import processes on a button click if there is a study available
/// </summary>
class ActivateImport : ICommand
{
private StudyImporterViewModel _studyImporterViewModel;
public ActivateImport(StudyImporterViewModel viewModel)
{
_studyImporterViewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
//this is forcing the CommandManager to check the ICommand again. If we didn't have this then the buitton wouldl only be
//disabled if the window was loaded with a blank name, not if it was loaded with a name and then was deleted by the user
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return String.IsNullOrWhiteSpace(_studyImporterViewModel.Error);
}
public void Execute(object parameter)
{
_studyImporterViewModel.Import();
}
}
}
Any help would be much appreciated!
List<string>property, maybe calledConsoleOutputs. so then our view would not have aTextBox, but maybe aListBox, whoseItemSourceis aBindingto ourConsoleOutputs. then, instead of usingConsoleOutputStreamor evenConsole, we can just add new strings toConsoleOutputs. when we add a new string toConsoleOutputs, then through the magic of databinding ourListViewwill automaticallly be updated to display it.