C# MVVM,我必须在自己的类中保留每个命令吗?

C# MVVM,我必须在自己的类中保留每个命令吗?,c#,wpf,mvvm,viewmodel,C#,Wpf,Mvvm,Viewmodel,一个月以来,我一直在努力适应MVVM和WPF。我试图做一些基本的事情,但我经常遇到问题。我觉得我通过在线搜索解决了大部分问题。但是现在命令出现了问题 问:我看到他们正在使用RelayCommand、DelegateCommand或SimpleCommand。像这样: public ICommand DeleteCommand => new SimpleCommand(DeleteProject); 即使我像他们那样创建了所有东西,我仍然拥有新的SimpleCommand(DeleteP

一个月以来,我一直在努力适应MVVM和WPF。我试图做一些基本的事情,但我经常遇到问题。我觉得我通过在线搜索解决了大部分问题。但是现在命令出现了问题

  • 问:我看到他们正在使用RelayCommand、DelegateCommand或SimpleCommand。像这样:

    public ICommand DeleteCommand => new SimpleCommand(DeleteProject);
    
  • 即使我像他们那样创建了所有东西,我仍然拥有新的SimpleCommand(DeleteProject)部件
    =>红色下划线

    到目前为止,我正在通过为每个命令创建命令类来解决这个问题,但这并不是正确的方法

  • 问:我也会发布整个项目,我想知道我是否做错了什么,或者我应该改进什么
  • xaml:

    模型视图:

    class GalleryViewModel : INotifyPropertyChanged
    {
        public GalleryViewModel()
        {
            GalleryList = new ObservableCollection<Gallery>();
            this.ShowFolderClick = new ShowFolderDialog(this);
            this.AddClick = new AddGalleryCommand(this);
            this.DeleteClick = new DeleteGalleryCommand(this);
        }
    
        private ObservableCollection<Gallery> _galleryList;
    
        public ObservableCollection<Gallery> GalleryList
        {
            get { return _galleryList; }
            set { 
                _galleryList = value;
                OnPropertyChanged("GalleryList");
            }
        }
    
        private Gallery _selectedGallery;
    
        public Gallery SelectedGallery
        {
            get { return _selectedGallery; }
            set { 
                _selectedGallery = value;
                OnPropertyChanged("SelectedGallery");
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(params string[] propertyNames)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
    
            if (handler != null)
            {
                foreach (string propertyName in propertyNames) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                handler(this, new PropertyChangedEventArgs("HasError"));
            }
        }
    
        public AddGalleryCommand AddClick { get; set; }
        public void AddGalleryClick(Gallery gallery)
        {
    
            Gallery g = new Gallery();
            g.Name = gallery.Name;
            g.Path = gallery.Path;
            GalleryList.Add(g);
    
        }
    
        public DeleteGalleryCommand DeleteClick { get; set; }
        public void DeleteGalleryClick()
        {
            if (SelectedGallery != null)
            {
                GalleryList.Remove(SelectedGallery);
            }
        }
    
        public ShowFolderDialog ShowFolderClick { get; set; }
        public void ShowFolderDialogClick(Gallery gallery)
        {
            System.Windows.Forms.FolderBrowserDialog browser = new System.Windows.Forms.FolderBrowserDialog();
            string tempPath = "";
    
            if (browser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tempPath = browser.SelectedPath; // prints path
            }
    
            gallery.Path = tempPath;
        }
    }
    

    感谢您花时间阅读,我将非常感谢您给我的每一个建议。

    有一些框架/库可以帮助简化命令绑定。例如,MVVMLight具有RelayCommand的通用实现,它只需要创建属性并为其指定方法名即可执行


    这是如何使用Mvvmlight Relaycommand的一个示例。

    您可以通过使用单个
    DelegateCommand
    实现而不是单独的
    ICommand
    类来实现这一点

    public class DelegateCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;
    
        public event EventHandler CanExecuteChanged;
    
        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public DelegateCommand(Action<object> execute) : this(execute, null) { }
    
        public virtual bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
    
            return _canExecute(parameter);
        }
    
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    
        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    
    关于MVVM的进一步简化,实现属性更改通知功能的一种方法是通过以下方式:

    public abstract class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        internal void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    然后在ViewModel中:

    public class ViewModel : ObservableObject
    {
        private object _myProperty;
        public object MyProperty
        {
            get { return _myProperty; }
            set
            {
                if (_myProperty != value)
                {
                    _myProperty = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        private object _anotherProperty;
        public object AnotherProperty
        {
            get { return _anotherProperty; }
            set
            {
                if (_anotherProperty != value)
                {
                    _anotherProperty = value;
                    NotifyPropertyChanged();
                    NotifyPropertyChanged("MyProperty");
                }
            }
        }
    }
    
    class GalleryViewModel : ObservableObject
    {
        private ObservableCollection<Gallery> _galleryList;
    
        public ObservableCollection<Gallery> GalleryList
        {
            get { return _galleryList; }
            set
            {
                _galleryList = value;
                NotifyPropertyChanged();
            }
        }
    
        private Gallery _galleryToAdd;
        public Gallery GalleryToAdd
        {
            get { return _galleryToAdd; }
            set
            {
                if (_galleryToAdd != value)
                {
                    _galleryToAdd = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        public DelegateCommand<Gallery> AddGalleryCommand { get; set; }
    
        public GalleryViewModel()
        {
            GalleryList = new ObservableCollection<Gallery>();
            AddGalleryCommand = new DelegateCommand<Gallery>(AddGallery, CanAddGallery);
            GalleryToAdd = new Gallery();
            GalleryToAdd.PropertyChanged += GalleryToAdd_PropertyChanged;
        }
    
        private void AddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            Gallery g = new Gallery();
            g.Name = gallery.Name;
            g.Path = gallery.Path;
            GalleryList.Add(g);
        }
    
        private bool CanAddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            if (string.IsNullOrEmpty(gallery.Name) || string.IsNullOrEmpty(gallery.Path))
            {
                return false;
            }
    
            return true;
        }
    
        private void GalleryToAdd_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name" || e.PropertyName == "Path")
            {
                AddGalleryCommand.RaiseCanExecuteChanged();
            }
        }
    }
    
    请注意,当从属性的setter中引发
    NotifyPropertyChanged
    时,您不需要提供属性的名称(感谢
    [CallerMemberName]
    ),尽管这样做仍然是一个选项,例如,
    另一个属性
    引发两个属性的更改通知

    澄清

    DelegateCommand
    适用于所有示例。传递给它的方法应具有以下签名:

    void MethodName(object parameter)
    
    这与
    ICommand
    Execute
    方法的签名相匹配。参数类型为
    object
    ,因此它接受任何内容,在方法中,您可以将其转换为实际传递给它的任何对象,例如:

    private void AddGallery(object parameter)
    {
        Gallery gallery = (Gallery)parameter;
    
        ...
    }
    
    如果设置了no
    CommandParameter
    ,则将传递
    null
    ,因此对于其他示例,您仍然可以使用相同的签名,只是不使用参数:

    private void DeleteGallery(object parameter)
    {
        ...
    }
    
    public class DelegateCommand<T> : ICommand
    {
        private readonly Predicate<T> _canExecute;
        private readonly Action<T> _execute;
    
        public event EventHandler CanExecuteChanged;
    
        public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public DelegateCommand(Action<T> execute) : this(execute, null) { }
    
        public virtual bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
    
            return _canExecute((T)parameter);
        }
    
        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }
    
        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    
    因此,您可以对上述所有操作使用
    DelegateCommand

    CanadGallery的实施

    下面应该为如何实现这一点提供一个很好的模型(我发明了两个属性,
    Property1
    Property2
    ,来表示您的
    TextBox
    值):

    关于下列执行情况的说明:

    我发现当我使用此方法时,
    DelegateCommand
    上的
    CanExecuteChanged
    EventHandler
    总是
    null
    ,因此事件从不触发。如果
    CanExecute
    开始时为
    false
    ,则按钮将始终被禁用-如果开始时为
    true
    ,则我仍然可以在命令执行与否方面获得准确的功能,但按钮将始终被启用。因此,我更喜欢上面示例中的方法,即:

    public DelegateCommand AddGalleryCommand { get; set; }
    
    public AddGalleryViewModel()
    {
        AddGalleryCommand = new DelegateCommand(AddGallery, CanAddGallery)
    
        ...
    }
    
    授权指挥专业化

    以下类允许您为命令参数指定类型:

    private void DeleteGallery(object parameter)
    {
        ...
    }
    
    public class DelegateCommand<T> : ICommand
    {
        private readonly Predicate<T> _canExecute;
        private readonly Action<T> _execute;
    
        public event EventHandler CanExecuteChanged;
    
        public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public DelegateCommand(Action<T> execute) : this(execute, null) { }
    
        public virtual bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
    
            return _canExecute((T)parameter);
        }
    
        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }
    
        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    
    用法:

    public class ViewModel
    {
        public ICommand DeleteProjectCommand => new DelegateCommand(DeleteProject);
    
        private void DeleteProject(object parameter)
        {
        }
    }
    
    public DelegateCommand<Gallery> AddGalleryCommand { get; set; }
    
    public AddGalleryViewModel()
    {
        AddGalleryCommand = new DelegateCommand<Gallery>(AddGallery, CanAddGallery)
    }
    
    private void AddGallery(Gallery gallery)
    {
        ...
    }
    
    private bool CanAddGallery(Gallery gallery)
    {
        ...
    }
    
    public InternalDelegateCommand CreateGalleryCommand { get; set; }
    
    public CreateGalleryViewModel()
    {
        CreateGalleryCommand = new InternalDelegateCommand(CreateGallery)
    }
    
    private void CreateGallery()
    {
        Gallery gallery = new Gallery();
    
        ...
    }
    

    好的,我已经尽量简化了。 我正在使用您的
    observeObject
    DelegateCommand

    问题是运行后立即出现在
    CanadGallery
    中的
    NullReferenceException
    。没有窗口弹出。我试图通过添加
    if(parameter==null)returnfalse
    来解决这个问题。这只会禁用按钮。我在想是否有必要禁用按钮。如果从用户的角度来看,最好不要禁用按钮,而是在文本框下显示红色文本“此项必须填写”(或弹出消息),当没有通过按钮单击发送参数时,会显示红色文本

    xaml:

    视图模型:

    public class ViewModel : ObservableObject
    {
        private object _myProperty;
        public object MyProperty
        {
            get { return _myProperty; }
            set
            {
                if (_myProperty != value)
                {
                    _myProperty = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        private object _anotherProperty;
        public object AnotherProperty
        {
            get { return _anotherProperty; }
            set
            {
                if (_anotherProperty != value)
                {
                    _anotherProperty = value;
                    NotifyPropertyChanged();
                    NotifyPropertyChanged("MyProperty");
                }
            }
        }
    }
    
    class GalleryViewModel : ObservableObject
    {
        private ObservableCollection<Gallery> _galleryList;
    
        public ObservableCollection<Gallery> GalleryList
        {
            get { return _galleryList; }
            set
            {
                _galleryList = value;
                NotifyPropertyChanged();
            }
        }
    
        private Gallery _galleryToAdd;
        public Gallery GalleryToAdd
        {
            get { return _galleryToAdd; }
            set
            {
                if (_galleryToAdd != value)
                {
                    _galleryToAdd = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        public DelegateCommand<Gallery> AddGalleryCommand { get; set; }
    
        public GalleryViewModel()
        {
            GalleryList = new ObservableCollection<Gallery>();
            AddGalleryCommand = new DelegateCommand<Gallery>(AddGallery, CanAddGallery);
            GalleryToAdd = new Gallery();
            GalleryToAdd.PropertyChanged += GalleryToAdd_PropertyChanged;
        }
    
        private void AddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            Gallery g = new Gallery();
            g.Name = gallery.Name;
            g.Path = gallery.Path;
            GalleryList.Add(g);
        }
    
        private bool CanAddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            if (string.IsNullOrEmpty(gallery.Name) || string.IsNullOrEmpty(gallery.Path))
            {
                return false;
            }
    
            return true;
        }
    
        private void GalleryToAdd_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name" || e.PropertyName == "Path")
            {
                AddGalleryCommand.RaiseCanExecuteChanged();
            }
        }
    }
    
    class GalleryViewModel:ObserveObject
    {
    私人可观察收集(galleryList);;
    公众可观测收集帆船列表
    {
    获取{return\u galleryList;}
    设置
    {
    _galleryList=值;
    NotifyPropertyChanged();
    }
    }
    私人画廊;
    公共画廊画廊
    {
    获取{return\u galleryToAdd;}
    设置
    {
    如果(_galleryToAdd!=值)
    {
    _galleryToAdd=值;
    NotifyPropertyChanged();
    }
    }
    }
    公共DelegateCommand AddGalleryCommand{get;set;}
    公共厨房视图模型()
    {
    GalleryList=新的ObservableCollection();
    AddGalleryCommand=新的DelegateCommand(AddGallery,CanAddGallery);
    GalleryToAdd=新画廊();
    GalleryToAdd.PropertyChanged+=GalleryToAdd_PropertyChanged;
    }
    私有void AddGallery(对象参数)
    {
    画廊=(画廊)参数;
    画廊g=新画廊();
    g、 Name=gallery.Name;
    g、 Path=gallery.Path;
    加上(g);
    }
    私有布尔CanadGallery(对象参数)
    {
    画廊=(画廊)参数;
    if(string.IsNullOrEmpty(gallery.Name)| string.IsNullOrEmpty(gallery.Path))
    {
    返回false;
    }
    返回true;
    }
    私有void GalleryToAdd_PropertyChanged(对象发送方,PropertyChangedEventArgs e)
    {
    if(e.PropertyName==“Name”| | e.PropertyName==“Path”)
    {
    AddGalleryCommand.RaiseCanExecuteChanged();
    }
    }
    }
    
    如果您有明确的语法错误:
    public ICommand DeleteCommand=>new SimpleCommand(DeleteProject),尝试<代码
    
    public InternalDelegateCommand CreateGalleryCommand { get; set; }
    
    public CreateGalleryViewModel()
    {
        CreateGalleryCommand = new InternalDelegateCommand(CreateGallery)
    }
    
    private void CreateGallery()
    {
        Gallery gallery = new Gallery();
    
        ...
    }
    
    <StackPanel DataContext="{Binding Source={StaticResource gallery}}" Margin="10">
        <ListView DataContext="{Binding Source={StaticResource viewModel}}" 
                  SelectedItem="{Binding SelectedGallery}"
                  ItemsSource="{Binding GalleryList}"
                  Height="150">
    
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Name}"/>
                    <GridViewColumn Header="Path" Width="100" DisplayMemberBinding="{Binding Path}"/>
                </GridView>
            </ListView.View>
        </ListView>
    
        <TextBlock Text="Name" Margin="0, 10, 0, 5"/>
        <TextBox Text="{Binding Name}" />
        <TextBlock Text="Path" Margin="0, 10, 0, 5" />
        <TextBox Text="{Binding Path}" Grid.Column="0"/>
        <Button DataContext="{Binding Source={StaticResource viewModel}}"
                Command="{Binding Path=AddGalleryCommand}" 
                CommandParameter="{Binding Path=GalleryToAdd}" Content="Add"/>
    </StackPanel>
    
    class Gallery : ObservableObject
    {
        private string _name;
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                NotifyPropertyChanged();
            }
        }
    
    
        private string _path;
    
        public string Path
        {
            get
            {
                return _path;
            }
            set
            {
                _path = value;
                NotifyPropertyChanged();
            }
        }
    }
    
    class GalleryViewModel : ObservableObject
    {
        private ObservableCollection<Gallery> _galleryList;
    
        public ObservableCollection<Gallery> GalleryList
        {
            get { return _galleryList; }
            set
            {
                _galleryList = value;
                NotifyPropertyChanged();
            }
        }
    
        private Gallery _galleryToAdd;
        public Gallery GalleryToAdd
        {
            get { return _galleryToAdd; }
            set
            {
                if (_galleryToAdd != value)
                {
                    _galleryToAdd = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        public DelegateCommand<Gallery> AddGalleryCommand { get; set; }
    
        public GalleryViewModel()
        {
            GalleryList = new ObservableCollection<Gallery>();
            AddGalleryCommand = new DelegateCommand<Gallery>(AddGallery, CanAddGallery);
            GalleryToAdd = new Gallery();
            GalleryToAdd.PropertyChanged += GalleryToAdd_PropertyChanged;
        }
    
        private void AddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            Gallery g = new Gallery();
            g.Name = gallery.Name;
            g.Path = gallery.Path;
            GalleryList.Add(g);
        }
    
        private bool CanAddGallery(object parameter)
        {
            Gallery gallery = (Gallery)parameter;
    
            if (string.IsNullOrEmpty(gallery.Name) || string.IsNullOrEmpty(gallery.Path))
            {
                return false;
            }
    
            return true;
        }
    
        private void GalleryToAdd_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name" || e.PropertyName == "Path")
            {
                AddGalleryCommand.RaiseCanExecuteChanged();
            }
        }
    }