多窗口WPF应用程序中的命令绑定

3

我的应用程序可以有多个设计师窗口。每个窗口由几个用户控件组成,这些控件通过RelayCommands动态通信。我创建了以下类作为命令基础设施的支撑。

public static class Commands
{
    public static readonly RoutedCommand EntityEditRequest = new RoutedCommand();
    public static  RelayCommand EntityEditorChangeRequest;
    public static RelayCommand XMLUpdateRequest;
    public static RelayCommand SaveRequest;   
}

每个用户控件的视图模型在构造函数中都会执行以下操作

 public XMLEditorViewModel()
 {
        Commands.Commands.SaveRequest = new RelayCommand(Save_Executed);
        Commands.Commands.XMLUpdateRequest = new RelayCommand(UpdateXML); 
 }

然而,我完全忽略了应用程序可以有多个窗口。每次打开一个新窗口时,静态命令都会为该特定窗口设置。例如:打开窗口A,用户控件的构造函数设置RelayCommands,一切正常。打开窗口B,用户控件的构造函数设置RelayCommands,窗口A的命令绑定丢失!因此,当我切换到窗口A的选项卡时(这些窗口是选项卡式的),没有任何命令起作用。我需要一些想法,以便在切换选项卡时,活动窗口总是设置命令。我可以尝试将命令放入tab_selection_changed事件中,但这样做对我来说似乎不太好。是否有适当的方法来解决这个问题?非常感谢任何帮助。编辑:读者们对这个问题有点困惑。我不是要为一个命令创建多个订阅者。在任何给定时间只有一个窗口处于活动状态。该窗口包含几个用户控件-其中一些使用命令动态加载;但每个命令都由单个视图模型类处理-因此没有多个订阅者。我的问题是应用程序可以在选项卡中加载多个窗口-任何给定时间只有一个窗口处于活动状态-但用户可以切换到不同的选项卡并使另一个窗口处于活动状态。由于视图模型构造函数分配了静态RelayCommands,因此每次加载新窗口时,静态命令都会设置为新绑定。例如:打开窗口A,窗口A视图模型构造函数将静态命令绑定到其对象命令处理程序。窗口A处于活动状态。命令正常工作。加载窗口B,窗口B视图模型构造函数将静态命令绑定到其对象命令处理程序。窗口B处于活动状态。命令正常工作。现在,用户选择窗口A选项卡以将窗口A设置为活动状态。命令无法工作。当然不行,因为命令绑定到窗口B的命令处理程序。从理论上讲,静态命令可以处理该场景,因为任何给定时间只有一个活动窗口。但是怎么做呢?

命令的整个意义在于更好地将逻辑与设计分离,使用事件来使命令运作是错误的方法。 - Danny Varod
@Danny-maybe,但那完全不同,我有一个合法的理由这样做。问题在于如何使全局命令绑定到活动窗口,当有许多选项卡式窗口时。 - Jimmy
4个回答

2

全局命令应该使用 CompositeCommand 或类似的方法(CompositeCommand 来自 Prism)。这将允许多个子元素注册到命令中。

  public static CompositeCommand SaveCommand = new CompositeCommand();

命令可以在ViewModels中或适用的地方通过以下方式访问...
  SaveCommand = new DelegateCommand<object>(Save, CanExecuteSave);
  GlobalCommands.SaveCommand.RegisterCommand(SaveCommand);

您可以利用 IActiveAware 接口 来定义哪个 Window 是活动的 Window 并相应地执行命令。
还有一个关于创建全局可用命令的MSDN文章。别忘了取消注册命令以避免内存泄漏。

@Aaron-多个子元素绑定不是问题。请阅读我在帖子中的编辑。 - Jimmy
@Jimmy,这是为了展示如何设置全局命令;利用IActiveAware接口和组合方法来进行命令。你当前的设计本质上非常耦合;你试图将命令绑定到活动实例上。使用IActiveAware策略,因此命令将根据焦点所在的窗口被激活/停用。 - Aaron McIver
@Aaron-interesting。我需要在周一尝试这个,然后再回复你。 - Jimmy
好的,我看了一下IActiveaAware,我明白你的意思了。在我的情况下并不容易,因为我不能在应用程序中使用任何框架(如prism)-所以我必须在没有RegionManager的情况下完成它。我会找到一些方法,在窗口处于活动状态时激活IActiveAware对象。但是,是的 - 你给了我答案。 - Jimmy

1

为什么你决定将它放入静态类中呢?

class XMLEditorViewModel
{
    public ICommand SaveRequest { get; private set; }

    public XMLEditorViewModel()
    {
        SaveRequest = new RelayCommand(Save_Executed)?
    }
}

像 Save 这样的命令通常是静态的,因为您不希望在每个 ViewModel 中都创建一个新实例和定义 Save。命令的静态方法提供了一种全局的应用程序方法;这将允许您在相关位置注册...而不是在每次使用 Save 时都创建一个新的。 - Aaron McIver
@snowbear-因为窗口中有许多用户控件,并且该命令可以由任何用户控件触发。并不是说该命令将由XMLEditorView触发。 - Jimmy

1

不特定于视图的命令可以在静态类上定义。

特定于视图的命令应该在视图模型上定义, 作为数据上下文传递给视图,以便为具有不同视图模型的不同视图实现分离的实现, 或者至少让视图传递一个CommandParameter, 可以用来标识它们(例如对视图的引用)或它们的数据上下文。

如果命令是静态的,请仅注册一次, 可能在由视图模型使用的单例上。


@Danny,请阅读我的编辑过的帖子。我的问题是在多窗口场景中,如何根据活动窗口的更改重新绑定静态命令。 - Jimmy
我的观点是,如果您稍微更改设计,就不必重新绑定它们。 - Danny Varod

0
创建一个全局可用的命令,创建委托命令或复合命令的实例,并通过静态类公开它。 public static class GlobalCommands { public static CompositeCommand MyCompositeCommand = new CompositeCommand(); }
在您的模块中,将子命令关联到全局可用的命令。
GlobalCommands.MyCompositeCommand.RegisterCommand(command1); GlobalCommands.MyCompositeCommand.RegisterCommand(command2);
为了增加代码的可测试性,您可以使用代理类访问全局可用的命令,并在测试中模拟该代理类。
以下代码示例显示如何在WPF中将按钮绑定到命令。
执行我的复合命令。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接