如何使用 wpf 和使用 mvvm 将窗口置于前面

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11941401/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 04:57:52  来源:igfitidea点击:

How to bring window to front with wpf and using mvvm

c#wpfmvvm

提问by Josh

I have a window that essentially runs a timer. When the timer hits 0 I want to bring the window to the front so that it is visible and not hidden behind some other application.

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

From what I can gather I would simply call window.activate() to accomplish this but with mvvm my view model doesn't have a reference to window.

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

采纳答案by Charleh

You could go about it in a couple of ways - adding a reference to the window could work since the viewmodel is not coupled with the view but related to it, but I don't really like that approach since it pretty much does couple your view to your viewmodel - which is not really the point of MVVM

您可以通过几种方式进行操作 - 添加对窗口的引用可以工作,因为视图模型不与视图耦合但与之相关,但我真的不喜欢这种方法,因为它几乎与您的视图耦合到您的视图模型 - 这不是 MVVM 的真正重点

A better approach may be to have your viewmodel raise an event or a command which the view can handle. This way the view gets to decide what UI action is associated with the command/event

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

e.g. simply

例如简单地

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

Of course how you wire this up is up to you but I'd probably try and get routed commands happening

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

Edit: You can't really 'bind' a simple event, since it's invoked from the viewmodel.

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

A simple event based example is just to add the event to the viewmodel and handle it directly ... e.g. imagine the following MainWindow with a ViewModel property

一个简单的基于事件的示例只是将事件添加到视图模型并直接处理它......例如想象以下带有 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);
    }

}

Then the ViewModel can just fire the event:

然后 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;
    }
}

The XAML would be:

XAML 将是:

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

So the button invokes the command, which in turn fires the event which the view (MainWindow) handles and shows a messagebox. This way the view/UI decides on the course of action based on the type of event raised. Of course it could be your timer which fired the event

因此按钮调用命令,该命令依次触发视图 (MainWindow) 处理的事件并显示一个消息框。这样,视图/UI 会根据引发的事件类型决定操作过程。当然,它可能是您的计时器触发了事件

You can always go down the more involved route such as some of the answers on this question...

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

How should the ViewModel close the form?

ViewModel 应该如何关闭表单?

but to be honest, it depends if you really need it - a simple event works well - some people overcomplicate things for the sake of elegance, but at the detriment of simplicity and productivity!

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

回答by Martin Liversage

A "purist" MVVM solution is to use a behavior. Below is a behavior for a Windowwith an Activatedproperty. Setting the property to true will activate the window (and restore it if it is minimized):

“纯粹”的 MVVM 解决方案是使用行为。以下是Window具有Activated属性的a 的行为。将属性设置为 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;
  }

}

The behavior requires a reference to System.Windows.Interactivity.dll. Fortunately, this is now available on NuGet in the Blend.Interactivity.Wpfpackage.

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

The behavior is attached to a Window in XAML like this:

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

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

The view-model should expose a boolean Activatedproperty. Setting this property to true will activate the window (unless it is already activated). As an added bonus it will also restore a minimized window.

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

回答by romanoza

I would go this way:

我会走这条路:

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
{
}