基本的にタイマーを実行するウィンドウがあります。タイマーが0に達したときに、ウィンドウを前面に移動して、他のアプリケーションの背後に表示されたり隠されたりしないようにします。
私が収集できるものから、これを達成するためにwindow.activate()を呼び出すだけですが、mvvmを使用すると、ビューモデルにwindowへの参照がありません。
「純粋な」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 に設定すると、ウィンドウがアクティブになります (まだアクティブになっていない場合)。追加のボーナスとして、最小化されたウィンドウも復元されます。
いくつかの方法でそれを行うことができます-ビューモデルはビューと結合されていませんが、それに関連しているため、ウィンドウへの参照を追加することは機能しますが、ビューをほとんど結合するため、そのアプローチはあまり好きではありませんあなたのビューモデルに-これは実際には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 は、発生したイベントのタイプに基づいて一連のアクションを決定します。もちろん、イベントを発生させたのはタイマーである可能性があります
この質問に対するいくつかの回答など、より複雑なルートをいつでも下ることができます...
ViewModel はどのようにフォームを閉じる必要がありますか?
しかし正直なところ、それは本当に必要かどうかによって異なります - シンプルなイベントはうまくいきます - エレガンスのために物事を過度に複雑にする人もいますが、シンプルさと生産性を犠牲にしています!
私はこのように行きます:
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
{
}