【问题标题】:How to bring window to front with wpf and using mvvm如何使用 wpf 和使用 mvvm 将窗口置于前面
【发布时间】:2012-08-13 20:00:31
【问题描述】:

我有一个基本上运行计时器的窗口。当计时器达到 0 时,我想将窗口放在前面,以便它可见,而不是隐藏在其他应用程序后面。

据我所知,我只需调用 window.activate() 即可完成此操作,但使用 mvvm 我的视图模型没有对 window 的引用。

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    “纯粹”的 MVVM 解决方案是使用行为。以下是具有 Activated 属性的 Window 的行为。将该属性设置为 true 将激活窗口(如果最小化则将其恢复):

    public class ActivateBehavior : Behavior<Window> {
    
      Boolean isActivated;
    
      public static readonly DependencyProperty ActivatedProperty =
        DependencyProperty.Register(
          "Activated",
          typeof(Boolean),
          typeof(ActivateBehavior),
          new PropertyMetadata(OnActivatedChanged)
        );
    
      public Boolean Activated {
        get { return (Boolean) GetValue(ActivatedProperty); }
        set { SetValue(ActivatedProperty, value); }
      }
    
      static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
        var behavior = (ActivateBehavior) dependencyObject;
        if (!behavior.Activated || behavior.isActivated)
          return;
        // The Activated property is set to true but the Activated event (tracked by the
        // isActivated field) hasn't been fired. Go ahead and activate the window.
        if (behavior.AssociatedObject.WindowState == WindowState.Minimized)
          behavior.AssociatedObject.WindowState = WindowState.Normal;
        behavior.AssociatedObject.Activate();
      }
    
      protected override void OnAttached() {
        AssociatedObject.Activated += OnActivated;
        AssociatedObject.Deactivated += OnDeactivated;
      }
    
      protected override void OnDetaching() {
        AssociatedObject.Activated -= OnActivated;
        AssociatedObject.Deactivated -= OnDeactivated;
      }
    
      void OnActivated(Object sender, EventArgs eventArgs) {
        this.isActivated = true;
        Activated = true;
      }
    
      void OnDeactivated(Object sender, EventArgs eventArgs) {
        this.isActivated = false;
        Activated = false;
      }
    
    }
    

    该行为需要引用System.Windows.Interactivity.dll。幸运的是,现在可以在 NuGet 上的 Blend.Interactivity.Wpf 包中使用它。

    行为在 XAML 中附加到窗口,如下所示:

    <Window ...>
      <i:Interaction.Behaviors>
        <Behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}"/>
      </i:Interaction.Behaviors>
    

    视图模型应该公开一个布尔值Activated 属性。将此属性设置为 true 将激活窗口(除非它已被激活)。作为额外的奖励,它还将恢复一个最小化的窗口。

    【讨论】:

    • 谢谢马丁。这是真正的答案,而不是事件黑客攻击。
    • Activated 属性设置为false 会发生什么?
    • @RomanKo:除了属性之外没有任何东西会变为假,但您不应该将其设置为假。设置属性为true激活窗口,获取属性值判断窗口是否被激活。
    【解决方案2】:

    您可以通过多种方式来解决它 - 添加对窗口的引用可能会起作用,因为 viewmodel 不与视图耦合而是与之相关,但我不太喜欢这种方法,因为它几乎可以将您的视图耦合到您的视图模型 - 这并不是 MVVM 的真正意义

    更好的方法可能是让您的视图模型引发视图可以处理的事件或命令。这样,视图就可以决定与命令/事件相关联的 UI 操作

    例如简单的

    class SomeView 
    {
        void HandleSomeCommandOrEvent() 
        {
            this.Activate();
        }
    }
    

    当然,如何连接这取决于你,但我可能会尝试让路由命令发生

    编辑:你不能真正“绑定”一个简单的事件,因为它是从视图模型中调用的。

    一个简单的基于事件的示例只是将事件添加到视图模型并直接处理它......例如想象以下带有 ViewModel 属性的 MainWindow

    public partial class MainWindow : Window
    {
        MainWindowViewModel ViewModel { get; set; }
    
        public MainWindow()
        {
            InitializeComponent();
    
            ViewModel = new MainWindowViewModel();
            ViewModel.ShowMessage += ViewModel_ShowMessage;
            this.DataContext = ViewModel;
        }
    
        void ViewModel_ShowMessage(object sender, ShowMessageEventArgs e)
        {
            MessageBox.Show(e.Message, "Some caption", MessageBoxButton.OK);
        }
    
    }
    

    然后 ViewModel 可以触发事件:

    // The view model
    public class MainWindowViewModel
    {
        // The button click command
        public RelayCommand ButtonClickCommand { get; set; }
    
        // The event to fire
        public event EventHandler<ShowMessageEventArgs> ShowMessage;
    
        public MainWindowViewModel()
        {            
            ButtonClickCommand = new RelayCommand(ButtonClicked);            
        }
    
        void ButtonClicked(object param)
        {
            // This button is wired up in the view as normal and fires the event
            OnShowMessage("You clicked the button");
        }
    
        // Fire the event - it's up to the view to decide how to implement this event and show a message
        void OnShowMessage(string message)
        {
            if (ShowMessage != null) ShowMessage(this, new ShowMessageEventArgs(message));
        }
    }
    
    public class ShowMessageEventArgs : EventArgs
    {
        public string Message { get; private set; }
    
        public ShowMessageEventArgs(string message)
        {
            Message = message;
        }
    }
    

    XAML 将是:

    <Button Command="{Binding ButtonClickCommand}">Click me!</Button>
    

    所以按钮调用命令,该命令反过来触发视图(MainWindow)处理的事件并显示一个消息框。通过这种方式,视图/UI 根据引发的事件类型决定操作过程。当然可能是你的计时器触发了这个事件

    你总是可以走更复杂的路线,比如这个问题的一些答案......

    How should the ViewModel close the form?

    但老实说,这取决于您是否真的需要它 - 一个简单的事件效果很好 - 有些人为了优雅而将事情过度复杂化,但却损害了简单性和生产力!

    【讨论】:

    • 我正在学习 wpf 和绑定,所以这可能是一个简单的问题。如何将命令绑定到视图?我正在使用按钮执行此操作,但我不知道如何在没有 wpf 控件的情况下执行此操作 其中 AddTask 是一个 ICommand。
    【解决方案3】:

    我会走这条路:

    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;    
    using GalaSoft.MvvmLight.Messaging; 
    
    // View
    
    public partial class TestActivateWindow : Window
    {
        public TestActivateWindow() {
            InitializeComponent();
            Messenger.Default.Register<ActivateWindowMsg>(this, (msg) => Activate());
        }
    }
    
    // View Model
    
    public class MainViewModel: ViewModelBase
    {
        ICommand _activateChildWindowCommand;
    
        public ICommand ActivateChildWindowCommand {
            get {
                return _activateChildWindowCommand?? (_activateChildWindowCommand = new RelayCommand(() => {
                    Messenger.Default.Send(new ActivateWindowMsg());
            }));
            }
        }
    }
    
    public class ActivateWindowMsg
    {
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-04
      • 2012-10-08
      • 1970-01-01
      • 2017-12-02
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 2015-11-08
      相关资源
      最近更新 更多