我有这段代码:
public ICommand ChangePageCommand
{
get
{
if (this.changePageCommand == null)
{
this.changePageCommand = new ActionCommand(
parameter => {
this.ChangeViewModel((IPageViewModel)parameter);
},
parameter => parameter is IPageViewModel);
}
return this.changePageCommand;
}
}
每当我调用此特定命令并传递IPageViewModel
实现类时,ActionCommand
的第二个参数(谓词)始终为false。我基本上把所有IPageViewModel
都放在字典里。这是我的整个ViewModel类:
using VexLibrary.Windows;
using System.Windows.Input;
using System.Collections.Generic;
using VexLibrary.DesktopClient.ViewModels;
using System.Linq;
namespace VexLibrary.DesktopClient.ViewModels
{
class ApplicationViewModel : ViewModel
{
private ICommand changePageCommand;
private IPageViewModel currentPageViewModel;
private Dictionary<string, IPageViewModel> pageViewModels;
public ApplicationViewModel()
{
this.PageViewModels.Add("Dashboard", new DashboardViewModel(this));
this.PageViewModels.Add("Statistics", new StatisticsViewModel());
this.CurrentPageViewModel = this.PageViewModels["Dashboard"];
}
public ICommand ChangePageCommand
{
get
{
if (this.changePageCommand == null)
{
this.changePageCommand = new ActionCommand(
parameter => {
this.ChangeViewModel((IPageViewModel)parameter);
},
parameter => parameter is IPageViewModel);
}
return this.changePageCommand;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return this.currentPageViewModel;
}
set
{
if (this.currentPageViewModel != value)
{
this.currentPageViewModel = value;
NotifyPropertyChanged();
}
}
}
public Dictionary<string, IPageViewModel> PageViewModels
{
get
{
if (this.pageViewModels == null)
this.pageViewModels = new Dictionary<string, IPageViewModel>();
return this.pageViewModels;
}
}
public void ChangeViewModel(IPageViewModel viewModel)
{
this.CurrentPageViewModel = this.pageViewModels.FirstOrDefault(element => element.Value == viewModel).Value;
}
}
}
出于某种原因,虽然参数实现了IPageViewModel
,但它总是会导致错误。这是我的ActionCommand
课程:
using System;
using System.Windows.Input;
namespace VexLibrary.Windows
{
public sealed class ActionCommand : ICommand
{
private readonly Action<Object> action;
private readonly Predicate<Object> predicate;
public event EventHandler CanExecuteChanged;
/// <summary>
/// Initializes a new instance of the <see cref="ActionCommand"/> class.
/// </summary>
/// <param name="action">The <see cref="Action"/> delegate to wrap.</param>
public ActionCommand(Action<Object> action) : this(action, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ActionCommand"/> class.
/// </summary>
/// <param name="action">The <see cref="Action"/> delegate to wrap.</param>
/// <param name="predicate">The <see cref="Predicate{Object}"/> that determines whether the action delegate may be invoked.</param>
public ActionCommand(Action<Object> action, Predicate<Object> predicate)
{
if (action == null)
{
throw new ArgumentNullException("action", "You must specify an Action<T>.");
}
this.action = action;
this.predicate = predicate;
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public bool CanExecute(object parameter)
{
if (predicate == null)
{
return true;
}
return predicate(parameter);
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter)
{
action(parameter);
}
/// <summary>
/// Executes the action delegate without any parameters.
/// </summary>
public void Execute()
{
Execute(null);
}
}
}
这是一个用例:
<Button Command="{Binding ChangePageCommand}" CommandParameter="{Binding PageViewModels[Dashboard]}">
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
</Button>
这里的事情是,CanExecute
总是计算为false,因此,在这里禁用Button click。但是,我尝试将谓词替换为true
,这使得程序完全按预期工作。这是我在这里传递给命令的DashboardViewModel:
using VexLibrary.Windows;
using System.Windows.Input;
namespace VexLibrary.DesktopClient.ViewModels
{
class DashboardViewModel : ViewModel, IPageViewModel
{
private string name = "Dashboard";
private ApplicationViewModel parentViewModel;
private ICommand statisticsPageCommand;
public DashboardViewModel(ApplicationViewModel parentViewModel)
{
this.parentViewModel = parentViewModel;
}
public ICommand StatisticsPageCommand
{
get
{
return new ActionCommand(
parameter => this.parentViewModel.ChangeViewModel(this.parentViewModel.PageViewModels["Statistics"])
);
}
}
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
NotifyPropertyChanged();
}
}
}
}
答案 0 :(得分:2)
由于parameter
与CanExecute
和Execute
相同,因此您的代码段的唯一方式
this.changePageCommand = new ActionCommand(
parameter => { this.ChangeViewModel((IPageViewModel)parameter); },
parameter => parameter is IPageViewModel);
不在第一个赋值中抛出异常(显式转换)并在第二个赋值中产生null,以传递null
作为参数。所以检查null并适当地做出反应。
希望这有帮助。
答案 1 :(得分:1)
改变按钮的xaml,以便在命令之前评估CommandParameter。
<Button CommandParameter="{Binding PageViewModels[Dashboard]}" Command="{Binding ChangePageCommand}">
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
</Button>
同样,当您更改CommandParameter的绑定时,您需要手动强制执行命令的CanExecuteChanged评估。
然而,最好只使用名称作为参数。
<Button Command="{Binding ChangePageCommand}" CommandParameter="Dashboard">
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
</Button>
并在viewmodel中进行以下更改。
public ICommand ChangePageCommand
{
get
{
return new Command(
parameter => { this.ChangeViewModel(parameter as string); },
parameter =>
{
var str = parameter as string;
return !string.IsNullOrEmpty(str) && this.PageViewModels.ContainsKey(str);
});
}
}
public void ChangeViewModel(string viewName)
{
this.CurrentPageViewModel = this.pageViewModels.FirstOrDefault(element => element.Key == viewName).Value;
}