winforms C# Windows Forms和MVVN -如何正确?

9q78igpj  于 2023-04-12  发布在  C#
关注(0)|答案(1)|浏览(139)

下午好。我正在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中的属性从异步方法更改,如何做正确的事情。提前感谢!
我看了很多例子,但我不明白

vatpfxk5

vatpfxk51#

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

//----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
                }
            }
        }

相关问题