多窗口 WPF 应用程序中的命令绑定
我的应用程序可以有多个设计器窗口。每个窗口都由多个用户控件组成,这些控件在 RelayCommand 的帮助下进行动态通信。我创建了以下类作为指挥基础设施的骨干。
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 处于活动状态。指挥还是不错的
窗口 B 加载窗口 B 视图模型构造函数将静态命令绑定设置到其对象命令处理程序。窗口 B 处于活动状态。指挥还是不错的
现在,用户选择“窗口 A”选项卡将窗口 A 设置为活动窗口。指挥是行不通的。当然它不会,因为命令绑定到窗口 B 命令处理程序。
理论上,静态命令可以处理这种情况,因为在任何给定点都只有一个活动窗口。但如何?
My application can have multiple designer windows. Each window constitutes of several user controls which communicates dynamically with the help of RelayCommands. I created the following class as the backbone of the commanding infrastructure.
public static class Commands
{
public static readonly RoutedCommand EntityEditRequest = new RoutedCommand();
public static RelayCommand EntityEditorChangeRequest;
public static RelayCommand XMLUpdateRequest;
public static RelayCommand SaveRequest;
}
Each viewmodel for the user controls will do something like this in the constructor
public XMLEditorViewModel()
{
Commands.Commands.SaveRequest = new RelayCommand(Save_Executed);
Commands.Commands.XMLUpdateRequest = new RelayCommand(UpdateXML);
}
However, I completely missed the point that the application can have multiple windows. When each window is opened the static Commands are set for that particular window.
Example:
Window A is opened-the constructors for the usercontrols set the RelayCommands and all is well.
Window B opened-the constructors for the usercontrols set the RelayCommands. Window A's command binding is lost!
So when I change the tab to Window A(the windows are tabbed) no commands are working.
I need some idea so that when I change the tab the active window always sets the commands. I can try to put the commanding in tab_selection_changed event, but somehow it is looking bad to me. Is there a proper way to do this? Any help is much appreciated.
Edit:
The question proved a bit confusing among the readers. I am not trying to make multiple subscribers for a command. At any given point only one window is active. This window consists several user controls-some of them loaded dynamically with the help of commands; but each command is handled by a single view model class-so no multi subscribers. My problem is the application can load multiple windows in tabs-only one window is active at any given point-but the user can do to a different tab and make another window active. As the view model constructor assigns the static RelayCommands, when each new window is loaded the static command is set to a new binding.
Window A opened-window A view model constructor sets the static command bind to its object command handler. Window A is active. Commanding is fine.
Window B loaded-window B view model constructor sets the static command bind to its object command handler. Window B is active. Commanding is fine.
Now, User select the Window A tab to set the Window A as active. Commanding wont work. Of course it wont as the Command is bind to Window B command handler.
Theoretically static commands can handle the scenario as at any given point there will be only one active window. But how??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
全局命令应该是 CompositeCommand 或类似方法 (
CompositeCommand
来自 Prism)。这将允许多个孩子使用该命令进行注册。然后可以跨 ViewModel 或在适用的情况下访问该命令,如下所示...
然后您可以利用
IActiveAware
接口 定义哪个Window
是活动Window
并相应地对命令执行操作。还有一篇关于创建全局可用命令的 MSDN 帖子。不要忘记取消注册该命令以避免内存泄漏。
The global command should be a CompositeCommand or similar approach (
CompositeCommand
is from Prism). This will allow multiple children to register with the command.The command can then be accessed across the ViewModels or where applicable like so...
You can then leverage the
IActiveAware
interface to define whichWindow
is the activeWindow
and act on the command accordingly.There is also an MSDN posting on creating globally available commands. Don't forget to unregister the command to avoid a memory leak.
您决定将其放入静态类的原因是什么?
Any reason why have you decided to put it into static class?
可以在静态类上定义不特定于视图的命令。
特定于视图的命令应该在视图模型上定义,
作为 DataContext 传递给视图,从而实现单独的实现
对于具有不同视图模型的不同视图,
或者至少让视图传递一个 CommandParameter
可用于识别它们(例如对视图的引用)或它们的 DataContext。
如果命令是静态的,则仅注册一次,
也许在视图模型使用的单例上。
Commands that are not view specific can be defined on static classes.
Commands that are view specific should either be defined on view-model,
passed as DataContext to view, enabling separate implementation
for different views with different view models,
or at least have the views pass a CommandParameter that
can be used to either identify them (e.g. reference to the view) or their DataContext.
If the commands are static, register them once only,
perhaps on a singleton used by view models.
创建全局可用的命令,创建 DelegateCommand 或 CompositeCommand 的实例并通过静态类公开它。
公共静态类全局命令
{
公共静态 CompositeCommand MyCompositeCommand = new CompositeCommand();
}
在您的模块中,将子命令关联到全局可用的命令。
GlobalCommands.MyCompositeCommand.RegisterCommand(command1);
GlobalCommands.MyCompositeCommand.RegisterCommand(command2);
为了提高代码的可测试性,您可以使用代理类来访问全局可用的命令并在测试中模拟该代理类。
以下代码示例演示如何将按钮绑定到 WPF 中的命令。
执行我的复合命令
create a globally available command, create an instance of the DelegateCommand or the CompositeCommand and expose it through a static class.
public static class GlobalCommands
{
public static CompositeCommand MyCompositeCommand = new CompositeCommand();
}
In your module, associate child commands to the globally available command.
GlobalCommands.MyCompositeCommand.RegisterCommand(command1);
GlobalCommands.MyCompositeCommand.RegisterCommand(command2);
To increase the testability of your code, you can use a proxy class to access the globally available commands and mock that proxy class in your tests.
The following code example shows how to bind a button to the command in WPF.
Execute My Composite Command