So my first attempt did everything out of the code behind, and now I'm trying to refactor my code to use the MVVM pattern, following the guidance of the MVVM in the box information.
I've created a viewmodel class to match my view class, and I'm moving the code out of the code behind into the viewmodel starting with the commands.
My first snag is trying to implement a 'Close' button that closes the window if the data has not been modified. I've rigged up a CloseCommand to replace the 'onClick' method and all is good except for where the code tries to run this.Close(). Obviously, since the code has been moved from a window to a normal class, 'this' isn't a window and therefore isn't closeable. However, according to MVVM, the viewmodel doesn't know about the view, so i can't call view.Close().
Can someone suggest how I can close the window from the viewmodel command?
I personally use a very simple approach: for every ViewModel that is related to a closeable View, I created a base ViewModel like this following example:
public abstract class CloseableViewModel
{
public event EventHandler ClosingRequest;
protected void OnClosingRequest()
{
if (this.ClosingRequest != null)
{
this.ClosingRequest(this, EventArgs.Empty);
}
}
}
Then in your ViewModel that inherits from CloseableViewModel, simply call this.OnClosingRequest(); for the Close command.
In the view:
public class YourView
{
...
var vm = new ClosableViewModel();
this.Datacontext = vm;
vm.ClosingRequest += (sender, e) => this.Close();
}
You don't need to pass the View instance to your ViewModel layer. You can access the main window like this -
Application.Current.MainWindow.Close()
I see no issue in accessing your main window in ViewModel class as stated above. As per MVVM principle there should not be tight coupling between your View and ViewModel i.e. they should work be oblivious of others operation. Here, we are not passing anything to ViewModel from View. If you want to look for other options this might help you - Close window using MVVM
My solution to close a window from view model while clicking a button is as follows:
In view model
public RelayCommand CloseWindow;
Constructor()
{
CloseWindow = new RelayCommand(CloseWin);
}
public void CloseWin(object obj)
{
Window win = obj as Window;
win.Close();
}
In View, set as follows
<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
I do it by creating a attached property called DialogResult:
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null && (bool?)e.NewValue == true)
window.Close();
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
then write this to you XAML, in the window tag
WindowActions:DialogCloser.DialogResult="{Binding Close}"
finally in the ViewModel
private bool _close;
public bool Close
{
get { return _close; }
set
{
if (_close == value)
return;
_close = value;
NotifyPropertyChanged("Close");
}
}
if you change the Close to true, the window will be closed
Close = True;
Here is the simplest and pure MVVM solution
ViewModel Code
public class ViewModel
{
public Action CloseAction { get; set; }
private void CloseCommandFunction()
{
CloseAction();
}
}
Here is XAML View Code
public partial class DialogWindow : Window
{
public DialogWindow()
{
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.CloseAction = Close;
}
}
This solution is quick and easy. Downside is that there is some coupling between the layers.
In your viewmodel:
public class MyWindowViewModel: ViewModelBase
{
public Command.StandardCommand CloseCommand
{
get
{
return new Command.StandardCommand(Close);
}
}
public void Close()
{
foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
{
if (window.DataContext == this)
{
window.Close();
}
}
}
}
MVVM-light with a custom message notification to avoid the window to process every notificationmessage
In the viewmodel:
public class CloseDialogMessage : NotificationMessage
{
public CloseDialogMessage(object sender) : base(sender, "") { }
}
private void OnClose()
{
Messenger.Default.Send(new CloseDialogMessage(this));
}
Register the message in the window constructor:
Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
Close();
});
This is very similar to eoldre's answer. It's functionally the same in that it looks through the same Windows collection for a window that has the view model as its datacontext; but I've used a RelayCommand and some LINQ to achieve the same result.
public RelayCommand CloseCommand
{
get
{
return new RelayCommand(() => Application.Current.Windows
.Cast<Window>()
.Single(w => w.DataContext == this)
.Close());
}
}
using MVVM-light toolkit:
In the ViewModel:
public void notifyWindowToClose()
{
Messenger.Default.Send<NotificationMessage>(
new NotificationMessage(this, "CloseWindowsBoundToMe")
);
}
And in the View:
Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
if (nm.Notification == "CloseWindowsBoundToMe")
{
if (nm.Sender == this.DataContext)
this.Close();
}
});
This is taken from ken2k answer (thanks!), just adding the CloseCommand also to the base CloseableViewModel.
public class CloseableViewModel
{
public CloseableViewModel()
{
CloseCommand = new RelayCommand(this.OnClosingRequest);
}
public event EventHandler ClosingRequest;
protected void OnClosingRequest()
{
if (this.ClosingRequest != null)
{
this.ClosingRequest(this, EventArgs.Empty);
}
}
public RelayCommand CloseCommand
{
get;
private set;
}
}
Your view model, inherits it
public class MyViewModel : CloseableViewModel
Then on you view
public MyView()
{
var viewModel = new StudyDataStructureViewModel(studyId);
this.DataContext = viewModel;
//InitializeComponent(); ...
viewModel.ClosingRequest += (sender, e) => this.Close();
}
Given a way, Please check
https://stackoverflow.com/a/30546407/3659387
Short Description
Derive your ViewModel from INotifyPropertyChanged
Create a observable property CloseDialog in ViewModel, Change CloseDialog property whenever you want to close the dialog.
Attach a Handler in View for this property change
Now you are almost done. In the event handler make DialogResult = true
first of all give your window a name like
x:Name="AboutViewWindow"
on my close button I've defined Command and Command Parameter like
CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"
then in my view model
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
if (_cancelCommand == null)
{
_cancelCommand = new DelegateCommand<Window>(
x =>
{
x?.Close();
});
}
return _cancelCommand;
}
}
Most MVVM-compliant solution using HanumanInstitute.MvvmDialogs
Implement ICloseable interface in your ViewModel and that's it!
No code in your view whatsoever.
Related
I have been trying to solve the following problem for a very long time but unfortunately, I am unable to get it fixed.
I have a button which I want to disable it in another page .cd
This is how my code looks like:
<StackLayout>
<Button Text="Click" IsEnabled="{Binding IsButtonEnabled}" Command="{Binding OnEnabledButtonClicked}"/>
</StackLayout>
public class MainPageViewModel : BaseViewModel
{
bool _isButtonEnabled;
public bool IsButtonEnabled
{
get => _isButtonEnabled;
set
{
_isButtonEnabled = value;
OnPropertyChanged(nameof(IsButtonEnabled));
}
}
public Command OnEnabledButtonClicked
{
get
{
return new Command( () =>
{
IsButtonEnabled = true;
}
}
}
}
And this is the class where I want to change the value of VM's button.
public class Page1 {
class page1() {
InitializeComponent();
}
public void OnDisabledButtonClicked(object sender, EventArgs e) {
/// IsButtonEnabled = false;
}
}
I have already tried different ways but still no result.
It would be a big help for me if someone provides me a solution for it.
Thanks in advance
Before I gave you an answer, I would like to point out few things.
It's common to suffix your command with Command : EnabledButtonCommand.
I can see that your command is async while you don't await anything. It's bad.
Why would you want to set IsButtonEnabled in the code behind instead of in the method executed by the command (in the ViewModel) ?
Where do you set the DataContext ? Do you use Prism or anything else to associate the ViewModel to your page's DataContext ? If you don't, you need to do this :
public class MyPage()
{
private MyViewModel _viewModel = new MyViewModel();
public MyPage()
{
InitializeComponent();
DataContext = _viewModel;
}
public void OnDisabledButtonClicked(object sender, EventArgs e)
{
_viewModel.IsButtonEnabled = false;
}
}
If your ViewModel was set in Xaml or elsewhere (while navigating, with Prism, etc)
public class MyPage()
{
private MyViewModel _viewModel;
public MyPage()
{
InitializeComponent();
_viewModel = DataContext as MyViewModel;
}
public void OnDisabledButtonClicked(object sender, EventArgs e)
{
_myViewModel.IsButtonEnabled = false;
}
}
A last word : if a button is not enabled, the command/click event won't be available to user until the button is enabled again.
You can test that with a button with its command binded to the given command and another button with IsEnabled binded to your boolean.
The code I gave may have things wrong as I answer in browser without using an EDI.
EDIT: Added concrete example to clarify what I trying to achieve.
Here is application scheme:
To make code simpler, I will use trivial Messenger class instead of event aggregator from Prism. Tuple contains Id and string payload.
public static class Messenger
{
public static event EventHandler<Tuple<int, string>> DoWork;
public static void RaiseDoWork(int id, string path)
{
DoWork?.Invoke(null, new Tuple<int, string>(id, path));
}
}
Model instance subscribe to messenger for knowing when to start work (if Id correct), and notify view-model when work finished.
public class Model
{
public int id;
public Model(int id)
{
this.id = id;
Messenger.DoWork += (sender, tuple) =>
{
if (tuple.Item1 != this.Id)
{
return;
}
var result = tuple.Item2 + " processed with id " + this.id;
this.OnWorkCompleted(result);
};
}
public event EventHandler<string> WorkCompleted;
private void OnWorkCompleted(string path)
{
this.WorkCompleted?.Invoke(null, path);
}
}
UserControlResult is responsible for payload processing and result output. To make code simpler, lets just trace output instead of putting it on UI. So XAML will be default.
Code-behind:
public partial class UserControlResult : UserControl
{
private ResultViewModel viewModel;
public UserControlResult()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ResultViewModel(id);
this.DataContext = this.viewModel;
}
}
View-model:
public class ResultViewModel
{
private Model model;
public ResultViewModel(int id)
{
this.model = new Model(id);
this.model.WorkCompleted += path =>
{
Trace.WriteLine(path);
};
}
}
UserControlButtons contains buttons, one of them should start processing of model in UserControlResult via messenger. To make code simpler, lets omit command implementation and just show its handler.
Code-behind:
public partial class UserControlButtons : UserControl
{
private ButtonsViewModel viewModel;
public UserControlButtons()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ButtonsViewModel(id);
this.DataContext = this.viewModel;
}
}
View-model:
public class ButtonsViewModel
{
private int id;
public ButtonsViewModel(int id)
{
this.id = id;
}
// DelegateCommand implementation...
private void StartWorkingCommandHandler()
{
Messenger.RaiseDoWork(this.id, "test path");
}
}
UserControlParent contains both UserControlResult and UserControlButtons. His only role is to pass Id to them, so he doesn't even need view-model.
Xaml:
<StackPanel>
<uc:UserControlResult x:Name="UserControlResult" />
<uc:UserControlButtons x:Name="UserControlButtons" />
</StackPanel>
Code-behind:
public partial class UserControlParent : UserControl
{
public UserControlParent()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.UserControlResult.Init(id);
this.UserControlButtons.Init(id);
}
}
And finally MainWindow contains two instances of UserControlParent. Its role to assign them different Ids.
Xaml:
<StackPanel>
<uc:UserControlParent x:Name="UserControlParent1" />
<uc:UserControlParent x:Name="UserControlParent2" />
</StackPanel>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.UserControlParent1.Init(111);
this.UserControlParent2.Init(222);
}
}
This will work: pressing button in UserControlButtons will start working in UserControlResult model, and both UserControlParent will working correct and independend thanks to Id.
But I believe that this chain of invoking Init methods is violates MVVM because code-behind (which is View in MVVM) should not know anything about Id value (which is relative to Model in MVVM). Talking that, I'm sure that Id is not part of view-model, because it doesn't have any presentation in UI.
How can I pass Id value from top window to "deepest" view-models without violating MVVM?
Original Question
Here is WPF application consisting from 3 UserControls:
UserControl3 is a part of UserControl2 content. I keep MVVM during developing and using Prism.
I need to invoke method of custom class (which is model in terms of MVVM) in UserControl3 from view-model of UserControl1. The restriction that custom class can't be singleton. I suppose to do it one of the following way:
Using event aggregator from Prism. UserControl1 view-model is publisher and UserControl3 model is subscriber. For this I'll need to create unique Id in Window and pass it to UserControl1 and UserControl3.
Creating service instance in Window and pass it to UserControl1 and UserControl3. Then UserControl1 will just invoke method of this instance.
Window pass UserControl2 instance to UserControl1. View-model in UserControl1 will just invoke method of UserControl2, which will invoke method of UserControl3 and so on.
It seems like 2 and 3 approaches violates MVVM. How would you resolve this situation?
I would use option 1. I use MVVM Light to send a message and whoever receives that specific message will fire off the service method. Loosely coupled.
I think I achieved truly MVVM implementation shown in simplified example below. Special thanks to Ed Plunkett's comment and Nikita's answer.
First, I don't need to pass unique Ids anymore. For identification of different ParentViewModel instances, I just pass them different Messenger instances (which replaces Prism's EventAggregator for the sake of simplicity):
internal class Messenger
{
public event EventHandler<string> DoWork;
public void RaiseDoWork(string path)
{
this.DoWork?.Invoke(this, path);
}
}
Second, it seems like in my particular case Model should not worry about Messenger's DoWork event. As soon as this event raised in one view-model (ButtonsViewModel), it is more appropriate for this event to be consumed by another view-model (ResultViewModel) rather than by Model itself. So Model simplified too:
internal class Model
{
public string Process(string input)
{
return input + " processed!";
}
}
Below demonstrated all view-models "from top to bottom".
internal class MainViewModel
{
private readonly Messenger eventAggregator1 = new Messenger();
private readonly Messenger eventAggregator2 = new Messenger();
public MainViewModel()
{
this.ParentViewModel1 = new ParentViewModel(this.eventAggregator1);
this.ParentViewModel2 = new ParentViewModel(this.eventAggregator2);
}
public ParentViewModel ParentViewModel1 { get; }
public ParentViewModel ParentViewModel2 { get; }
}
internal class ParentViewModel
{
public ParentViewModel(Messenger eventAggregator)
{
this.ButtonsViewModel = new ButtonsViewModel(eventAggregator);
this.ResultViewModel = new ResultViewModel(eventAggregator);
}
public ButtonsViewModel ButtonsViewModel { get; }
public ResultViewModel ResultViewModel { get; }
}
internal class ButtonsViewModel
{
private readonly Messenger eventAggregator;
public ButtonsViewModel(Messenger eventAggregator)
{
this.eventAggregator = eventAggregator;
this.StartCommand = new DelegateCommand(this.StartProcessing);
}
public DelegateCommand StartCommand { get; }
private void StartProcessing()
{
this.eventAggregator.RaiseDoWork("test path");
}
}
internal class ResultViewModel : ViewModelBase
{
private readonly Model model = new Model();
private string textValue;
public ResultViewModel(Messenger eventAggregator)
{
eventAggregator.DoWork += (sender, s) => this.DoWorkHandler(s);
}
public string TextValue
{
get { return this.textValue; }
set { this.SetProperty(ref this.textValue, value); }
}
private void DoWorkHandler(string s)
{
var result = this.model.Process(s);
this.TextValue = result;
}
}
Note that in ResultViewModel I replaced Trace.WriteLine with actual screen output (because now strings are without Id, so trace output the same). ViewModelBase just implements INotifyPropertyChanged.
Below demonstrated content part of all views "from top to bottom".
<!-- MainWindow.xaml -->
<StackPanel Orientation="Horizontal">
<views:UserControlParent DataContext="{Binding ParentViewModel1}" />
<views:UserControlParent DataContext="{Binding ParentViewModel2}" />
</StackPanel>
<!-- UserControlParent.xaml -->
<StackPanel>
<local:UserControlResult DataContext="{Binding ResultViewModel}" />
<local:UserControlButtons DataContext="{Binding ButtonsViewModel}" />
</StackPanel>
<!-- UserControlButtons.xaml -->
<Grid>
<Button Content="Test" Command="{Binding StartCommand}" />
</Grid>
<!-- UserControlResult.xaml -->
<Grid>
<TextBlock Text="{Binding TextValue}" />
</Grid>
And finally this two worlds are connected in App.xaml.cs:
private void App_OnStartup(object sender, StartupEventArgs e)
{
new MainWindow { DataContext = new MainViewModel() }.Show();
}
Seems like MVVM, but any remarks are welcome.
I want to open a new modal window using the MVVM pattern in a Xamarin Forms app. I have researched opening a new window with the MVVM pattern, which has got me this far, but the thing about windows in Xamarin forms, is they need a reference to the current page (view) to open a new window (new view) from. This forces me to pass a reference to the current page (view) from my viewModel, to my window factory, to launch the new window from. This is a violation of MVVM. My goal is to get rid of any references to views from within my viewModel. That is my question, how do I do that? My code here happens to be a modal window, but normal windows also need a reference to the page it is launching from. Here is my code and you will see what I mean:
Window Factory (look at the CreateNewWindow method):
public interface IWindowFactory
{
void CreateNewWindow();
}
public class ProductionWindowFactory: IWindowFactory
{
Page launchFromPage;
BackLogViewModel viewModel;
public ProductionWindowFactory(BackLogViewModel ViewModel, Page page)
{
viewModel = ViewModel;
launchFromPage = page;
}
public void CreateNewWindow()
{
AddStoryPage window = new AddStoryPage (new AddStoryViewModel (viewModel));
launchFromPage.Navigation.PushModalAsync (window);
}
}
}
ViewModel that opens a new modal window (look particularly at the AddTask Command):
public class BackLogViewModel : INotifyPropertyChanged
{
private IWindowFactory m_windowFactory;
public void DoOpenNewWindow()
{
m_windowFactory.CreateNewWindow();
}
public ObservableCollection<Story> AllMyStories { get; set; }
private string _updated;
public string Updated
{
get
{
return _updated;
}
set
{
_updated = value;
OnPropertyChanged ();
}
}
public Page mypage;
public BackLogViewModel (Page page)
{
Updated = DateTime.Now.ToString();
mypage = page;
AllMyStories = new ObservableCollection<Story> ();
}
public ICommand Save
{
get {
return new Command (() => {
Updated = DateTime.Now.ToString();
});
}
}
public ICommand AddTask
{
get {
return new Command ( () => {
m_windowFactory = new ProductionWindowFactory(this, mypage);
DoOpenNewWindow();
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs (propertyName));
}
}
private Command selectCmd;
public Command Select {
get {
this.selectCmd = this.selectCmd ?? new Command<Story>(p =>
{
var monkey = p as Story;
if (monkey == null) return;
Page z = new Views.StoryPage(p);
mypage.Navigation.PushAsync(z);
}
);
return this.selectCmd;
}
}
}
}
How do I get rid of the reference to the current page (view) within my viewModel?
I have since found This tutorial on navigating views from the ViewModel in Xamarin
It basically does what I was already doing but instead of passing the full view to the ViewModel, it passes only the INavigation interface of the view, and uses that to navigate from. It states that it can be argued that it is violating MVVM, but has the attitude of "so be it", I suspect because no obvious and easy alternatives exist. There may be alternatives that do not reference any part of the view from the ViewModel, but in order to keep moving forward I have opted for this easy solution. I have kept my window factory in order to not specify a concrete window to build in my ViewModel.
I am using wpf event to show popup window.
As my event method 'AddToBasketClicked' is executing twice, the popup window is loaded two times.
After popup window is opened first time, after performing operations and closing the window, the window is loaded again after executing the event method 'AddToBasketClicked' again.
[Export(typeof(IFigureDetailView))]
public partial class FigureDetailsView : IFigureDetailView
{
protected IEventAggregator EventAggregator
{
get { return MefFactory.CompositionContainer.GetExportedValueOrDefault<IEventAggregator>(); }
}
public FigureDetailsView()
{
LoggingManager.Debug("Entered into FigureDetails of FigureDetails.xaml.cs-TMSSS.PIT.Modules.Tempo.Views");
InitializeComponent();
var viewModel = MefFactory.CompositionContainer.GetExportedValueOrDefault<IFigureDetailViewModel>();
ViewModel = viewModel;
viewModel.EventAggregator.GetEvent<AddToBasketClickedEvent>().Subscribe(AddToBasketClicked);
LoggingManager.Debug("Exited from FigureDetails of FigureDetails.xaml.cs-TMSSS.PIT.Modules.Tempo.Views");
}
private void AddToBasketClicked(Guid figureItem)
{
LoggingManager.Debug("Entered into AddToBasketClicked of FigureDetails.xaml.cs-TMSSS.PIT.Modules.Tempo.Views");
var addToBasketView = new AddToBasketView();
var viewModel = ViewModel as IFigureDetailViewModel;
if (viewModel != null)
{
addToBasketView.LoadSelectedPart(viewModel.Asset, viewModel.FigureId, figureItem, viewModel.EventAggregator);
}
addToBasketView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
if (addToBasketView.ShowDialog() != true)
{
}
LoggingManager.Debug("Exited from AddToBasketClicked of FigureDetails.xaml.cs-TMSSS.PIT.Modules.Tempo.Views");
}
public bool IsFrontView
{
get { return true; }
set { }
}
public IViewModel ViewModel
{
get { return DataContext as IViewModel; }
set { DataContext = value; }
}
}
As mentioned in the comments by #argaz, the possibility is that you are creating two instances of FigureDetailsView, hence two subscriptions and two dialogs (easiest way would be to check your log).
From general understanding, it's okay to allow creating two instances of FigureDeailsView and in that case your 'AddToBasket' subscription shouldn't be there in this class. It should rather be in a single instance window, like 'Shell'. If nothing specific in your project, then 'FigureDetailsView' shouldn't actually have he code to show "AddToBasket" dialog.
Hope that helps.
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.
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.
A "purist" MVVM solution is to use a behavior. Below is a behavior for a Window with an Activated property. Setting the property to true will activate the window (and restore it if it is minimized):
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.Wpf package.
The behavior is attached to a Window in XAML like this:
<Window ...>
<i:Interaction.Behaviors>
<Behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
The view-model should expose a boolean Activated property. 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.
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
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
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
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:
// 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:
<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
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?
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!
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
{
}