shifting code of code behind to viewmodel in wpf - wpf

Here is my code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (FrameworkElement fe in canvas.Children)
{
double top = (double)fe.GetValue(Canvas.TopProperty);
double left = (double)fe.GetValue(Canvas.LeftProperty);
}
}
A 'canvas' is the name of my Canvas control in XAML. The current code provide me with ability to get the location of images inside the canvas. This is implemented in a code-behind. The question is, how we get the location of images in MVVM manner(using the command to start the process)?

how we get the location of images in MVVM manner(using the command to start the process)?
Commanding is not needed, simply bind each of the needed properties of each of the canvas(es) to the VM. Then when needed extract the target canvas properties needed to acquire the image.

A good solution would be to use XAML behaviors:
<Canvas x:Name="Canvas">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding YourCommand}"
CommandParameter="{Binding ElementName=canvas}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
In this manner you will have access to your Canvas in your ViewModel and you can execute the code behind inside the command.

Related

How to use Commands to react to a ListBox

I'm trying to learn Commanding and have set up a simple wpf project to use a custom command. I have a ListBox and a Button on a Window. When the ListBox has the focus and an Item is selected, I want the Button to be enabled, otherwise it should be disabled.
I define a CustomCommand in a separate CustomCommands class:
Public Shared ReceivedFocus As New RoutedCommand
and in my Window I set it up as follows:
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
and use the command for the ListBox as follows:
<ListBox
x:Name="lstInactive">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="GotFocus">
<i:InvokeCommandAction
Command="{x:Static local:CustomCommands.ReceivedFocus}"
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
and, finally, the CanActivate routine is:
Private Sub CanActivate(sender As Object, e As CanExecuteRoutedEventArgs)
If lstInactive.SelectedIndex >= 0 Then
e.CanExecute = True
Else
e.CanExecute = False
End If
End Sub
This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button. Should I ignore the CanExecute value in the CanActivate routine and instead just set the Enabled property of the Button? If so, what is the value of the CanExecute paramter of the CanExecuteRoutedEventArgs?
A second problem is that the GotFocus event is not firing until I select an item in the ListBox a second time.
Or maybe I don't have a grasp on Commanding at all and this is not the right approach. This small project is not important in itself, it is intended to make sure I understand Commanding after reading numerous articles about it before I start to use Commands in "real" projects. Sadly, at this stage it is clear I don't.
This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button.
Bind its Command property to the same command:
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
The Button should then be enabled or disabled based on the value that you set the CanExecute property to in your CanActivate event handler.
You probably also want to listen to the SelectionChanged event. This works as expected:
<StackPanel>
<StackPanel.CommandBindings>
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
</StackPanel.CommandBindings>
<ListBox x:Name="lstInactive">
<ListBoxItem>first</ListBoxItem>
<ListBoxItem>second</ListBoxItem>
<ListBoxItem>third</ListBoxItem>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{x:Static local:CustomCommands.ReceivedFocus}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CanActivate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = lstInactive.SelectedIndex >= 0;
}
private void ChangeSelection(object sender, ExecutedRoutedEventArgs e)
{
}
}

Wpf Mouse event set on canvas, but targets child object

I have a Canvas that contains a Rectangle. On that canvas, I bind a mousedown event to a command on the ViewModel. In that command, I am being passed the MouseEventArgs, but there the Target element is either the Canvas or the Rectangle. Where can I find in the MouseEventArgs the Canvas this event was fired from?
My code is more or less:
<Canvas Background="White">
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:InteractiveCommand Command="{Binding CmdMouseLeftButtonDown}"/>
</i:EventTrigger>
<Rectangle Width="50" Height="50" />
</Canvas>
And in the ViewModel:
ICommand CmdMouseLeftButtonDown => new DelegateCommand<MouseEventArgs>(e =>
{
e.??? // <= Where do I find the Canvas here, whether I click on the Rectangle or Canvas?
}
Please do not answer with some hackish solution like e.MouseDevice.Target.Parent. This needs to work however complicated the element in the canvas is. It could contain another canvas for instance.
A view model is not supposed to have a reference to a UI element such as a Canvas or a Rectangle at all in the first place. This effectively breaks the MVVM pattern and that's why it makes no sense to pass the sender argument to the command.
You might as well get rid of the EventTrigger and invoke the command programmatically from the code-behind of the view:
<Canvas Background="White" MouseLeftButtonDown="Canvas_MouseLeftButtonDown">
<Rectangle Width="50" Height="50" Fill="Red" />
</Canvas>
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var yourViewModel vm = DataContext as YourClass;
vm.CmdMouseLeftButtonDown.Execute(sender as Canvas); //<-- pass the Canvas as a command argument or create a new command argument type that holds a reference to the Canvas
}
This is certainly not any worse than your current approach as far as the MVVM pattern is concerned. You are still invoking the very same command from the very same view and MVVM is not about eliminating code. It is about separation of concerns.
Your MouseEventArgs.Source will reference to the Canvas in any case but the MouseEventArgs.OriginalSource will referece to the Rectange if you have clicked on its area. It will be the control determined by pure hit testing.
Set <Canvas Background="Transparent" ... />
as answered in the following question by #Rob Fonseca-Ensor:
WPF: Canvas mouse events not firing on empty space

wpf xaml resource dictionary add custom property

I want to use the following xaml code for navigation in some pages:
<Button Content="Go to page2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type Page}, Mode=FindAncestor}}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
So I´m wondering if there is a possibility to outsource the interaction part into a style (in a resource dictionary) and add a custom property like "NavigationUri" where you can directly declare the page to navigate to.
Another idea (which would probably be the better approach) is to create a custom control and inherit from button class.
Anywhere I would prefer a more compact and lean way without code behind.
Please let me know, which is the more suitable solution and how to implement it.
Although there are various simple techniques to make our Behavior Xaml as static resource. But, we need a custom behavior, as we are using a parameter in the form of Page name to navigate to. This variable demands programming.
So, I came up with
a. Custom behavior(NavigationBehavior), and
b. Button subclassing(NavigationButton)
NavigationBehavior
using System;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApplication1.Navigation
{
public class NavigationBehavior:Behavior<NavigationButton>
{
protected override void OnAttached()
{
AssociatedObject.Click += AssociatedObject_Click;
base.OnAttached();
}
void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
{
((Page)AssociatedObject.DataContext).NavigationService.Source = new Uri(AssociatedObject.DestinationUri, UriKind.Relative);
}
}
}
NavigationButton
namespace WpfApplication1.Navigation
{
public class NavigationButton : Button
{
NavigationBehavior behavior = new NavigationBehavior();
public NavigationButton()
{
behavior.Attach(this);
}
public string DestinationUri { get; set; }
}
}
Usage :
<nav:NavigationButton Content="Navigate to Page2" DestinationUri="/Navigation/Page2.xaml" />
Important Note
We are using DataContext property in our behavior to get access to the containing page. So, set this.DataContext = this; in the constructor of your all pages.
One can try using a common base class / interface to avoid this.
you cannot have interaction triggers extracted but there is a workaround to get this done
you can create a Button in resources with interaction logic in it, and then where needed you can have a content control with content set to your resource.
something like this --
<Window.Resources>
<Button x:Key="MyButton"
Content="Go to page2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type Page}, Mode=FindAncestor}}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Window.Resources>
<Grid>
<ContentControl Name="MyLocalButton" Content="{StaticResource MyButton}" />
</Grid>
You should put the navigation logic in a command in each page's view model and bind the buttons' Command properties to those commands, or else give the view models each a NextPage property and bind to that.
And create separate buttons in the views. Define a Style in the resource dictionary to make them all look the same.
Defining a Button as a resource is a bad idea. Among other things, there's only one instance of it and it can have only one visual parent, so when you add it to one view it'll vanish from the last. And you run into ugly problems like this one. You're working against XAML, and XAML is already hard enough when you're working with it.

How to trigger ViewModel command for a specific button events

How can a command on a ViewModel be invoked by a specific event of a button, such as MouseDoubleClick?
You can use the EventTrigger in the System.Windows.Interactivity namespace, which is part of the so-called Prism framework. If you're just getting started with MVVM, don't care too much for Prism by now, but keep it in mind for later. Anyway, you can steel the EventTrigger
It works like this:
Reference the assembly System.Windows.Interactivity.dll
In XAML, reference the namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your Button or any other control, add a EventTrigger like this:
<Button Content="Button">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding CommandToBindTo}"
CommandParameter="{Binding CommandParameterToBindTo}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
This way, you bind your event to a Command on your DataContext.
Remark
To clarify the usage, here's a kind of real life example including the ViewModel. The fictional requirement is to allow the user to select an item in a list and then perform a command which takes the selected item as a parameter:
<ListBox x:Name="ItemsList" ItemsSource="{Binding Items}" />
<Button Content="Do something with selected item">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoSomethingCommand}"
CommandParameter="{Binding SelectedItem,
ElementName=ItemsList}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
And that would be the ViewModel. Note how the parameter to the command is used, in the example with a generic version of a DelegateCommand object as you get it in every MVVM framework (sometimes RelayCommand). This class takes the type of the required parameter as a generic parameter (here ItemViewModel) and requires a method which takes an according parameter (here ExecuteDoSomethingWithItem(ItemViewModel ...)). The rest is WPF magic: The oject to which the CommandParameter property is bound in your XAML will be passed through as the parameter in your Execute(...) function.
public class ViewModel
{
ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand DoSomethingCommand
{
get
{
return _doSomethingCommand ??
(_doSomethingCommand = new DelegateCommand<ItemViewModel>(ExecuteDoSomethingWithItem));
}
}
private DelegateCommand<ItemViewModel> _doSomethingCommand;
private void ExecuteDoSomethingWithItem(ItemViewModel itemToDoSomethingWith)
{
// Do something
}
public ViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
// Fill the collection
}
}
Have fun with learning MVVM, it's worth it.
you can use attached command behaviors
=> http://geekswithblogs.net/HouseOfBilz/archive/2009/08/21/adventures-in-mvvm-ndash-generalized-command-behavior-attachments.aspx
You need to do a lot of pluming yourself if you going to use Command and Event Binding from out of the box WPF. You can gain a lot of just using existing framework such as MVVM Light Toolkit, or Cliburn Micro that already provide command and even binding.

Silverlight 4 EventTrigger Handled

I have two nested Grid (FrameworkElement) items in my application.
<UserControl xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<Grid x:name="OuterGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction x:Name="TheOuterCommand" Command="{Binding OuterCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid x:name="InnerGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction x:Name="TheInnerCommand" Command="{Binding InnerCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</Grid>
</UserControl>
Each of the InvokeCommands is attached to a DelegateCommand (from the Prism libraries) in the viewmodel.
OuterCommand = new DelegateCommand(OuterCommandMethod, e => true);
InnerCommand = new DelegateCommand(InnerCommandMethod, e => true);
At the moment, the EventTrigger on InnerGrid also triggers the event on the OuterGrid due to the MouseLeftButtonEvent not being handled at the InnerGrid level.
Is there a way I can notify the EventTrigger that it is handled and it should not bubble up to the OuterGrid?
At the moment, all I can think to do is have a wrapper FrameworkElement around the InnerGrid that uses an event on the XAML code-behind to set the event to handled. Does anyone have any other ideas?
---- Edit ----
In the end, I have included MVVM Light in my application and replaced InvokeCommandAction with RelayCommand. This is now working as I intended. I'll mark Bryant's answer as the winner for giving me the suggestion.
We have extended EventTrigger by adding dependency property called IsInner and then we always set a static flag in the inner EventTrigger. The outer EventTrigger unsets the flag and returns if the flag was set. That is extremely easy to write and works well.
Your best bet would be to pass the event args to the Command and then mark the event handled using the event args. You can do this by following this example here.

Resources