It stays for me unclear who creates and disposes windows in WPF.
As we know window can be closed by clicking the cross sign in the upper right corner and we can stop closing only by setting Cancel = true in Closing event handler.
For me it is quite natural for the ViewModel to decide if View is allowed to close. And I think it is VM whow request the instantiation of the appropriate window.
In our project we created IViewManager:
public interface IViewManager
{
void ShowView();
void CloseView();
}
public interface IWindowedViewModel : IDisposable
{
bool IsEnabled {get;set;}
WindowState WindowState {get;set;}
Visibility Visibility {get;set;}
bool IsActive {get;set;}
bool CanBeClosed();
}
So our VMs communicate with the view via bindings and indirectly via IViewManager.
In the examples of MVVM applications I've seen so far VM is quite passive and is not responsible for View's lifecyle. That's why I have some doubts about our design.
I wan't to be sure we are not missing something important concerning MVVM pattern.
For that you will have to Modify your Window to have bool type Depedency Property like CanSave that will be bound to your ViewModel(this Property will say whether ther are any pending changes or not).Now if CanSave is true you will show user a DialogBox which says "Do you want to Save pending changes" and if User Click Yes fire the Same Command as that is for your Save Button , If user clicks No Just simply close the window.And if CanSave is false simply close the window.I hope this will give you an idea.
Another way to do that is Make Window Close Button Custom and bind it to ViewModel through Command and handle it From there.
Related
I've been using Josh Smith's implementation of RelayCommand in a couple of large projects for some years now. However today I've come across a scenario where the CanExecute on one of my commands isn't refreshing. I'm at a loss as to what's causing it - the view-model isn't doing anything that I haven't done dozens of times already.
The VM's constructor creates a couple of commands. The first one is a "start" command:-
StartCommand = new RelayCommand(o => StartAsync(), o => true);
The StartAsync() method looks like this:-
private async void StartAsync()
{
IsRunning = true;
await Task.Run(() => SomeLongRunningProcess(); }
IsRunning = false;
}
There is also a "save" command:-
SaveCommand = new RelayCommand(o => Save(), o => !IsRunning);
('IsRunning' is a bog-standard property, implementing INotifyPropertyChanged. As well as being used for the "CanExecute" delegate, it's also bound to the IsEnabled property of a few controls in the view to enable/disable them).
When I click my "Start" button (bound to 'StartCommand'), the "Save" button is correctly disabled. The b/g process runs to completion, then IsRunning is set to false, but this doesn't trigger the "Save" button to become enabled. It only enables if I click somewhere on my view.
(The controls whose IsEnabled property is bound to the VM IsRunning property do enable and disable correctly, by the way).
I've come across a few SO articles about this, but nothing really explains why this happens. My workaround was to bind the button's IsEnabled property to 'IsRunning', but it's frustrating that this particular view refused to play ball. Any thoughts on what might be causing this? Common sense says it's something specific to this view/VM, but I'm stumped (and I'm not going to post the code here - there's too much of it).
Yes, because the version of RelayCommand you're using is depending on CommandManager.RequerySuggested event and it's not accurate.
Its documentation states that
Occurs when the CommandManager detects conditions that might change
the ability of a command to execute.
Basically it guesses all the possible events where your data could be changed. It can never know when your ViewModel/Model is changed. It isn't listening for the property change notifications.
If you want to react immediately without waiting for CommandManager to guess, you need to fire the ICommand.CanExecuteChanged manually yourself when model is updated.
You saw that the event not fired unless you click the window or something but do note that it could fire several times too
I have a LoginWindows, that run in startup.
I have a enterButton ,when click it, send a parametr to mainwindows and show it then hide self.
public RelayCommand EnterCommand { get; set; }
...
public LoginViewModel()
{
EnterCommand = new RelayCommand(() => Enter());
}
private object Enter()
{
//Show MainWndow
}
What is the best way to open a new window from the viewmodel in mvvmLight?
It is Useful answer. https://stackoverflow.com/a/16994523/970404
Concepts:
Registering Multiple VM's with the SimpleIoC and using
GetInstance(...) to request them out.
Messenger class usage with a custom message type OpenWindowMessage
Opening Modal / Non Modal
Windows from a parent VM staying true to the MVVM principles
Passing
data between windows(just shown in NonModal)
Important Note:
The method used in this example to set the non DP DialogResult from the modal window is not MVVM friendly cos it uses code-behind to set the DialogResult property on a Window.Closing event which should be avoided(If needing to be "testable"). My preferred approach is a bit long and is very well documented HERE(Mixture of question and answer). Hence why I ignored it for the sake of this sample.
I have an application, and I have an assembly.
In the application, I have a window, and in the assembly I have a user control.
There is an instance of the user control in the window.
Both the user control and the window are backed by separate viewmodels.
In the user control, there is a button. The button should be enabled/disabled based on the state of the user control's viewmodel. When the button is clicked, processing needs to be done, based on the information in the user control's viewmodel, but it needs to be done by the window's viewmodel. (There are aspects of what needs to be done that are, and should be, outside of the scope of the user control.)
And here's the twist - this user control won't be used exclusively in this window, it might be used in another, or in a control that is used in a third. The user control can't be allowed to know what kind of window or control contains it, or is handling the process when its button is clicked.
So, what to do?
Define a command in the assembly, and bind the user control's button to it, passing the user control's viewmodel as the command parameter? How, then, do I bind the command to the window's viewmodel?
Or should I define the command in the user control's viewmodel, then raise an event to tell the parent window that the appropriate action needs to be taken?
It's not clear to me which is cleaner.
If you always know that the parent's property is going to be exposed the same with the same name, you can do something like this that has worked for me plenty of times:
Command={Binding Parent.DataContext.SomeCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}
This gets the usercontrol, then goes to the parent and gets that datacontext and binds it to that command. This works when the user control will be encompassed by many windows / controls that expose the same command (you could implement an interface here).
You could then pass the user control's viewmodel to the command (again, implement some interface) like so:
CommandParaemter={Binding }
You could use a Messenger structure to communicate between ViewModels.
MVVMLight contains one that you could use or you could write your own.
Before doing this make sure you did separate the responsibilities correctly or you'll end up with spaghetti-messages-code.
There should be hierarchy with your view models, just like you have with your controls. The main window has a child user-control. The Main View Model should be able to get connected with User Control View Model (and assign it if needed). Here is how I would do it:
public class MainVM:NotificationObject
{
// Make this a Notify Property
public UserVM userVM { get{return _userVM;}; set {_userVM = value; RaisePropertyChanged("userVM");}
public MainVM
{
userVM = new UserVM();
userVM.ExecuteCmd = new DelegateCommand (yourAction);
}
}
public class UserVM:NotificationObject
{
public DelegateCommand ExecuteCmd {get{return _executeCmd;} set{_executeCmd = value; RaisePropertyChanged("ExecuteCmd");
}
}
XAML:
<local:urUserCtrl DataContext={Binding userVM}/>
This is of course psuedocode
Sounds like a case for the Strategy pattern. http://en.wikipedia.org/wiki/Strategy_pattern
Define an interface for a strategy object that can be assigned to the UserControl's viewmodel, (or used to initialise it). The interface defines whatever properties/methods/events are required to enable the strategy object to retrieve from the UserControl viewmodel the data needed for the processing, plus a means of returning the result of the processing back to the UserControl viewmodel.
Then create a concrete implementation of that strategy object that collaberates with the Window's viewmodel to perform whatever task it needs to. In this case the Window's viewmodel might even implement the strategy interface itself.
Other instances of the UserControl in other scenarios can then be initialised with other concrete implementations of the strategy object that perform the same required task, but possibly in very different ways.
I was wondering if there was a way to close a window when a property in the view model changes. In my situation I have a login window with an Ok button bound to a LoginCommand so that the function Login executes when Ok is clicked. If the login is successful, I want the window to close.
Now I know I could do this by adding an event handler on my button, which calls a function like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
DatabaseCredentialsViewModel vm = (this.DataContext as DatabaseCredentialsViewModel);
vm.Login();
if (vm.LoginSuccessful)
{
this.Close();
}
}
But I was wondering if there was a way to close the window when LoginSuccessful property changes without having an event handler on my button (I like working only with command binding and not having event handlers on Click event).
Thank you
Here's a similar question, which filled my need.
Basically, you use an attached property for your window, which binds to a bool? property on your VM. When the VM property is set to something non-null, the attached property sets the Window's DialogResult, which will automatically close the window.
If you want you can try this different approach.
You can do this by associating the OK button with a command. Create an event such as LoginSuccess and when then add a window.Close() to the list of event callback. Then you have only to raise the LoginSuccess event to close the windows.
In my opinion, this respect the MVVM pattern defining an event that can be used for other trigger and not only for closing windows.
You could do this fairly easily by creating an attached property or Behavior (from Blend SDK) that hooked into your Window.
I posted a sample behavior to the Expression Code Gallery which does something similar (though definitely different) - it prevents a window from being closed via a property on the VM. You could very easily adapt the code (included in the download) to just close the window on a property change.
within my current project file I have a user control that has a storyboard animation applied to the control. When a button is clicked in the page the storyboard starts and basically visually presents the control to the user. The storyboard resides in the current page as a resource
<navigation:Page.Resources>
<Storyboard x:Name="PreferncesOpen">....</Storyboard x:Name="PreferncesOpen">
</navigation:Page.Resources>
Within the page I have button that I have a click event on that starts the storyboard
private void btnOpenPreferences_Click(object sender, RoutedEventArgs e)
{
preferencesPanel.Visibility = System.Windows.Visibility.Visible;
PreferncesOpen.Begin();
}
Within the userControl (preferencesPanel) I have a button that when clicked needs to close/collapse the user control. I plan to do this using Visibility.collapsed. I assume that I need to use routed commands since the button is within the user control but the actions need to be called within the page that contains the control? I'm still new to routed commands and I assume this is the correct approach. I'm just unsure how to click on a button within the user control and have it modify or execute commands that would impact how the page (in which this control resides) may change or for that part affect other elements within the page? For example when the button is clicked within the user control I would like the visibility of the user control to be set to collapsed. I also would like to have the width of one of the grid columns within the main page re-size. I have done this in the past using the code behind for the page but I am trying to separate some of this and I thought routed commands would be the way to go?
I'd greatly appreciate any tips.
Thank you in advance
The title is a bit misleading, you're asking about commands rather then routed events if I understand you correctly.
Here's an example of using a DelegateCommand<T> from the Prism library; It happens to be my personal preference.
Markup :
<Button x:Name="MyButton" Content="Btn" Command="{Binding DoSomethingCommand}"/>
Code-behind* or ViewModel :
(* if you're not using MVVM make sure to add MyButton.DataContext = this; so you're sure that the button can databind to your code behind effectively)
public DelegateCommand<object> DoSomethingCommand
{
get
{
if(mDoSomethingCommand == null)
mDoSomethingCommand = new DelegateCommand(DoSomething, canDoSomething);
return mDoSomethingCommand;
}
private DelegateCommand<object> mDoSomethingCommand;
// here's where the command is actually executed
void DoSomething(object o)
{}
// here's where the check is made whether the command can actually be executed
// insert your own condition here
bool canDoSomething(object o)
{ return true; }
// here's how you can force the command to check whether it can be executed
// typically a reaction for a PropertyChanged event or whatever you like
DoSomethingCommand.RaiseCanExecuteChanged();
The argument that's passed to the above function is the CommandParameter dependency property (in Prism it's an attached property as well as the Command property if memory serves me right).
When it's set, you can pass a value of your choosing to the command that you wish to execute.
Hope that helps.