WPF MVVM C# паттерн и диалоговые окна с применением Command
Не могу разобраться, можно ли сделать централизованный доступ к отдельной папке с командами во ViewModel. Имею некую папку Commands, в которой хочу размещать все команды. Их подключил к разметке, они работают. Но как отображать изменения во ViewModel, после отрабатывания команды? Конкретно сейчас проблема с открытием и загрузкой файла. Вызвать-то вызву команду, но как результат поместить во ViewModel? Они не связаны
ViewModel
using loopa.ViewModels.Base;
namespace loopa.ViewModels
{
internal class MainWindowViewModel : ViewModel
{
private string _CurrentFilePath = "Не выбран";
public string CurrentFilePath
{
get => _CurrentFilePath;
set => Set(ref _CurrentFilePath, value);
}
public MainWindowViewModel()
{
// брать файл, если есть
}
}
}
Command
using System;
using System.IO;
using System.Windows.Forms;
using System.Text;
using loopa.Infrastucture.Commands.Base;
namespace loopa.Infrastucture.Commands
{
internal class OpenFileCommand : Command
{
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = ".txt|*.txt";
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
var filePath = ofd.FileName;
//Read the contents of the file into a stream
var fileStream = ofd.OpenFile();
using (StreamReader reader = new StreamReader(fileStream))
{
var fileContent = reader.ReadToEnd();
MessageBox.Show(fileContent);
}
}
}
}
}
Как путь к файлу или что-либо еще поместить во ViewModel? Или обязательно размещать все в нее, и отделить не выйдет?
Ответы (1 шт):
Я бы сделал немного по-другому.
Например вот такой класс команды:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
=> (_execute, _canExecute) = (execute, canExecute);
public bool CanExecute(object parameter)
=> _canExecute == null || _canExecute(parameter);
public void Execute(object parameter)
=> _execute(parameter);
}
И вот такой код во вьюмодели. Кстати вместо Windows.Forms.OpenFileDialog, мне кажется, лучше использовать Microsoft.Win32.OpenFileDialog (документация), зачем цепляться к огромной библиотеке Winforms ради одного диалога?
using Microsoft.Win32;
private ICommand _openFileCommand;
public ICommand OpenFileCommand => _openFileCommand ??= new RelayCommand(parameter =>
{
OpenFileDialog ofd = new OpenFileDialog() { Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*" };
if (ofd.ShowDialog() ?? false)
{
string filePath = ofd.FileName;
string fileContent = File.ReadAllText(filePath);
MessageBox.Show(fileContent);
}
});
Так как этот код теперь расположен во вьюмодели, то и все ее свойства и методы доступны.
XAML
<Button Content="Открыть файл..." Margin="5" Command="{Binding OpenFileCommand}"/>
Мне конечно не нравится работа с диалогами во вьюмодели. Можно это в отдельный класс вынести и реализовать по-своему, но для начала и такой вариант вполне прилично выглядит.
Кстати
_CurrentFilePath = "Не выбран";
Не делайте так. Лучше оставьте его null по умолчанию
Вьюмодель не должна за поведение окна отвечать.
<TextBlock Text="{Binding CurrentFilePath, TargetNullValue=Не выбран}"/>
Если CurrentFilePath будет null, то текстблок сам напишет нужный текст.