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

时间:2012-08-13 20:00:31

标签: c# wpf mvvm

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

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

3 个答案:

答案 0 :(得分:18)

A"纯粹主义者" MVVM解决方案是使用行为。以下是具有Window属性的Activated的行为。将该属性设置为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。幸运的是,现在可以在Blend.Interactivity.Wpf包中的NuGet上使用。

行为附加到XAML中的Window,如下所示:

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

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

答案 1 :(得分:4)

你可以通过几种方式来实现它 - 添加对窗口的引用可以工作,因为viewmodel没有与视图耦合但与之相关,但我不喜欢这种方法,因为它几乎可以将您的视图耦合到您的viewmodel - 这实际上不是MVVM的重点

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

关联的UI操作

e.g。简单地

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

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

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

基于简单事件的示例只是将事件添加到viewmodel并直接处理它...例如想象一下具有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?

但说实话,这取决于你是否真的需要它 - 一个简单的事件很有效 - 有些人为了优雅而过于复杂化,但却不利于简单和生产力!

答案 2 :(得分:0)

我会这样:

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