C# WPF绑定到全局变量以更新UI

C# WPF绑定到全局变量以更新UI,c#,.net,wpf,mvvm,C#,.net,Wpf,Mvvm,假设我有两个窗口和一个trayicon上下文菜单。每个窗口都有一个切换按钮,上下文菜单有一个可检查的菜单项。所有三个控件都设计用于显示和切换相同值的状态 在本例中,我如何将三个控件绑定到一个全局变量,当其中一个控件被选中/取消选中时,其他控件将相应地更新?我应该只调用还是有MVVM解决方案?我是WPF新手,所以我不确定实现这一点的最佳/最正确的方法。您可以添加到codebehind bool IsChecked属性中,并将其用于所有您想要的组件。您可以将it组件的事件方法更改为true或fals

假设我有两个窗口和一个trayicon上下文菜单。每个窗口都有一个切换按钮,上下文菜单有一个可检查的菜单项。所有三个控件都设计用于显示和切换相同值的状态


在本例中,我如何将三个控件绑定到一个全局变量,当其中一个控件被选中/取消选中时,其他控件将相应地更新?我应该只调用还是有MVVM解决方案?我是WPF新手,所以我不确定实现这一点的最佳/最正确的方法。

您可以添加到codebehind bool IsChecked属性中,并将其用于所有您想要的组件。您可以将it组件的事件方法更改为true或false

您可以将bool IsChecked属性添加到codebehind中,并将其用于所需的所有组件。您可以将it组件的事件方法更改为true或false

假设您有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 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):


我演示的示例不是唯一的方法,我不会说“最好”,但它解决了以下问题:

  • 封装公共(共享)状态
  • 在不同的windows实例(或类型)之间同步状态
  • 允许CommonState独立于窗口实现进行扩展(只需更新XAML)
  • 另一种可能的解决方案是将CommonState的单例实例注册到静态公开的控制反转容器(InversionofControl container,IoC)中,并使每个具体窗口的ViewModel获得一个实例。这样可以避免注射步骤。对于小项目来说,这是一种过分的做法


    如果任何人试图运行上述代码,请记住从App.xaml中删除StartupUri=“MainWindow.xaml”。假设您有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 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):

    
    
    我演示的示例不是唯一的方法,我不会说“最好”,但它解决了以下问题:

  • 封装公共(共享)状态
  • 在不同的windows实例(或类型)之间同步状态
  • 允许CommonState独立于窗口实现进行扩展(只需更新XAML)
  • 另一种可能的解决方案是将CommonState的单例实例注册到静态公开的控制反转容器(InversionofControl container,IoC)中,并使每个具体窗口的ViewModel获得一个实例。这样可以避免注射步骤。对于小项目来说,这是一种过分的做法


    如果任何人试图运行上述代码,请记住从App.xaml中删除StartupUri=“MainWindow.xaml”

    但不完全遵循。它是如何联系在一起的,从而使一个组件的更改更新所有其他组件的?下面不太清楚。如何将所有组件链接在一起,从而使一个组件的更改更新所有其他组件?到目前为止,这在两个窗口之间非常有效。如何将CommonState注入XAML定义的ContextMenu?我正在使用Hardcodet.Wpf.TaskbarNotification TaskbarIcon库。ContextMenu是在XAML ResourceDictionary中定义的。TaskbarIcon由FindResource在App.OnStartup.Add属性期间创建,如CommonState中的ICommand CommandA{get;set;},并初始化命令。然后像这样绑定命令:我得到一个错误:找不到用于绑定的源,引用为“RelativeSource FindAncestor,AncestorType='System.Windows.Window',AncestorLevel='1'。BindingExpression:Path=CommonState.IsCheck1;DataItem=null;目标元素是“MenuItem”(Name='mnuCheck1');目标属性为“Command”(类型为“ICommand”)。这里有更多的细节:。。。公共部分类NotifyIconResources:ResourceDictionary,ICommonState{(MenuEventHandlers)}RD如何获取CommonState引用?这在两个窗口之间非常有效。如何将CommonState注入XAML定义的ContextMenu?我正在使用Hardcodet.Wpf.TaskbarNotification TaskbarIcon库。ContextMenu是在XAML ResourceDictionary中定义的。TaskbarIcon由FindResource在App.OnStartup.Add属性期间创建,如CommonState中的ICommand CommandA{get;set;},并初始化命令。然后像这样绑定命令:我得到一个错误:找不到用于绑定的源,引用为“RelativeSource FindAncestor,AncestorType='System.Windows.Window',AncestorLevel='1'。BindingExpression:Path=CommonState.IsCheck1;DataItem=null;目标元素是“MenuItem”(Name='mnuCheck1');目标属性为“命令”(ty)