如何使用wpf和使用mvvm将窗口带到前面

Jos*_*osh 6 c# wpf mvvm

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

从我可以收集的内容中我只需要调用window.activate()来完成此操作但是使用mvvm我的视图模型没有对窗口的引用.

Mar*_*age 18

"纯粹的"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;
  }

}
Run Code Online (Sandbox Code Playgroud)

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

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

<Window ...>
  <i:Interaction.Behaviors>
    <Behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}"/>
  </i:Interaction.Behaviors>
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢马丁。这是真正的答案,而不是事件黑客。 (2认同)

Cha*_*leh 4

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

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

例如简单地

class SomeView 
{
    void HandleSomeCommandOrEvent() 
    {
        this.Activate();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

然后 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

XAML 将是:

<Button Command="{Binding ButtonClickCommand}">Click me!</Button>
Run Code Online (Sandbox Code Playgroud)

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

您始终可以走更复杂的路线,例如这个问题的一些答案......

ViewModel 应该如何关闭表单?

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