I have a window with a canvas (that is smaller than the window itself). Now I want to draw a rectangle whenever I click on the canvas, and it should appear where I clicked. I got it working, but whenever I click on an existing rectangle, it seems to take the mouse coordinates relative to the upper left corner of the existing rectangle and not of the canvas itself. Below you see my XAML:
<ItemsControl ItemsSource="{Binding CanvasItems}" Grid.Column="1" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="White">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding MouseButtonDownCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="Shape">
<Setter Property="Canvas.Left" Value="{Binding ModelLeft}"/>
<Setter Property="Canvas.Top" Value="{Binding ModelTop}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
And here you can see the according command method in my ViewModel:
private void onm ouseButtonDown(object obj)
{
var mouseArgs = obj as MouseButtonEventArgs;
var point = mouseArgs.GetPosition((IInputElement)mouseArgs.Source);
ModelLeft = (int)point.X;
ModelTop = (int)point.Y;
Rectangle rect = new Rectangle();
rect.Stroke = new SolidColorBrush(Colors.Black);
rect.Fill = new SolidColorBrush(Colors.Red);
rect.Width = 200;
rect.Height = 200;
CanvasItems.Add(rect);
}
For a better uderstanding, ModelLeft
and ModelTop
are two int properties and CanvasItems
is an ObservableCollection<object>
.
Also, I'm using System.Windows.Shapes;
for the rectangles.
How can I get it working as expected, that it will always take the mouse position relative to the canvas and not an already existing shape on the canvas?
CodePudding user response:
GetPosition
returns the mouse coordinates relative to the top-left angle of the element passed in.
So rather than the Shape element hit and referred to by mouseArgs.Source
you likely just have to pass the canvas itself to mouseArgs.GetPosition.
var point = mouseArgs.GetPosition(myCanvas);
CodePudding user response:
Your could also make your rectangles "invisible" to the mouse clicks, so that the source of the event will always be your canvas.
rect.Width = 200;
rect.Height = 200;
rect.IsHitTestVisible = false;
Also, mouse clicks come in both bubbling and tunneling versions, and to catch the tunneling version, handle the PreviewMouseDown event. That way, the canvas will always have a chance to handle the event first. But, you will still have to have the canvas object as a parameter in your GetPosition call.