【发布时间】:2018-03-12 05:55:04
【问题描述】:
假设我有两个窗口和一个托盘图标上下文菜单。每个窗口都有一个切换按钮,上下文菜单有一个可检查的菜单项。所有三个控件都旨在显示和切换相同值的状态。
在这种情况下,如何将三个控件中的 IsChecked 绑定到一个全局变量,当其中一个控件被选中/取消选中时,其他控件将相应地更新?我应该只调用还是有 MVVM 解决方案?我是 WPF 新手,所以我不确定完成此任务的最佳/最正确方法。
【问题讨论】:
假设我有两个窗口和一个托盘图标上下文菜单。每个窗口都有一个切换按钮,上下文菜单有一个可检查的菜单项。所有三个控件都旨在显示和切换相同值的状态。
在这种情况下,如何将三个控件中的 IsChecked 绑定到一个全局变量,当其中一个控件被选中/取消选中时,其他控件将相应地更新?我应该只调用还是有 MVVM 解决方案?我是 WPF 新手,所以我不确定完成此任务的最佳/最正确方法。
【问题讨论】:
假设您有 WindowA、WindowB、...、WindowN,并假设它们都是不同的类型。
创建一个类,比如说 CommonState,它封装了所有常见的属性、命令等并实现 INotifyPropertyChanged
public class CommonState : INotifyPropertyChanged
{
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
if (value != _isChecked)
{
_isChecked = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
然后声明一个接口:
public interface ICommonStateWindow
{
CommonState { get; set; }
}
让每个窗口都实现这个接口:
public partial class WindowA : Window, ICommonState
{
public WindowA()
{
InitializeComponent();
}
// This property will be injected, do not re-assign
public CommonState CommonState { get; set; }
}
在显示之前在每个窗口中注入公共状态,例如:
public partial class App : Application
{
private CommonState _state;
protected override void OnStartup(StartupEventArgs e)
{
_state = new CommonState() {IsChecked = true};
var wndA = new WindowA() { CommonState = _state };
var wndB = new WindowB() { CommonState = _state };
wndA.Show();
wndB.Show();
}
}
请记住在某个长期存在的对象(如应用程序或主窗口)中至少保留一个对创建的 CommonState 的引用,这样它就不会在某些时候被垃圾收集。
在 XAML 中,您应该使用 RelativeSource 进行绑定,以便您创建的每种新类型的窗口都可以拥有自己独立的 ViewModel (DataContext):
<Window x:Class="Example.WindowA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowA" Height="300" Width="300">
<Grid>
<CheckBox IsChecked="{Binding CommonState.IsChecked, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
我演示的示例不是唯一的方法,我不会说“最好的”,但它解决了以下问题:
另一种可能的解决方案是将 CommonState 的单例实例注册到静态暴露的控制容器反转 (IoC) 中,并让每个具体窗口的 ViewModel 获得一个实例。这样,您将避免注射步骤。对于小型项目来说,这将是一个过大的杀伤力
我有人试图运行上面的代码,记得从 App.xaml 中删除 StartupUri="MainWindow.xaml"
【讨论】:
您可以添加到您的代码隐藏 bool IsChecked 属性并将其用于您想要的所有组件。您可以将其组件的事件方法更改为 true 或 false。
【讨论】: