C#: WPF MVVM Command Binding vs. Event Callback - c#

What would be the point to using MVVM for the close action of File->Exit.
It seems like a lot of work to make a close command when you can simply create an event callback for the Click event. For something like this that doesn't have anything to do with data or business logic, I don't see the point to using MVVM approach. Why not just use the following:
xaml:
<MenuItem Header="_File" Background="WhiteSmoke">
<MenuItem Name ="Exit" Tag="Exit" Header="Exit" Click="Exit_Click"/>
</MenuItem>
Code behind:
private void Exit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}

For that case, you have a bit of an argument. If nothing else, closing the view could easily be construed as entirely view related, so a Click event handler makes sense to start with. View-related code goes in the view, not the VM.
However, I would challenge your notion that a Command is that hard to set up. Using a DelegateCommand: http://wpftutorial.net/DelegateCommand.html requires two lines of additional code:
public ICommand ExitCommand {get; private set;}
public MyViewModel()
{
ExitCommand = new DelegateCommand(ExitApp);
}
The handler is the same either way. While Exit may not need a command, in general, ICommand is the right way to go and isn't actually that hard.

You're absolutely right - if handler is pure-UI related then it doesn't break MVVM by any means (despite what some extremists say). MVVM is pattern created to decouple UI and business logic - "UI things" in code are ok.

Related

Does using the Click method in WPF break MVVM?

In a wpf interface with MVVM I have a close button and I'm using the button's Click method to close the application like so:
<Button Content="Close" Click="CloseFunction"/>
My question is, am I breaking MVVM by doing this?
Does using the Click method in WPF break MVVM?
Not necessarily as long as you don't implement any application logic in the click event handler in the code-behind of the view.
For example, consider the following implementation that simply invokes a command of the view model from the event handler:
private void CloseFunction(object sender, RoutedEventArgs e)
{
ViewModel vm = this.DataContext as ViewModel;
if (vm != null)
vm.SomeCommand?.Invoke(null);
}
It's as MVVM compliant as the following XAML markup which invokes the very same command from the very same view:
<Button Content="Close" Command="{Binding SomeCommand}" />
MVVM is not about eliminating code from the views. It's mainly about separation of concerns.

In WPF and by using Prism, how to use the MouseButtonEventArgs as a parameter of the command for a window?

I want to move a borderless windows, and before I adopt the Prism framework, I'd do it as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MouseDown += Window_MouseDown;
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
DragMove();
}
}
}
but I don't know how to implement this while using Prism in MainWindowViewModel.cs (the view model), it seems the InvokeCommandAction can pass the event argument for an element like button or so, but it doesn't work for a window in my case.
Can anyone help me on this? Thanks in advance.
I don't know how to implement this while using Prism
I don't know what this is supposed to be exactly, but I'll assume it's how to call the view model when some event on the view happens:
The cleanest option is an attached behavior. Alternatively, you can use an InvokeCommandAction variant like DevExpress' EventToCommand that supports forwarding the parameter.
Well, finally I got the event fired, but it seems this approach contradict the concept of MVVM pattern, which requires that the view model should not know anything about nor has any dependency upon any view elements.
In my case, I can add the Interaction.Triggers to the Window and pass the MouseButton to the view model by usng Prism’s InvokeCommandAction, as follows:
<Window
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<prism:InvokeCommandAction Command="{Binding WindowMouseCommand}" TriggerParameterPath="ChangedButton" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the view model:
public DelegateCommand<object> WindowMouseCommand { get; private set; }
...
WindowMouseCommand = new DelegateCommand<object>(WindowMouse);
...
private void WindowMouse(object mouseButton)
{
if (mouseButton is MouseButton m)
{
if (m == MouseButton.Left)
{
// DragMove();
}
}
}
if I want to call the .DragMove(), I need a reference of the Window... it's not the correct implementation of MVVM pattern.
So what is the best approach/practice for that?
I was suddenly enlightened when I saw this answer:
https://stackoverflow.com/a/3426183/10958770
yes, moving a window is a pure UI logic, therefore it's not necessary to move it to a ViewModel... so let me just leave it in the view.

What is the proper design for interacting with controls using MVVM where I need to do calculations based on XAML controls?

I am still a bit green in WPF. I am refactoring a sizable applications where all the work was done in the code behind. I am refactoring to use MVVM.
A bit about the application:
This application contains a single window, the single window and the pertinent pieces of the XAML are below:
<Grid>
<Border Style="{StaticResource ClipBorderStyle}" Name="ImageBorder">
<Grid Style="{StaticResource ClipGridStyle}" Name="ImageGrid">
<Image Name="KeyImage" Style="{StaticResource MainImageStyle}" Source="{Binding ImageSource}" Cursor="{Binding ImageCursor}"></Image>
<Canvas Name="KeyCanvas" Height="{Binding Source=KeyImage, Path=Height}" common:CanvasAssistant.BoundChildren="{Binding CanvasControls}"></Canvas>
</Grid>
</Border>
</Grid>
When the user clicks on the image, I drop a control onto the Canvas at the location where the user clicked. There can be many objects, and multiple control types. Currently I have a view model defined for each of the different control types and I keep an observable collection of them in a main view model. I am about half way through refactoring, and am realizing I still have a ton of work being done in the code behind, and am modifying the objects in the DataContext a lot. I am curious how I can move a lot of this out of the code behind into a more structured format (maybe into the view model, maybe another pattern). If it were simply modifying data, this would not be a problem, but in many cases I need to do transforms, and track the location on the image. The user can interact with the application using both their mouse and their keyboard (click to select an object, left arrow to move it, etc).
The Core Questions
When I have to to something like any of the following:
private Point TranslateImageCoordinatesToParentGrid(Point pointOnImage)
{
return KeyImage.TransformToVisual(ImageGrid).Transform(pointOnImage);
}
OR
private void Marker_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var Marker = (Marker)sender;
if (Marker.IsMouseCaptured)
Marker.ReleaseMouseCapture();
}
OR
private void Marker_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var Marker = (Marker)sender;
Marker.CaptureMouse();
_dataContext.SelectedObject = Marker;
Marker.Focus();
}
OR
private void controlMarker_OnMouseMove(object sender, MouseEventArgs e)
{
var controlMarker = (controlMarker)sender;
var controlDataContext = ((controlMarkerObject)controlMarker.DataContext);
if (!controlMarker.IsMouseCaptured) return;
var tt =
(TranslateTransform)
((TransformGroup)controlMarker.RenderTransform).Children.First(tr => tr is TranslateTransform);
var currentMousePos = e.GetPosition(ImageGrid);
// center mouse on controlMarker
currentMousePos -= controlDataContext.CenterOffsetFromTopLeft;
var v = _dataContext.Start - currentMousePos;
tt.X = _dataContext.Origin.X - v.X;
tt.Y = _dataContext.Origin.Y - v.Y;
var currentImagePos = TranslateImageGridCoordinatesToChildImage(currentMousePos + controlDataContext.TopLeftOffset);
controlDataContext.ImagePosition = currentImagePos;
}
Where is the appropriate place for this logic that interacts with both the view and the view model (and view models for the controls)?
Is the code behind the appropriate place for this?
Should I be using this eventing model where I defined the mouse events, or convert them to ICommands?
Is there a better/cleaner pattern to use for an application like this?
The single most important aspect of WPF that makes MVVM a great pattern to use is the data binding infrastructure. By binding properties of a view to a ViewModel, you get loose coupling between the VM and view and entirely remove the need for writing code in a ViewModel that directly updates a view. The data binding system also supports input validation, which provides a standardized way of transmitting validation errors to a view.
I didn't say that you have to honor the pattern, I just say that you do violate the MVVM pattern if you handle clicks in the code-behind of the view. That's a fact.
If you want to remove all this code from code behind I suggest to use Caliburn, or
System.Windows.Interactivity v4.0 for WPF:
Example with interactivity on your code:
<Button Content="Submit">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding SubmitCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
When you need to work directly with UI controls from the view it's perfectly fine to write that code in the code-behind of the view. As a matter of fact that is where it belongs.
Since your ViewModel is already DataContext of the view you can easily get a reference to VM from the View's code-behind (var svm = DataContext as SomeViewModel) and update VM properties, call VM methods/function or do anything else that is in the domain of the ViewModel and defined within the ViewModel.

Is accessing the ViewModel in code behind always violating the MVVM pattern?

One thing I am really not sure about is how to properly pass mouse events to the ViewModel. There is the way of binding triggers using the interactivity extension like for instance in: WPF event binding from View to ViewModel?
But this does not forward the MouseEventArgs to my knowledge, and this solution does not appear very elegant to me.
So what would be the proper solution? One way is to register an event and to handle it in the code behind, e.g.:
private void ListBox_PreviewMouseDown(object sender, System.Windows.Input.MouseEventArgs e)
{
var listbox = sender as ListBox;
if (listbox == null)
return;
var vm = listbox.DataContext as MainViewModel;
if (vm == null)
return;
// access the source of the listbox in viewmodel
double x = e.GetPosition(listbox).X;
double y = e.GetPosition(listbox).Y;
vm.Nodes.Add(new Node(x, y));
}
Here I assume that the listbox's ItemsSource is bound to the vm.Nodes property. So again the question: is it the proper way of doing it? Or is there a better one?
Good timing, I wrote some code to do exactly this about two hours ago. You can indeed pass arguments, and personally I thnk it is elegant because it allows you to fully test your user interface. MVVM Lite allows you to bind events to commands with EventToCommand, so start by adding the relevant namespaces to your control/window:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight"
Now add event triggers to the child control whose events you want to intercept:
<ItemsControl ... etc ... >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseDownCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseUp">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseUpCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=MouseMoveCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ItemsControl>
In my specific case I'm rendering a collection of items onto a canvas, hence my use of ItemsControl, but it'll work on anything including the parent window. It will also work for key strokes (e.g. KeyDown) but if your child control isn't focus-able then you'll have to add the trigger to the parent instead. In any case all that remains is to add the relevant handlers to your view model:
public class MyViewModel : ViewModelBase
{
public ICommand MouseDownCommand { get; set; }
public ICommand MouseUpCommand { get; set; }
public ICommand MouseMoveCommand { get; set; }
public ICommand KeyDownCommand { get; set; }
// I'm using a dependency injection framework which is why I'm
// doing this here, otherwise you could do it in the constructor
[InjectionMethod]
public void Init()
{
this.MouseDownCommand = new RelayCommand<MouseButtonEventArgs>(args => OnMouseDown(args));
this.MouseUpCommand = new RelayCommand<MouseButtonEventArgs>(args => OnMouseUp(args));
this.MouseMoveCommand = new RelayCommand<MouseEventArgs>(args => OnMouseMove(args));
this.KeyDownCommand = new RelayCommand<KeyEventArgs>(args => OnKeyDown(args));
}
private void OnMouseDown(MouseButtonEventArgs args)
{
// handle mouse press here
}
// OnMouseUp, OnMouseMove and OnKeyDown handlers go here
}
One last thing I will mention that is only a little bit off-topic is that often you'll need to communicate back to the code-behind e.g. when the user presses the left mouse button you might need to capture the mouse, but this can easily be accomplished with attached behaviors. The mouse capture behavior is simple enough, you just add a "MouseCaptured" boolean property to your view model, bind your attached behavior to it and have it's changed handler respond accordingly. For anything more complicated you might want to create an event inside your view model which your attached behaviour can then subscribe to. Either way, your UI is now fully unit-testable and your code-behind has been moved into generic behaviors for re-use in other classes.
I think your approach is good. Those events, that work with View, can be in your code-behind if you handlers work via ViewModel. However, there is an alternative use GalaSoft.MvvmLight (link to download), in which have EventToCommand, supports parameter PassEventArgsToCommand.
Example of using:
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<cmd:EventToCommand Command="{Binding FooCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
I think you can use both approaches. Your solution is simple, does not require the use of the any frameworks but uses code-behind, in this case it is not critical. One thing is certain, it is advisable not to keep ViewModel event handlers, use the command or store these handlers on View side.
Some new notes
I think, your way does not violate the principles of MVVM, all event handlers working with View, should be on the side of the View, the main thing - it's event handlers need to work with a ViewModel and have a dependency via an interface, but not directly with the UI.
The only principle MVVM that you break - is the mantra "no code" and this is not the main principle of MVVM. The main principles:
Split data Model of View
Application logic should not be tied to UI
Support testability code
Once the code-behind violate at least one of these principles, you must already see the alternatives to solve their problem.
Also, you can read opinions about it on this link:
WPF MVVM Code Behind

WPF: Referencing the Window from a Custom Control

I have followed the Accepted Answer's instructions from this post as regards creating a code behind file for a Resource Dictionary, and it worked...so now I can attach events to controls in the generic.xml file.
But now I want to be able to call the DragMove() method from an event in there and since there aren't any references to the window hosting the dictionary at the time, I don't know how to call this DragMove() method.
So, from a Resource Dictionary Code behind file, is there any way I can make a reference to the window that will currently be hosting that Resource Dictionary?
[Update] (Temporary Solution)
As a simple (yet stupid) workaround, I have currently done the following:
Since I can reference the Application.Current.MainWindow from the Generic.xaml.cs code-behind, I now have this in the Generic.xaml.cs:
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application.Current.MainWindow.DragMove();
}
And then I'm attaching PreviewMouseLeftButtonDown handler to each Window I have, like such:
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application.Current.MainWindow = this;
}
It, well, it works...and until someone can come up with the proper way on how to do this, It should serve me well enough.
There is no way I know of doing this. However, if you're trying to determine the Window given a specific resource, you could use a RelativeSource:
<SolidColorBrush x:Key="MyBrush" Color="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource WindowToColorConverter}"/>
And if you're doing it from code, you can use Window.GetWindow(). You just need a DependencyObject hosted in that Window.
From architectural point of view I would say you are about to break the paradigm. It might be a bad decision providing Resource Dictionary with notion of UI that consumes it and giving some logic other than providing resources.
You might want some Adapter between UI and resource dictionary, or Controller if this is really needed to wire Resource Dictionary but again you shouldn't inject any logic in a resource container...
You can access your main window through
Application.Current.MainWindow
Hope this helps

Categories

Resources