I'm currently trying to implement a ImageViewer with the possibility to move and zoom with touch inputs.
I already implemented these functionalities in a recent project in the Code Behind, but I'm struggling to do so in a View Model in MVVM.
Problem is that for my code to work I have to know how many touch inputs are recognized at the same time.
In my Code-Behind I used:
canvas.TouchesCaptured.Count()
The ViewModel shouldn't not know any Controls of the View, so passing the Canvas as a Command Parameter is not the way to go.
Beside the canvas I need the TouchEventArgs of the triggered TouchEvent to determine the position of the TouchEvent on the canvas.
Using Prism I was able to get the TouchEventArgs into the ViewModel.
<i:Interaction.Triggers>
<i:EventTrigger EventName="TouchDown">
<prism:InvokeCommandAction Command="{Binding TouchDownCommand}"
</i:EventTrigger>
</i:Interaction.Triggers>
prism:InvokeCommandAction automatically sets the EventArgs as the CommandParameter for clarification.
To determine the position of the TouchEvent on the Canvas I need the canvas and the TouchEvent.
In my Code-Behind it looked like that:
startingPoint = e.GetTouchPoint(canvas);
Anyone has a idea how I can solve this problem without violating the MVVM Pattern?
You could try writing a Blend behavior that encapsulates the Canvas event handling and exposes commands (e.g. ManipulationDelta in particular). You could even add a property to the behavior that exposes the TouchesCaptured values (during the ManipulationDelta event).
e.g.
<Canvas>
<i:Interaction.Behaviors>
<bhv:CanvasBehavior ManipulationDeltaCommand="{Binding MyViewModelCommand}" TouchPointCount="{Binding MyViewModelTouchPointCount}" />
</i:Interaction.Behaviors>
</Canvas>
Related
I am starting to use MVVM but I'm finding difficult to replicate simple things that I do with events: I have a canvas and I want to get the position of the mouse click so I did a command and the xaml is this
<Canvas x:Name="cnvLeft">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseDown">
<cmd:EventToCommand Command="{Binding CanvasClick}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
However it pass only the mouse arguments, which is not enough because i need the sender, how can i fix this?
As recommended already: register a common event handler for the mouse click event.
MVVM is not concerned with code-behind. It's absolutely fine and even necessary to use code-behind.
Code-behind files are a compiler feature i.e. language feature (partial classes). They have nothing to do with application architecture. MVVM does not care about compilers - no design pattern does.
MVVM is also not concerned with commands (or data binding or any framework concept in general). Commanding is part of the framework's infrastructure and MVVM does not care about frameworks - no design pattern does.
MVVM does not mean to use commands. Events are usually just as good. So don't force commands. Instead of using interaction behaviors to convert an input event to a command, simply handle the event directly (of course in the view).
Controls must always be handled in the View of an MVVM application. The code-behind file of a control is a partial class. It's part of the control and therefore part of the View.
Implement the user input event handler in the hosting control's code-behind. Here you must implement the Canvas related logic (UI logic).
If you want to encapsulate the logic, you can move it along with the Canvas to a new custom Control (or UserControl).
MainWindow.xaml
<Window>
<Canvas PreviewMouseDown="OnCanvasePreviewMouseDown" />
</Window>
MainWindow.xaml.cs
private void OnCanvasePreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var canvas = sender as Canvas;
Point canvasClickPosition = e.GetPosition(canvas);
}
Mouse gestures can be bound to commands using the MouseBinding InputBinding,
for example:
<Grid.InputBindings>
<MouseBinding Command="{Binding MyCommand}" Gesture="LeftClick"/>
</Grid.InputBindings>
In that example, the LeftClick gesture is used. What is the full list of gesture strings? I'm looking for a left mouse button down gesture, if it exists.
That is a MouseAction value. You can see possible values in the documentation. Mouse down is not a built-in gesture. Only various clicks and double clicks are in the enumeration.
It is possible to make your own input bindings by creating classes that extend InputBinding and InputGesture. You can reference the implementation of MouseBinding for an example. Alternatively, you can find a different way to accomplish whatever it is you are trying to do.
I'm looking for a left mouse button down gesture, if it exists.
That would be the LeftClick mouse action that you are currently using.
If you want to invoke a command when the MouseLeftButtonDown event occurs, you could do this using an interaction trigger:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown" >
<i:InvokeCommandAction Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Please refer to the following blog post for more information about this.
Handling events in an MVVM WPF application: https://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/
The EventTrigger class is included in th Expression Blend SDK which you can download from here: http://www.microsoft.com/en-us/download/details.aspx?id=10801.
Hooking into it using code behind is trivial, but I can't work out a good way of handling it using MVVM.
I'll answer my question.
You can use Interaction.Triggers.
<i:Interaction.Triggers>
<i:EventTrigger EventName="Completed">
<i:InvokeCommandAction Command="{Binding CompletedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
One method I can think at the back of my mind now is, other than putting an EventTrigger for storyboard animation, you should also put up a DataTrigger. The DataTrigger should trigger on the same Property being animated, and the trigger value should be the value when the animation completes. If this control is your own custom control (or UserControl), you can create a new dependency property HasCompletedAnimation on it, and bind it to the ViewModel. If the control is neither a custom control nor UserControl, you can try using that control's Tag property.
i'm trying to get location, when I tap on bing map control. I know how to get it, but I want to make it in ViewModel. For Tap event I use interactions.
<map:Map CopyrightVisibility="Collapsed" LogoVisibility="Collapsed">
<map:MapTileLayer>
<map:MapTileLayer.TileSources>
<customMap:OpenStreetTile />
</map:MapTileLayer.TileSources>
</map:MapTileLayer>
<map:MapLayer>
<map:Pushpin Location="{Binding Location}" />
</map:MapLayer>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding AddPushpinCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</map:Map>
I can get position from eventarguments with GetPosition function. But this function need UIElement parameter.I made it so:
var source = (e.OriginalSource as MapLayer).ParentMap;
var point = e.GetPosition(source);
Location = source.ViewportPointToLocation(point);
But Im not sure if it is good solution. Do you have any idea how to do it?
Thanks
Well, your approach certainly works and sometimes, good enough is good enough. ;) If you want to be 100% clean with your MVVM implementation, you should avoid references to the view (and thus the MapLayer class) however.
Joost van Schaik wrote a blog post that shows how to achieve this using behaviors (it's for Windows Phone 8, but I'm sure it applies to Windows Phone 7 as well): http://dotnetbyexample.blogspot.nl/2013/03/simple-reverse-geocoding-with-windows.html
Is it possible to create a command behavior using Prism's CommandBehaviorBase class for Silverlight's grid? I know that it is only intended for actual controls, so I was wondering if anyone might know if a workaround. I would like to create an attachable mouse over behavior for a grid, that executes a specific command, and ideally would like to use Prism for this approach, just can't seem to use CommandBehaviorBase for a Grid.
Thanks.
The arguably easier way to achieve this is to use Triggers. Doesn't require you to write any code, all you have to do is this:
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<si:InvokeDataCommand Command="{Binding DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
</Grid>
Here the DoSomethingCommand (defined in a ViewModel) will trigger when MouseEnter event is fired on the Grid.