c# Windows窗体和MVVN -它是如何正确的?



下午好。我在Windows窗体上做一个应用程序。因为我正在为过渡到WPF和MVVM做心理准备,所以我决定首先在一个熟悉的环境中尝试这种模式。我要求你评估我如何处理绑定和异步方法。

public partial class MainForm : Form
{
ViewModel vm;
public MainForm()
{
InitializeComponent();
//----bindings
vm = new ViewModel();
vm.Message += (s, msg) => MessageBox.Show(msg);
tbArchieve.DataBindings.Add(new Binding("Text", vm, "ArchieveName", false, DataSourceUpdateMode.OnPropertyChanged));
tbDestinationFolder.DataBindings.Add(new Binding("Text", vm, "DestinationFolder", false, DataSourceUpdateMode.OnPropertyChanged));
btStart.DataBindings.Add(new Binding("Enabled", vm, "Ready"));
pbExtraction.DataBindings.Add(new Binding("Value", vm, "Progress"));

}

private void btBrowseFile_Click(object sender, EventArgs e)
{
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.Filter = "Архивы|*.7z;*.rar;*.zip";
if(dialog.ShowDialog() == DialogResult.OK)
{
vm.ArchieveName = dialog.FileName;
}
}
}

private void btBrowserDistrination_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
{
dialog.SelectedPath = Application.ExecutablePath;
if(dialog.ShowDialog() == DialogResult.OK)
{
vm.DestinationFolder = dialog.SelectedPath;
}
}
}

private void btStart_Click(object sender, EventArgs e)
{
vm.Start();
}
public class ViewModel : INotifyPropertyChanged
{

public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<string> Message;

private void OnPropertyChanged(params string[] props)
{
foreach (var p in props)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(p)); 
}
}

public ViewModel()
{
_destinationFolder = string.Empty;
_archieveName = string.Empty;
}

#region Поля для формы

string _archieveName;
public string ArchieveName
{
get
{ return _archieveName; }
set
{
if(_archieveName!= value)
{
_archieveName = value;
OnPropertyChanged("ArchieveName","Ready");
}
}
}

string _destinationFolder;
public string DestinationFolder
{
get
{ return _destinationFolder; }
set
{
if (_destinationFolder != value)
{
_destinationFolder = value;
OnPropertyChanged("DestinationFolder", "Ready");
}
}
}

int _progress;
public int Progress
{
get
{
return _progress;
}
set
{
if(_progress!=value)
{
_progress = value;
OnPropertyChanged("Progress");
}
}
}

public bool Ready
{
get
{
if(File.Exists(_archieveName))
{
if(IsValidPath(_destinationFolder))
{
return true;
}
}
return false;
}
}

private bool IsValidPath(string path)
{
var empty = string.IsNullOrWhiteSpace(path);
var wrongsyms = path.IndexOfAny(new char[] { '<', '>', '?', '*' }) != -1;
if (empty || wrongsyms)
{
return false;
}
return true;
}

#endregion

public async void Start()
{
Unpacker unpacker = new Unpacker(_archieveName);
unpacker.FileExtracted += (s, ea) => Progress = ea.PercentDone;
try
{
await unpacker.ExtractArchieve(_destinationFolder);
}
catch(Exception e)
{
Message.Invoke(this, e.Message);
}

Message?.Invoke(this, "Архив извлечен!");
}
}
public class Unpacker
{
SevenZipExtractor extractor;
public async Task ExtractArchieve(string dstFolder)
{
await extractor.ExtractArchiveAsync(dstFolder);
}

public Unpacker(string fileName)
{
extractor = new SevenZipExtractor(fileName);
extractor.FileExtractionFinished += Extractor_FileExtractionFinished;
}

public event EventHandler<FileInfoEventArgs> FileExtracted;

private void Extractor_FileExtractionFinished(object sender, FileInfoEventArgs e)
{
FileExtracted?.Invoke(this, e);
}
}

还有一个问题-如果窗体上的控件绑定到的ViewModel中的属性从异步方法更改,如何做正确的事情。提前感谢!

我读了很多例子,但是我不理解

有几件事引起了我的注意(但老实说,我不是这方面的专家):

//----bindings
vm = new ViewModel();
vm.Message += (s, msg) => MessageBox.Show(msg);
tbArchieve.DataBindings.Add(new Binding("Text", vm, "ArchieveName", false, DataSourceUpdateMode.OnPropertyChanged));
// ...

你通常不会手工做这个。WinForms设计器支持数据绑定,所以你只需要在设计器中把绑定放在一起,指定视图模型的类型(它应该作为属性可用,因此将字段vm更改为属性)。

INotifyPropertyChanged通常是这样实现的:

public class NotifyPropertyChangedCore : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

你可以这样定义你的属性:

string _destinationFolder;
public string DestinationFolder
{
get
{ return _destinationFolder; }
set
{
if (_destinationFolder != value)
{
_destinationFolder = value;
OnPropertyChanged(); // No need to specify name of own property
}
}
}