Does calling View Model methods in Code Behind events break the MVVM? - c#

I wonder if that would break the MVVM pattern and, if so, why and why is it so bad?
WPF:
<Button Click="Button_Click" />
Code Behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
ViewModel.CallMethod();
}
View Model:
public void CallMethod()
{
// Some code
}
IMHO, it keeps the code behind quite simple, the view model is still agnostic about the view and code behind and a change to the view doesn't affect the business logic.
It seems to me more simple and clear than Commands or CallMethodAction.
I don't want the kind of answer "it is not how it should be done". I need a proper and logical reason of why doing so could lead to maintenance or comprehension problems.

Nope, this is perfectly fine.
It's the View's job to handle user input and interact with the ViewModel. A button click event-handler, which calls a method of the ViewModel in response, falls quite cleanly into this role.
What you have posted is clean, readable, efficient, maintainable, and fully in the spirit of the MVVM design pattern.
Now, in a more general sense, what you really want to ask is: "why choose ICommands, vs Event Handlers for MVVM?" Well, you certainly wouldn't be | the | first.

No, it doesn't break MVVM, as long as you don't introduce logic that more appropriately belongs in the viewmodel.
It has the potential to reduce the clarity, IMO, because it breaks the view into XAML and c# files that are tightly coupled and where you can't see everything going on in one place. I find it easier to have zero code behind because it means less context switching when working on the view.
It can also make it more challenging to work in an environment where your UI designer isn't a C# programmer, because then two different people are maintaining the tightly-coupled files.
Edit
Here's an example of what I mean. This is from a weekend project I did to implement Minesweeper in WPF for fun and experience. One of my biggest WPF challenges was mouse input. For anyone who hasn't wasted time on the game before, the left mouse click reveals a cell, the right mouse button toggles a flag on the cell, and the middle mouse button will (conditionally) reveal adjacent cells.
I first started by considering using System.Windows.Interactivity's EventTrigger along with InvokeCommandAction to map events to commands. This sort-of worked for the right mouse button (wasn't a true click event, but a MouseRightButtonUp) but it wouldn't work at all for the middle mouse button which had no specific actions, only the generic MouseDown/MouseUp. I briefly considered prism's variation on InvokeCommandAction which could pass the MouseButtonEventArgs to its handler, but that very much broke the MVVM concept and I quickly discarded it.
I didn't like the idea of directly putting event handlers in the code-behind because that tightly coupled the action (the mouse click) and the response (revealing cells, etc.). It also wasn't a very reusable solution - every time I wanted to handle a middle click I'd be copying, pasting, and editing.
What I settled on was this:
<i:Interaction.Triggers>
<mu:MouseTrigger MouseButton="Middle" MouseAction="Click">
<i:InvokeCommandAction Command="{Binding Path=RevealAdjacentCells}" />
</mu:MouseTrigger>
</i:Interaction.Triggers>
In this case, there's no code in the code behind. I moved it into a MouseTrigger class I created that inherits from System.Windows.Interactivity.TriggerBase which, while being view-layer code, isn't part of any specific view, but a class which any view could utilize. This handler code is as agnostic as possible as to what kind of element it's attached to - anything derived from UIElement will work.
By leveraging this approach, I gained two key things over doing this in the event handlers on the code-behind:
There's a loose coupling between the event and the action. If I had a UXD working on the UI, they could change what mouse button the command was associated to by just editing a line of XAML. For example, swapping right and middle mouse buttons is trivial and requires no .cs changes.
It's reusable on any UIElement, not tied to any particular one. I can pull this out anytime I need to solve this kind of problem in the future.
The main drawback here is that it was initially more work to set up. My MouseTrigger class is more complex than the event handlers by themselves would be (mainly around properly handling dependency properties & changes thereof). XAML can also often be rather verbose for something that would seem simple.

Related

Prism ConfirmNavigationRequest() called twice when DataContext = this

I'm using Prism and my views implement IConfirmNavigationRequest in order to enable them to perform validations and cancel the navigation if required.
My problem is that I have several views which don't use MVVM, and define DataContext = this. Doing so causes Prism to call my view's ConfirmNavigationRequest() twice, which means I ask for the user's response twice.
Basically what's going on is this:
Prism checks if the view implements IConfirmNavigationRequest and calls ConfirmNavigationRequest() on it if it does.
The user is asked whether he'd like to continue.
The user clicks OK and ConfirmNavigationRequest() returns true.
Prism checks if the viewmodel (in my case, it's the view again) implements IConfirmNavigationRequest and calls ConfirmNavigationRequest() on it if it does.
The user is asked again whether he'd like to continue.
As you can see, Prism asks my view for confirmation twice because it queries both the view and the viewmodel.
So my question is, how can I prevent this from happening or how can I detect which call is which so I can ignore one of them? I thought about investigating the continuationCallback parameter, but I don't like this solution so much since it's not unlikely it'll break in the next versions of Prism.
The best solution I got so far is the one I got from DCherubini at Prism's forum, which suggests that I won't set the view's DataContext on my UserControl, but use an inner element that will hold the view, and set the DataContext for it instead:
<UserControl>
<Grid x:Name="grid">
...
</Grid>
</UserControl>
grid.DataContext = this;
instead of
<UserControl x:Name="uc">
</UserControl>
uc.DataContext = this;
This should work, but it means I need to change each view individually. A solution that doesn't require making changes to the views would be nicer.

WPF event handling between objects

I'm pretty new to C#/WPF, coming from Obj-c background. I'm not really sure how this works in terms of object oriented design, and how different classes see each other.
So I have a large view (MainView) that has some custom plots and a datagrid on the bottom (Datagrid is in its separate xaml and has .cs file behind it). There is a custom object that gets added to the plots that when you drag it, the datagrid gets updated (by using dataGrid.ScrollIntoView). The code for the ScrollIntoView is in the xaml.cs file of the Datagrid.
To me this makes sense since the MainView has all the components and "sees" all the objects, so when the event handler of the dragWindow gets called, then the MainView asks for the DataGrid, and calls its method to update its column position. (This is the way I understand it, please feel free to correct me if I'm wrong).
Now I want to go the other way as well. So that if I update the scrollbar horizontally, then the dragwindow in the MainView will get updated. This does not make as much sense to me. I can create an event handler in the xaml.cs of the datagrid. But it does not see the dragWindow in the MainView right? So I'm not quite sure how to start implementing this functionality. Any help is always appreciated. Thanks!
Your grid control should expose an event to notify any consumers (MainView in this case) that scrolling has taken place.
public class YourGridControl
{
public event EventHandler GridScrolled;
}
MainView can then attach a handler to this event in the designer or in the code:
gridCtrl.GridScrolled += OnGridScrolled;
private void OnGridScrolled(object sender, EventArgs e)
{
// Logic here
}

How to add an event handler for events from button in C# from source view (aspx)

What is the easiest way to create code-behind (webforms) event handlers for say a button from HTML source view?
In VB.NET it is quite easy to switch to code behind page and use the object and events combo boxes along the top to select and create.
In c# those are missing (and I really don't like design view).
Make sure the Properties window is open.
Click anywhere in the element in source view.
Click the lightning symbol (events) in the Properties window.
Find the event you want to create a handler for.
Double click it.
I agree that this is less trivial with C# then with VB. My personal preference is to simply add a function with the following signature (always works):
protected void MyButtonName_Clicked(object sender, EventArgs e)
{
Button btn = (Button) sender; // remember, never null, and cast always works
... etc
}
Then, inside the code view of the HTML/ASP.NET part (aka the declarative code) you simply add:
<asp:Button runat="server" OnClick="MyButtonName_Clicked" />
I find this faster in practice then going through the several properties menus, which don't always work depending on focus and successful compile etc. You can adjust the EventArgs to whatever it is for that event, but all events work with the basic signature above. If you don't know the type, just place breakpoint on that line and hover over the e object when it breaks to find out the actual type (but most of the time you'll know it beforehand).
After a few times doing this, it becomes a second nature. If you don't like it, wait a moment for VS2010, it's been made much easier.
Note: both VB and C# never show the objects or events of elements that are placed inside naming containers (i.e., GridView, ListView). In those cases, you have to do it this way.

MVVM Madness: Commands

I like MVVM. I don't love it, but like it. Most of it makes sense. But, I keep reading articles that encourage you to write a lot of code so that you can write XAML and don't have to write any code in the code-behind.
Let me give you an example.
Recently I wanted to hookup a command in my ViewModel to a ListView MouseDoubleClickEvent. I wasn't quite sure how to do this. Fortunately, Google has answers for everything. I found the following articles:
http://blog.functionalfun.net/2008/09/hooking-up-commands-to-events-in-wpf.html
http://joyfulwpf.blogspot.com/2009/05/mvvm-invoking-command-on-attached-event.html
http://sachabarber.net/?p=514
Link
http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/
While the solutions were helpful in my understanding of commands, there were problems. Some of the aforementioned solutions rendered the WPF designer unusable because of a common hack of appending "Internal" after a dependency property; the WPF designer can't find it, but the CLR can. Some of the solutions didn't allow multiple commands to the same control. Some of the solutions didn't allow parameters.
After experimenting for a few hours I just decided to do this:
private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
ListView lv = sender as ListView;
MyViewModel vm = this.DataContext as MyViewModel;
vm.DoSomethingCommand.Execute(lv.SelectedItem);
}
So, MVVM purists, please tell me what's wrong with this? I can still Unit test my command. This seems very practical, but seems to violate the guideline of "ZOMG... you have code in your code-behind!!!!" Please share your thoughts.
Thanks in advance.
I think the fault lies in the purity requirement. Design patterns, MVVM included, are a tool in the toolbox, not an end unto themselves. If it makes more sense to break with the purity of the model for a well-considered case (and it clearly looks like you've considered this case), then break with the model.
If that works for you, and you don't believe it's an undue maintenance burden, then I'd say that nothing is wrong with what you've done. I think that you've clearly met the burden of proof for showing that this is a reasonable solution to your problem in spite of what a pure MVVM implementation might be.
(I consider this argument similar to the arguments for multiparadigm languages. While a Pure OO approach can be applied, sometimes doing things in a more functional way is more appropriate. While a Pure Functional approach can be applied, sometimes the trade offs show that OO techniques are more than worth the while.)
I agree with you that many MVVM-Command solutions are too complicated. Personally, I use a mixed approach and define my Commands in the View rather than in the ViewModel, using methods and properties from the ViewModel.
XAML:
<Window.Resources>
<RoutedCommand x:Key="LookupAddressCommand" />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource LookupAddressCommand}" x:Name="cmdLookupAddress" />
</Window.CommandBindings>
Code (View):
Private Sub cmdLookupAddress_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) Handles cmdLookupAddress.CanExecute
e.CanExecute = myViewModel.SomeProperty OrElse (myViewModel.SomeOtherProperty = 2)
End Sub
Private Sub cmdLookupAddress_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Handles cmdLookupAddress.Executed
myViewModel.LookupAddress()
End Sub
It's not pure MVVM, but it simple, it works, it does not need special MVVM-command-classes and it makes your code much easier to read for non-MVVM-experts (= my co-workers).
Although I prefer not to write code-behind when using the MVVM pattern, I think it's OK to do it as long as that code is purely related to the UI.
But this is not the case here : you're calling a view-model command from the code-behind, so it's not purely UI-related, and the relation between the view and the view-model command is not directly apparent in XAML.
I think you could easily do it in XAML, using attached command behavior. That way you can "bind" the MouseDoubleClick event to a command of your view-model :
<ListView ItemSource="{Binding Items}">
<local:CommandBehaviorCollection.Behaviors>
<local:BehaviorBinding Event="MouseDoubleClick" Action="{Binding DoSomething}" />
</local:CommandBehaviorCollection.Behaviors>
...
</ListView>
You can also easily access the selected item of the ListView without referring to it directly, using the ICollectionView interface :
private ICommand _doSomething;
public ICommand DoSomething
{
get
{
if (_doSomething == null)
{
_doSomething = new DelegateCommand(
() =>
{
ICollectionView view = CollectionViewSource.GetDefaultView(Items);
object selected = view.CurrentItem;
DoSomethingWithItem(selected);
});
}
return _doSomething;
}
}
I believe that the goal of having "No code in the code-behind" is exactly that, a goal to reach for - not something that you should take as an absolute dogma. There are appropriate places for code in the View - and this isn't necessarily a bad example of where or how code may be simpler than an alternative approach.
The advantage of the other approaches you list, including attached properties or attached events, is that they are reusable. When you hook up an event directly, then do what you did, it's very easy to end up duplicating that code throughout your application. By creating a single attached property or event to handle that wiring, you add some extra code in the plumbing - but it's code that is reusable for any ListView where you want double-click handling.
That being said, I tend to prefer using the more "purist" approach. Keeping all of the event handling out of the View may not effect the testing scenario (which you specifically address), but it does effect the overall designability and maintainability. By introducing code into your code behind, you're restricting your View to always using a ListView with the event handler wired - which does tie your View into code, and restrict the flexibility to redesign by a designer.
What #JP describes in the original question and #Heinzi mentions in is answer is a pragmatic approach to handling difficult commands. Using a tiny bit of event handling code in the code behind comes in especially handy when you need to do a little UI work before invoking the command.
Consider the classic case of the OpenFileDialog. It's much easier to use a click event on the button, display the dialog, and then send the results to a command on your ViewModel than to adopt any of the complicated messaging routines used by the MVVM toolkits.
In your XAML:
<Button DockPanel.Dock="Left" Click="AttachFilesClicked">Attach files</Button>
In your code behind:
private void AttachFilesClicked(object sender, System.Windows.RoutedEventArgs e)
{
// Configure open file dialog box
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.FileName = "Document"; // Default file name
dlg.DefaultExt = ".txt"; // Default file extension
dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension
// Show open file dialog box
bool? result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
string filename = dlg.FileName;
// Invoke the command.
MyViewModel myViewModel = (MyViewModel)DataContext;
if (myViewModel .AttachFilesCommand.CanExecute(filename))
{
noteViewModel.AttachFilesCommand.Execute(filename);
}
}
}
Computer programming is inflexible. We programmers have to be flexible to order to deal with that.
Decoupling is one of the major feature of MVVM. If suppose you want to change say view or binded model to it. How much easy it is for your application?
Take an example where View1 and View2 both share the same ViewModel. Now will you implement the code behind method for both.
Also, suppose if you need to change the viewmodel for a view on later stage your command will get fail as view model is changed and statement
MyViewModel vm = this.DataContext as MyViewModel;
will return null and hence code crash. So there comes an extra burden to change the code behind also. These kind of scenarios will arise if you do it in this way.
Of course there are many ways to achieve the same thing in programming but which one is best will lead to best approach.
Commanding is for chumps. Real men wire up their entire ui to events in codebehind.

.Net C# Design View errors

I have subclassed a Treeview and on instantiation it loads a new ImageList (and the associated Images).
Whenever I switch to the designer view, it's also trying to run this code, however the images aren't in the designer's path, so it crashes. I ended up putting in a hack to see if the current directory is "Visual Studio", then do nothing... but that's so ugly.
I find this happening for other things. If a control is trying to use objects during load/initalization that are only available while the program is running, then the Design View cannot bring up the control.
But is there a way to get around this?
I guess what I'm hoping for is having a try/catch for the Designer (only) with the ability to ignore a few errors I know will be happening (like FileNotFoundException, etc.).
Thanks
Everything that inherits from System.Windows.Forms.Control has a DesignMode property that returns a boolean indicating if you are in design mode or not. You could use this to determine when to/when not to load external resources.
Usually it is better to move the loading of these resources to an override of OnLoad as they are rarely required directly at construction. This fixes the issue you are seeing and means that only trees which get displayed at least once will perform these additional resource loading steps.
Otherwise, you can just exclude these steps during design time by checking the DesignMode property and acting accordingly.
This is a fine pattern to use if you're making a control library with a sample of images when shown in the designer or hook ins to other designer features but as a pattern for development I'm not sure it's very effective.
I would suggest shifting your "business logic" (in this case your loading of certain images into a treeview) outside of the bounds of your treeview control. In your case I would place the logic within the Load event of the form that the control is inside:
public void Load(object sender, EventArgs e)
{
string path = "c:\somePath\toAwesome\Images";
myFunkyTreeView.AddImages(path);
}
For larger apps I personally think you want to shift the logic even out of the forms themselves, but this is debatable measure as it requires additional plumbing as a trade-off for the flexibility this provides.
Thanks for pointing me in the right directioon guys.
I had tried registering to the OnLoad event, but that event is triggered when the Design View comes up, so that didn't quite work for me (am I doing something wrong?).
Anyway, I looked a bit more into the DesignMode property. It can only work for Controls, and sometimes your object may not even be a control.
So here's the answer I prefer:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) {
// design-time stuff
} else {
// run-time stuff
}
Found it here.

Categories