WPF switching views - c#

I am implementing a WPF application and I am switching view models on button click. I had to implement an navigation store by youtube tutorial. When I click a button, navigateCommand will execute, creating a new viewModel and notifying view to change. However I dont understand what is method OnCurrentViewModelChanged() doing and why is it needed, action CurrentViewModelChanged is returning void, and is empty? Or am I missing something? What is CurrentViewModelChanged doing? Can someone please explain?
public class NavigationStore
{
public event Action CurrentViewModelChanged;
private NotifyPropertyChanged currentViewModel;
public NotifyPropertyChanged CurrentViewModel
{
get => currentViewModel;
set
{
currentViewModel = value;
OnCurrentViewModelChanged();
}
}
private void OnCurrentViewModelChanged()
{
CurrentViewModelChanged?.Invoke();
}
}
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;
public NavigateCommand(NavigationStore navigationStore, Func<TViewModel> createViewModel)
{
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}
public override void Execute()
{
_navigationStore.CurrentViewModel = _createViewModel();
}
}
public class MainViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
public NotifyPropertyChanged CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(NavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.CurrentViewModelChanged += OnCurrentViewModelChanged;
}
private void OnCurrentViewModelChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}

So first of all, I also followed his tutorials (it's most likely SingletonSean's) and I don't share #BenicCode's opinion on that (tho I'm not a professional at WPF like he may be), I really like his explanations and solutions to problems. Besides, he keeps changing the project throughout the guide, implementing better solutions and explaining why it's better to use this than that.
The OnCurrentViewModelChanged() method raises an event so that any method that is subscribed to it will be invoked. However, you actually don't need it, you can implement NavigationStore like this:
NavigationStore.cs
public class NavigationStore : INavigationStore
{
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel
{
get => _currentViewModel;
set
{
_currentViewModel?.Dispose();
_currentViewModel = value;
NavigationStateChanged?.Invoke();
}
}
public event Action? NavigationStateChanged;
}
And now, in your MainViewModel, you can simply subscribe the NavigationStateChanged action to OnCurrentViewModelChanged() instead of having one more method in your navigation store.
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private readonly INavigationStore _navigationStore;
public ViewModelBase? CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(INavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.NavigationStateChanged += OnNavigator_NavigationStateChanged;
}
private void OnNavigator_NavigationStateChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}
It's basically the same, but a bit simpler (correct me if I'm wrong). By subscribing NavigationStateChanged to OnNavigator_NavigationStateChanged, whenever NavigationStateChanged is raised, OnNavigator_NavigationStateChanged will fire too, which will notify your UI to change (since you bind the ContentControl's Content property to the CurrentViewModel property).
MainWindow.xaml
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
At this point of the tutorial he just wanted to demonstrate really basic navigation. As you progress further, things get cleaner and more complicated. I really suggest finishing his tutorials, there might be better guides, but as a starting point, I couldn't find any better channel.

Related

Action on ComboBox selection changed

I'm using MVVM to bind a ComboBox to a ViewModel, and I have few question about heavy actions and selection change.
I want to trigger some actions when the selected item is changed, my initial approach was to put the logic in the setter of the field to which the selected item is binded.
So my first question is, is this good practice or there is a better approach?
Those actions may be very expensive in time and resources (need to retrieve data through a web service) and I don't want the UI to freeze, so lately I've started to send a message from the set which is received in the view's code-behind and that call a ViewModel command asynchronously.
Am I just wasting time or does this make any sense?
The problem is that when I'm debugging the UI sometimes freeze anyway (it doesn't happened on release). Reading here and there I've come to know that it may be debugger related, can anyone confirm this behavior on VS 2015?
Additional information
As requested I provide some examples. This is my first approach:
(XAML)
<ComboBox SelectedItem="{Binding SelectedField}"/>
(ViewModel)
public class ViewModel
{
private MyObject _selectedField = null;
public MyObject SelectedField
{
get
{
return _selectedField;
}
set
{
if(_selectedField != value)
{
// Expensive action
_selectedField = value;
RaisePropertyChanged(() => SelectedField);
}
}
}
}
The expensive action make some web service calls and may take long, is this design good or is there a better way to achieve this?
My second approach is through messages, as shown in this example:
(ViewModel)
public class ViewModel
{
private MyObject _selectedField = null;
public MyObject SelectedField
{
get
{
return _selectedField;
}
set
{
if(_selectedField != value)
{
Messenger.Default.Send(new DoStuffMessage());
_selectedField = value;
RaisePropertyChanged(() => SelectedField);
}
}
}
private RelayCommand _doStuffCommand = null;
public ICommand DoStuffCommand
{
get
{
if (_doStuffCommand == null)
_doStuffCommand = new RelayCommand(async () => await DoStuff());
return _doStuffCommand;
}
}
private async Task DoStuff()
{
// Expensive action
}
}
(Code-behind)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<DoStuffMessage>(this, DoStuffMessage_Handler);
}
private void DoStuffMessage_Handler(DoStuffMessage msg)
{
(DataContext as ViewModel).DoStuffCommand.Execute(null);
}
}
Is this approach better or is just bad and useless?
For MVVM, I prefer to use RelayCommands to bind an EventTrigger in XAML to an ICommand in the viewmodel. I feel this creates the best separation of code and is clearer than adding a lot of logic to my setters, where it might be overlooked during troubleshooting. Here is an overview of the process: https://msdn.microsoft.com/en-us/magazine/dn237302.aspx
This is to wire up a button and pass in a parameter, so obviously you would need to modify it for your use case, but it will show the basic technique. In XAML:
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<Custom:EventToCommand Command="{Binding MyCommand}" CommandParameter="foo"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
In your VM:
public static ICommand MyCommand { get; set; } // declare an ICommand - bind to this!
public MainViewModel(IDataService dataService)
{
// associate your ICommand with a method. If you don't use a parameter, you don't need the lambda expression here.
MyCommand = new RelayCommand<string>((paramater) => MyCommandMethod(parameter));
}
public void MyCommandMethod(string parameter)
{
Debug.WriteLine("This is the code I want to run in my VM. The parameter is " + parameter);
}
I use the [free] MVVMLight toolkit for my applications, which was written by the guy who wrote the article that I linked to, but a lot of this is baked into .Net also. Using Expression Blend can make it easier to wire this stuff up when you are designing.
You can do whatever you like in setter as long as it is async.
private string _test;
public string Test
{
get { return _test; }
set
{
Task.Run(() =>
{
//do stuff
});
_test = value;
}
}
If you don't want to place logic in setter, because for example the Single Responsibility principle is violated, you should use interactions to catch the SelectionChange event and call a command in VM which should call an async method.
Here you have a sample that uses interactions : cute link
That's it!

Open another window in View.cs

I have a Window, which contains a Button AddParameter.
This Button has an Event called Button_Click.
Staying within the MVVM pattern, is it allowed to open a new window with a simple Button_Click? As far as I understood it, the code-behind of the View still counts as View:
private void Button_Click(object sender, RoutedEventArgs e) {
AddParameterWindow addParamWindow = new AddParameterWindow();
addParamWindow.Show();
}
Doing that with ICommands seems rather unnecessary, so I wanted to know if this would still count as a clean MVVM solution.
I don't think there is anything at all wrong with opening a window from another window in MVVM. The MVVM pattern is about separation of concerns in terms of ViewModels (and underlying models) being represented in any way necessary without it knowing anything about the View (see here for a good intro).
However, I think you have to ask yourself if making a new Window is really a good feature. Have you seen applications spawn another Window, and do you like that behavior? Have you given popups a thought which can look like Windows and can bind to the same ViewModel as the Window or UserControl it is logically under? Personally I avoid instantiating new Windows because I can centralize things that I want to appear in every View, like Styles, timeout Timers, etc.
You can ofcourse use the event Button_Click to open a new window, but that is now out of MVVM.
This maybe not right or good practice with MVVM, but this is how I do it:
assuming you have a ViewModelBase.cs that is something like this:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a DelegateCommand.cs that extends ICommand:
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}
Now in your SampleViewModel.cs:
public class SampleViewModel : ViewModelBase
{
public SampleViewModel()
{
}
public ICommand OpenWindowCommand
{
get { return new DelegateCommand(OpenSampleWindow); }
}
private void OpenSampleWindow()
{
var sampleWindow = new SampleWindow();
sampleWindow.Show();
}
}
Now in your View you can now bind your command to your button:
<Button Command="{Binding OpenWindowCommand}"/>

Is it okay to use ICommand in view-model

Most of the WPF mvvm applications, we are using ICommand in the view-model. But it is referring to System.Windows.Input. so the view-model is now tightly couple with System.Windows.Input namespace. according to my understanding view-model should be able to use in normal C# winform application or asp.net application.
Normally we are using following code lines to the command with RelayCommand implementation.
private RelayCommand testCommand;// or private ICommand testCommand;
public ICommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
What i feel is we need to remove all the ICommand and use RelayCommand instead. So we can eliminate the System.Windows namespace from the view-model. so final code will looks like this,
private RelayCommand testCommand;
public RelayCommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
Any suggestions on this approach? or is there any way to eliminate the System.Windows namespace from the view-model?
Any suggestions on this approach?
This still doesn't decouple you from System.Windows.Input as RelayCommand still must implement ICommand, even if it's indirectly implementing it.
Implementing ICommand within the ViewModel is one of those things that tends to be required in order to be pragmatic. Ideally, ICommand (or a similar interface) would have been implemented in a namespace that wasn't XAML specific. That being said, it is supported directly within the Portable Class Libraries, so it is not tied to a specific framework (WPF, Silverlight, Phone, etc) as much as XAML in general.
Pretty simple to avoid coupling your ViewModel to ICommand, if you want to. Probably not a bad idea, WPF will probably go the way of MFC one day. Overkill? maybe, but here is a how:
In your view:
<StackPanel>
<Button Command="{Binding Path=MyCommand}"> Do it! Kill me Now!</Button>
<TextBlock Text="{Binding Path=Message}"></TextBlock>
</StackPanel>
Inject your ViewModel into your DataContext, Take the responsibility for the native commands, out of your view model:
public class ViewModel : INotifyPropertyChanged
{
public string Message { get; set; }
public object MyCommand { get; set; }
public void OnMyCommand(object parameter)
{
Message += "I Ran something" + Environment.NewLine;
}
public bool CanMyCommand(object parameter)
{
return true;
}
// Injected Native Command handler
public ViewModel(ICommandFactory factory)
{
MyCommand = factory.CreateInstance(OnMyCommand, CanMyCommand);
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note I'm using FODY to weave in the property change handler. INotifyPropertyChanged is System.dll btw.
Now, Bind this contract:
public interface ICommandFactory
{
object CreateInstance(Action<object> action, Func<object, bool> predicate);
}
... to something that will give you a native Command object;
public class NativeCommand : ICommand
{
private readonly Action<object> _action;
private readonly Func<object, bool> _predicate;
public NativeCommand(Action<object> action, Func<object, bool> predicate)
{
_action = action;
_predicate = predicate;
}
public bool CanExecute(object parameter)
{
return _predicate(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public event EventHandler CanExecuteChanged;
}
public class NativeCommandFactory : ICommandFactory
{
public object CreateInstance(Action<object> action, Func<object, bool> predicate)
{
return new NativeCommand(action, predicate);
}
}
Bind<ICommandFactory>().To<NativeCommandFactory>();
VoilĂ , decoupled commands.
Also note, your injection is done at initial application start. Your ViewModel is decoupled from whatever IoC container you choose.
Well, in theory, you are pretty much right. It would if nice of ICommand was completely UI-platform-independent.
But from a practical standpoint, if you are using MVVM in a WPF app, there's a pretty good chance you are fairly dependent on WPF's databinding and datatemplating capabilities anyway. Trying to stick a WinForms UI on top of something like that would likely require a significant amount of extra effort.
I've worked on some fairly large WPF/MVVM projects in the past. We considered MVVM to be a way of separating the specific details of the UI from the code - not so that we could switch to WinForms/ASP.NET/whatever, but so that we could change the look and feel of our UI (i.e. edit the XAML) without having to change the ViewModels. In this respect, MVVM worked perfectly.
If you are really concerned about sharing code across multiple types of projects, it might be better to try and put your common code in a typical 'Business Layer'-type class library, instead of in view model.

Is it possible to bind to an private variables exposed property?

I don't know if this is possible or not, but here is the code to explain the question in the title:
public class LogicClass : INotifyPropertyChanged
{
private String _myText;
public String MyText
{
get{return _myText;}
set
{
_myText = value;
PropertyChanged(this, new PropertyChangedEventArgs("MyText"));
}
}
...
}
public partial class Window1: Window, INotifyPropertyChanged
{
private LogicClass _logic;
public String LogicText
{
get{return _logic.MyText;}
}
...
}
<ContentControl Name="contentControl1" >
<Binding ElementName="MainWindow" Path="LogicText"/>
</ContentControl>
Is there any way to make this work, without having to expose my LogicClass variable and make use of its implementation of INotifyPropertyChanged. I guess I want to know if this can bubble up, or anything other than having to have a redundant set in my UI code-behind (which is how I am doing this now)
Yes, you need to either handle the PropertyChanged event from _logic and then raise an equivalent PropertyChanged notification on LogicText, or you need to add a standard event on MyText, so you would have a MyTextChanged event, handle this and then raise the PropertyChanged for LogicText.
So if LogicClass if never bound to directly in the Xaml, you wouldn't need to implement INotifyPropertyChanged on LogicClass and you would do (something like) this:
public class LogicClass
{
private String _myText;
public event EventHandler MyTextChanged;
public String MyText
{
get{return _myText;}
set
{
_myText = value;
var handler = MyTextChanged;
if(handler != null){ MyTextChanged(this, EventArgs.Empty); }
}
}
...
}
public partial class Window1: Window, INotifyPropertyChanged
{
private LogicClass _logic;
public Window1()
{
_logic = ... initialised;
_logic.MyTextChanged += (s,e) => RaisePropertyChanged("LogicText");
}
public String LogicText
{
get{return _logic.MyText;}
}
...
}
If what you're asking is if you can essentially say "this property represents another property on another class, so you need to look and see if it changes" in some declarative fashion, then no, that's not possible. You can, however, mimic this behavior yourself. Just attach to the PropertyChanged event on your logic class and when the MyText property changes, raise the window's PropertyChanged event by calling OnPropertyChanged.
Note that this is almost certainly better suited to something that goes in your ViewModel, not something in the codebehind on the window.

Best / neatest way to declare RelayCommands

I've been trying to find a nice neat and succinct way to declare RelayCommands in my ViewModels.
The best I can come up with is:
public class MyViewModel
{
public ICommand StopCommand { get; private set; }
public MyViewModel()
{
StopCommand = new RelayCommand(OnStop);
}
private OnStop(object sender)
{
//hammertime
}
}
What I'd really like to do it remove the two stage declaration/construction, something like:
public class MyViewModel
{
public readonly ICommand StopCommand = new RelayCommand(OnStop);
private OnStop(object sender)
{
//hammertime
}
}
However, this fails to compile with
error CS0236: A field initializer cannot reference the non-static
field, method, or property 'MyViewModel.OnStop(object)'
It there a neater / "standard" way that people use?
I've used the first format you specified quite a bit and it works fine for me.
Also - if you're using WPF, binding doesn't work with fields anyway so even if you can get the second approach to compile, it won't hook up to your UI.
One option is to abandon commanding which has it's limitations, and use another mechanism such as Actions provided by Caliburn.Micro. Then, you just need your view model verb:
public void Save()
{
}
<Button x:Name="Save">Save</Button>
I was using something like:
public ICommand StopCommand
{
get{return new RelayCommand(OnStop);}
}

Categories