WPF MVVM Commands: Multiple Command Parameters - c#

Perhaps my problem is more architectural than functional, but I am trying to bind a TextBox to a command, and in that command, i'd like to pass multiple parameters (i.e. a custom object). Not sure how to do this in declarative fashion (xaml).
ViewA (sits on top of ViewModelA)
it has a TextBox, which is bound to CommandX
ViewB (sits on top of ViewModelB) (this is in fact another user control within the same Window as ViewA)
When commandX fires, ViewModelB needs to execute some method. ViewModelB needs to be updated with multiple properties before it can execute that method.
My question is, how can i structure my command/architecture, so that ViewModelB has sufficient information to execute its method.
side note: ViewModelA has all the necessary info for ViewModelB to execute its method. but, i don't want to get it from there, because later i'd want CommandX to be executed from different views
update
it appears that i could set CommandParameter property separately, which could likely be bound to a complex type in ViewModelA. That should be sufficient to suff all necessary properties into it.
awesome
this worked! here is what my parmeter property looks like on ViewA
public ExecuteQueryCommandParameters ExecuteQueryParameters {
get {
var p = new ExecuteQueryCommandParameters();
p.AllColumns = ColumnsMaster;
p.DatabaseName = SelectedDatabase;
p.ServerName = SelectedServer;
p.TopRows = 20;
p.ViewModelName = "MainDataView";
return p;
}
}
and the button implementation
<Button Command="{Binding ExecuteQuery}" CommandParameter="{Binding ExecuteQueryParameters}">Top 20</Button>
update
there is a small road block with this solution. the commandparameter is bound to the property. but it retrieves it at load time, not at the time of command being executed. I need it to happen at command execution time, so that all the properties have latest values. any ideas on how to achieve that ?

Here's how I would do it:
For this explanation, I'll assume all of the necessary information is in one class, called "MyCustomCommandParameters".
I'll also assume that your XAML sets the main parent control's data binding to ViewModelA.
Have ViewModelA expose a property of type MyCustomCommandParameters.
Then, on ViewB, create a DependencyProperty of a type MyCustomCommandParameters.
The handlers for this property would pass this information to ViewModelB.
In the XAML, bind the ViewB's "MyCustomCommandParameters" value to ViewModelA's "MyCustomCommandParameters" property.
It's a bit of one-off plumbing work, but then you would meet your goal of having the information passed from ViewModelA to ViewModelB by XAML binding, and the two models would not know about one another.

Related

Prism.WPF: Change MainWindow Window.Effect on Navigation

The application is a Prism Application in WPF using C#.
I am attempting to assign a BlurEffect to the Window.Effect property when a button is clicked on the navigation menu.
I have the Window.Effect bound to a property in my viewmodel.
<Window ... other properties ..
Effect = {Binding Fuzzy}>
and the Fuzzy property in the ViewModel.
private Effect _fuzzy;
public Effect Fuzzy { get => _fuzzy; set => SetProperty(ref _fuzzy, value); }
What I am attempting to implement is that when a button is clicked on the navigation menu that the window will blur while a UserControl is loading.
I have tried to implement the change in the Navigate method.
private void Navigate(string viewName)
{
PerformBlur();
_regionManager.RequestNavigate("ContentRegion", viewName);
}
private void PerformBlur()
{
BlurEffect blur = new BlurEffect();
blur.Radius = 4;
var ef = blur;
_fuzzy = ef; //I've tried Fuzzy = ef too
}
But that doesn't work.
I need to make the change to the window effect before it attempts to navigate, and I haven't been able to figure out how to make that happen. I have a feeling that the easiest way to do this would be to use a click event rather than a command, and then call the command in the viewmodel from the codebehind. However, that doesn't seem to be the proper implementation when using MVVM. Any suggestions on how to implement this functionality would be greatly appreciated.
(Bonus points if you can tell me how to animate the blur. lol)
I have a feeling that the easiest way to do this would be to use a click event rather than a command, and then call the command in the viewmodel from the codebehind. However, that doesn't seem to be the proper implementation when using MVVM.
Invoking the command programmatically from the code-behind of the view is not any worse than invoking it from the XAML markup of the very same view as far as MVVM is concerned.
MVVM is not about eliminating code from the views. It's about separation of concerns. You can implement an entire view programmtically in a C# without using XAML at all and still be fully compliant with MVVM.
Trying to do fairly complex stuff in XAML just because you possible can is generally considered as an antipattern. Remember that XAML is a markup language. C# is a much more expressive and concise language so if you can solve your issue by writing some code, then this is most probably what you should do.
Taking a look at the prism source code, I can see iregionmanager is full of abstracted interfaces.
( Wow. I don't know why it still surprises me but prism is very complicated ).
https://github.com/PrismLibrary/Prism/blob/master/src/Wpf/Prism.Wpf/Regions/IRegion.cs
That includes IRegionNavigationService
https://github.com/PrismLibrary/Prism/blob/master/src/Wpf/Prism.Wpf/Regions/IRegionNavigationService.cs
You could therefore override pretty much any functionality you like, if you wanted to.
Notice though, the two events :
/// <summary>
/// Raised when the region is about to be navigated to content.
/// </summary>
event EventHandler<RegionNavigationEventArgs> Navigating;
/// <summary>
/// Raised when the region is navigated to content.
/// </summary>
event EventHandler<RegionNavigationEventArgs> Navigated;
Looks to me like "all" you need is a reference to your region navigation service in the view.
Handle those two events to set blur then remove blur.
You could then do navigation in code behind or viewmodel. Whichever suits.
If you wanted to decouple viewmodel from view, you could use the eventaggregator.
There is another option though.
You don't explain exactly what you have there. So let's imagine and consider a better way to do this.
Say you have a set content of a set control you're always navigating. That's being switched out as you navigate for a new view whose datacontext is a new viewmodel.
You could bind an attached property from the window to the datacontext of that.
In that property you can have a change callback.
In a base viewmodel you could add an IsLoaded bool property which is initialy false.
When your dependency property callback returns null or false then you blur.
You change the viewmodel property to false in the current viewmodel when you start to navigate. The window blurs. The content is switched out and you get a new viewmodel. Once navigation completes you set that ILoaded true. You callback un blurs the window.

If setting a DataContext within a constructor, does it matter if I set it before or after the call to InitializeComponent()?

I have a WPF window that takes a few parameters in it's constructor. I then use these constructors to setup the state of the window. Part of that constructor process is instantiating my view model class that is then set as the windows DataContext.
My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?
I ask because if I set it beforehand, I then need to manually launch code that is executed after the window has been initialized, because certain events should fire when the DataContext is assigned, or re-assigned.
It is my assumption that there shouldn't be any binding issues if I set the DataContext after the call to InitializeComponent() but I wanted to ask for advice on the matter before making the final call to wire up my window this way. Could I be missing something the could come back to haunt me if I set my DataContext after the call to InitializeComponent()?
My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?
It shouldn't matter unless you rely on some bindings that are established during the call to the InitializeComponent(), like ElementName bindings:
Cannot bind ItemsSource to ElementName
The InitializeComponent() method itself locates a URI to the compiled XAML file and passes it to a LoadComponent() method that parses the BAML, i.e. the compiled XAML, and creates instances of the elements that you have defined in your XAML markup:
What is the connection between .xaml and .xaml.cs files
Simply setting the DataContext property of the window to an instance of a view model class that elements in the view bind to might as well be done after the call to the InitializeComponent() method. These bindings aren't resolved before the constructor returns anyway.
Here's my addition to #mm8's answer:
Usually it does not matter, but set DataContext after InitializeComponents. When DataContextChanged event is called, you naturally expect, that components are already initialized.
Also it's good to know whether the components can be initialized without DataContext and separate possible initialization issues from binding issues. If you set DataContext before InitializeComponents, the binding issues may result in an exception in InitializeComponents.
Make you ViewModel constructor very fast. Don't do any DB calls or any I/O calls, etc. You want to display the UI as soon as possible.
Make sure your ViewModel constructor never throws an exception. Parameter validations is OK, but just for debugging purposes. It should never happen in production.
If you need to load data into viewmodel, create separate async method called e.g. Activate(), which you will call from View's Loaded or OnNavigatedTo event.
Additionally, if you subscribe to some events in ViewModel, you should unsubscribe. Ideal place for subscription is Activate method, resp Deactivate to unsubscribe. If you subscribe in ViewModel's ctor, it may happen that Activate/Deactivate will never be called and you introduced memory leak.
If you feel your bindings are slowing the UI, try to use {Binding IsAsync=True}, resp x:Bind, or try to use codebehind to set the properties in worst case.
Different from what you ask, I suggest two changes:
Set the DataContext of an inner element and not on the Window / UserControl itself.
Set the DataContext on Loaded instead of the constructor.
These points are more obvious when looking at a UserControl, which will probably be embedded at multiple points, but remember that a Window can be created by explicit startup code instead of some App.StartupUri.
Regarding the first point, consider the OOP design basics. Forget about WPF / XAML specifics and remember that you derive from a Window class and create a subclass of it. The contract of this class includes a public get/set property named DataContext which accepts any kind of object. So you should at least think about, how bad you will screw up, if someone is replacing your DataContext from the outside. When you instead set the DataContext on the next-inner FrameworkElement inside the window, it is hosted in an environment that is owned by the window.
Setting the DataContext on Loaded is working for me, while I ran into problems with constructor time setting. However, I can't actually recall the details of it, maybe it was related to the visual designer (that I'm not using anymore). For other controls it is easier to explain: constructor time initialization sucks when hosted in a virtualizing panel, also property initializers (new MyControl { Prop = Value }, XAML property assignments, ...) are not handled by time the constructor runs, so objects tend to be in a different state than how they are presented later.

MVVM when to create a viewmodel for a control?

Or should I only create viewmodels for the domain data being represented? While reading on MVVM, I came across this:
"The ViewModel is responsible for these tasks. The term means "Model of a View", and can be thought of as abstraction of the view, but it also provides a specialization of the Model that the View can use for data-binding. In this latter role the ViewModel contains data-transformers that convert Model types into View types, and it contains Commands the View can use to interact with the Model. "
http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx
If the viewmodel is a model of the view, then doesn't it make sense to put properties of the view in the viewmodel rather than on the code behind of the view itself?
I guess in making a custom control I just have a hard time deciding when I should just add a property to the control's code behind and when it is worthwhile to make a viewmodel for the control to represent it. Honestly I kind of feel that moving all of the control's view related properties to the viewmodel would clean up the code behind of the control leaving only the control logic.
However, if I were to change things like this, then at times when an item needs properties from the control itself I can no longer use {Binding ElementName = control, Path=property} and have to instead get the data context of the parent (because the current datacontext would be on the individual subitem of the observable collection.
Basically I was considering whether I should move properties from Class GraphViewer into a GraphViewerViewModel and then just bind to it.
Code is worth a million words so:
public class GraphViewerViewModel :DependencyObject
{
private const int DEFAULT_PEN_WIDTH = 2;
private const int DEFAULT_GRAPH_HEIGHT = 25;
public SignalDataViewModel _SignalDataViewModel
{
get;
set;
}
public PreferencesViewModel _PreferencesViewModel
{
get;
set;
}
}
Meanwhile
public class SignalDataViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ObservableCollection<SignalViewModel> _signals;
public ObservableCollection<SignalViewModel> Signals
{
get
{
return _signals;
}
private set
{
_signals = value;
}
}
ObservableCollection<SignalViewModel> _AddedSignals;
public ObservableCollection<SignalViewModel> AddedSignals
{
get
{
return _AddedSignals;
}
private set
{
_AddedSignals = value;
}
}
it is a pain to type:
PenWidth="{Binding RelativeSource = {RelativeSource AncestorType={x:Type DaedalusGraphViewer:GraphViewer}},
Path = _GraphViewerViewModel._SignalDataViewModel._AxisDivisionUnit.GraphPenWidth, Mode=OneWay}"
and I'm wondering if it is worthwhile to make the change or whether I'm misunderstanding what a view model should be used for in mvvm.
I guess in making a custom control I just have a hard time deciding when I should just add a property to the control's code behind and when it is worthwhile to make a viewmodel for the control to represent it. Honestly I kind of feel that moving all of the control's view related properties to the viewmodel would clean up the code behind of the control leaving only the control logic.
In general, a custom control is 100% View layer code. As such, it really falls outside of MVVM entirely.
The main goal when making a custom control to be used within an application being designed with MVVM is to make sure that you design and build the custom control in a way that it is fully compatible with data binding. This will allow it to be used within your View layer of your application exactly like other controls.
As such, this pretty much guarantees that you'll have code behind, since implementing Dependency Properties really requires code behind. You also don't want to set the DataContext of a custom control within the control (since you want to inherit the data context of the user control or window using the control).
Basically I was considering whether I should move properties from Class GraphViewer into a GraphViewerViewModel and then just bind to it.
If the types are specific to your domain, then this is really typically more of a UserControl being used by your application. In that case, creating a ViewModel and just binding is likely good.
If this is, on the other hand, a true custom control that's made to be completely general purpose (ie: usable by anybody in any application), then keeping it as a "pure view" custom control typically means that you 1) won't take a dependency on any ViewModels or domain specific objects, and 2) not set the data context (which means no view model).

Refresh DataContext for Views in WPF?

I am working with a WPF application and using my own architecture that strongly resembles a M-V-VM /MVC. I have controllers for each of the views, and I have ViewModels that are bound to the Views.
For Example, I have a ToolBarView that has a corresponding ToolBarViewModel, and ToolBar Controller.
I am using notifications to update all the views so that they do not need to reference each other, but that is not relevant for my question.
Each of the Views is listening for an event to trigger in their controller when the model has been updated. In the ToolBarView this looks like the following.
/*In Constructor in ToolbarView*/
Controller.Updated += UpdateView
/*Event Handler in ToolbarView*/
private void Updateview(object sender,EventArgs e)
{
DataContext = Controller.Model;
//Other Updating if needed
}
If not obvious, what the above code is doing is saying that when the Controller fires the Updated event, to invoke the UpdateView(object sender,EventArgs e).
The problem that I am experiencing is that the first time that the UpdateView() is invoked, everything is working fine, but when it is invoked the second time, the DataContext seems to still be bound to the original Controller.Model.
It seems almost as if I have to release the DataContext, or refresh it in order for it to be bound to the Model every time.
The Controller is performing operations on the Model, and therefor when the UpdateView() is invoked, it needs to display the newly assigned values on that model.
Is there some way I need to refresh the DataContext, or is there a different way I need to do this?
If you are assigning the DataContext to the same instance of your model, the in effect it won't "change". WPF expects objects to notify it when their state changes, either through DependencyProperty properties or by implementing INotifyPropertyChanged.
So if you do something like:
MyObject o = new MyObject();
o.MyString = "One";
this.DataContext = o;
// ... some time later ...
o.MyString = "Two";
this.DataContext = o;
Assuming MyObject doesn't implement INotifyPropertyChanged, then the second assignment to DataContext is effectively worthless. You would have to set DataContext to null, then assign your object again to have it "refresh".
But your best bet in general would be to implement INotifyPropertyChanged. This would end up being much more efficient, as only the property that actually change would need to be updated in the UI.

Having trouble deciding how to wire up a UserControl with MVVM

I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.
My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.
However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.
Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?
I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.
UPDATE
Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.
If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.
Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.
You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.
Am new to MVVM myself but here's a possible solution:
Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.
if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.
so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.
You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.
As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
For the RelayCommand check out Josh Smiths MSDN article.
In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.
If you can't change your UC at all then you could derive a new class from it and override certain behaviour.
Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.
If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).
If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.
(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)
Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.
Hope that helps!

Categories

Resources