My XAML looksl ike this:
<UserControl x:Class="Dexel.Editor.Views.UI_Sketches.UiSketchCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cc="clr-namespace:Dexel.Editor.Views.CustomControls"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
mc:Ignorable="d"
Background="{DynamicResource BG}">
<Grid>
<ItemsControl ItemsSource="{Binding Rectangles}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<command:EventToCommand Command="{Binding mouseTestCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger >
</i:Interaction.Triggers>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"></Setter>
<Setter Property="Canvas.Top" Value="{Binding Y}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Transparent" Stroke="White">
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
In my ViewModel I have this code to handle the command:
public ICommand MouseTestCommand
{
get
{
if (_mouseTestCommand == null)
{
_mouseTestCommand = new CommandBase(eventArgs => mouseTest(eventArgs), null);
}
return _mouseTestCommand;
}
}
which calls this function:
private void MouseTest(object eventArgs)
{
var test = (MouseButtonEventArgs)eventArgs;
var source = (Rectangle)test.OriginalSource;
var rectangle = source.DataContext as SketchRectangleViewModel;
Selected = rectangle;
}
Is the implementation I use right now even MVVM? If so is the code I use right now bad? I'm not sure if there is a better way but I somehow have to get the information where the user clicked and recieve the DataContext if it is an rectangle.
Problems I see already:
- Somehow need to check that the recieved original source is actually a
rectangle
and not thecanvas
1 Answer 1
Looks OK to me. You do leak views to your viewmodel though. Some better approaches:
- Handle event on rectangle itself. It will allow you to bind it's
DataContext
directly to command parameter. This should eliminate the leak. - Use items control that supports selection, i.e. ListBox. Then you will be able to bind to
SelectedItem
property directly without any events.
Other minor things:
Base
postfix is normally used to indicate an abstract class. Might want to remove it from your command class, since it is clearly not abstract.I'm not sure where this whole thing with lazy initialization is coming from (it is not the first time I see it), but it makes no sense to me. What's the point in lazy initialization for commands? Just use regular readonly property:
public ICommand MouseTestCommand {get;}
And initialize it in constructor. It is cleaner and requires less code to write. Also you might want to create another constructor for your command, that passes
null
forCanExecute
by default.
-
\$\begingroup\$ Thanks for your answer. I will try to correct my code. Quick question though: If I handle my Events in the rectangle how do I get the position of my MouseClick and the DataContext to my ViewModel? \$\endgroup\$hullunist– hullunist2017年07月14日 09:12:06 +00:00Commented Jul 14, 2017 at 9:12
MouseButtonEventArgs
in view-model breaks MVVM. You need handlePreviewMouseDown
onRectangle
as said by @nikita-b passing itsDataContext
asCommandParameter
. \$\endgroup\$