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