How to enable event bubling from nested canvas - c#

I am drawing shapes on Canvas. I am using mouse events to do some action.
I draw rectangle with right mouse click, unfortunately when I release mouse on rectangle, no MouseUp event is fired.
ItemsControl
<ItemsControl ItemsSource="{Binding Shapes}" Width="{Binding ActualWidth, ElementName=img}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas" Background="Transparent" Cursor="Cross" Panel.ZIndex="0">
<i:EventTrigger EventName="MouseUp">
<i:CallMethodAction MethodName="MouseReleasedHandler" TargetObject="{Binding}" IsEnabled="{Binding IsDrawingEnabled}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
DataTemple for Rectangle
<DataTemplate DataType="{x:Type over:RectangleROI}">
<Canvas>
<Rectangle Canvas.Top="{Binding Y}" Canvas.Left="{Binding X}"
Fill="{Binding Fill}" Stroke="{Binding Stroke}"
Width="{Binding Width}" Height="{Binding Height}"
/>
</Canvas>
</DataTemplate>
My research:
I've used Snoop to sniff event.
I could see event is fired on Rectangle item
I could also see event is bubbling up (Rectangle -> Canvas -> ContentPresenter)
In all cases event wasn't marked as handled
UI structure snippet from Snoop (sorry for image quality):
What I've tried
I saw some post which adviced to set:
Canvas background to transparent
Explicitly set Width & Height
I've tried it on both Canvases
My current solution
I've I attached EventTrigger to Rectangle
<i:EventTrigger EventName="MouseUp" >
<i:CallMethodAction MethodName="MouseReleasedHandler" TargetObject="{Binding DataContext , RelativeSource={RelativeSource AncestorType=UserControl}}" />
</i:EventTrigger>
I don't like this solution because I think there has to be better way.
My question
What I should do to enable event bubbling from Content presenter up to second (main) Canvas (ItemsControlItemPanel)

Related

WPF MVVM - Send dynamic parameter to parent ViewModel

I'm creating a WPF application that will create an arbitrary number of circles in a Canvas. The user should be able to click a specific circle to "select" it, causing the circle to grow larger. Other GUI elements will also update with other data in the ViewModel for that circle.
The trick is that I need to be able to unselect all other circles when one is selected, so the circle VM itself can't handle the select method. I need the parent VM to be able to handle it - and I need to pass an ID of the specific circle that was clicked as a parameter. (I have this ID property created already in the circle VM.)
Here's what I've got so far:
<Window x:Class="MyApp.MainWindow"
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:local="clr-namespace:MyApp"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Window.DataContext>
<local:ClientManagerVM/>
</Window.DataContext>
<Grid>
<ScrollViewer x:Name="Scroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="Black" Grid.RowSpan="1">
<Canvas x:Name="DrawingCanvas" Height="{Binding circlesVM.Height, Mode=OneWay}" Width="{Binding circlesVM.Width, Mode=OneWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Black">
<ItemsControl ItemsSource="{Binding circlesVM.circleList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding x}"/>
<Setter Property="Canvas.Top" Value="{Binding y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Fill="White" Width="{Binding size, Mode=OneWay}" Height="{Binding size, Mode=OneWay}">
<Ellipse.InputBindings>
<MouseBinding Gesture="LeftClick" />
</Ellipse.InputBindings>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</ScrollViewer>
</Grid>
</Window>
How can I set up the MouseBinding inside my Ellipse to trigger a command in the ClientManagerVM with a parameter value of the ID of the specific Ellipse?
Something like this where YourCommand is an ICommand property of the "circlesVM" that accepts a circle as a command parameter:
<Ellipse Fill="White" Width="{Binding size, Mode=OneWay}" Height="{Binding size, Mode=OneWay}">
<Ellipse.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding DataContext.circlesVM.YourCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}" />
</Ellipse.InputBindings>
</Ellipse>
You should read this though before asking another question though: https://stackoverflow.com/help/mcve

WPF ItemsControl stretch FontSize of ButtonContent

I have a problem autosizing the (Text)Content of my buttons.
This is the style of my Buttons. Nothing special but notice the TextWrapping property!
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Text="{TemplateBinding Content}" TextWrapping="Wrap"/>
</Setter.Value>
</Setter>
</Style>
I defined a ItemTemplate based on my Buttons:
<DataTemplate x:Key="MyItemTemplate">
<Button Content="{Binding Content}" Command="{Binding Command}" FontWeight="Bold" FontSize="{Binding FontSize}"/>
</DataTemplate>
I am showing a list of items in with a ItemsControl with 3 columns:
<ItemsControl ItemsSource="{Binding MyItems}" ItemTemplate="{StaticResource MyItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Until this point, this is nothing special. But now, I want the FontSize of all my Buttons stretched to maximum possible, the item with the largest content allows (all items should have the same FontSize in the end).
I found a solution to calculate the needed size of a text. So I could calculate the maximum FontSize of a Button. But therefore I need the actual size of one of my Buttons (all have the same size).
I have not really an idea how to get the size and solve my problem. Would be great if anybody has an idea - or a totally different approach.
You can use ActualWidth of your Button as CommandParameter to get the width like this :
<UserControl ....
.................>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Command}" CommandParameter="{Binding ActualWidth,ElementName=button}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataTemplate x:Key="MyItemTemplate">
<Button Content="{Binding Content}" x:Name="button"
Command="{Binding Command}"
FontWeight="Bold" FontSize="{Binding FontSize}"/>
</DataTemplate>
your Command should then be like this :
public ICommand Command => new DelegateCommand<object>(ExecuteCommand);
private void ExecuteCommand(object obj)
{
double width = (double)obj;
}

Is there a better way to implement a WrapPanel "like" control as a ListView's PanelTemplate without losing virtualisation

I currently have a ListView which has has a datatemplate so that it displays all .jpg files in a given folder. Currently I have it set up as a wrap panel and I found that when the jpgs in this folder get large the program slows down. I'm guessing due to the loss of visualization. I have tried using This Example of a Visualizing WrapPanel - The reason this doesn't work for me is because I still get the obscure scroll-bar behavior, even though each item is the same height / width. I was just wondering if there was a better way to implement this. I wouldn't be bothered if I had to fix the amount of columns, e.g. a 3 x 3 displayed at one time.
My current listview looks like this:
<ListView ItemsSource="{Binding FilteredPhotoFiles}" SelectedItem="{Binding SelectedPhotoVM.SelectedPhoto}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType={x:Type ScrollContentPresenter}}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="2,4,2,4">
<Grid.Background>
<SolidColorBrush Color="LightGray" Opacity="0.5"/>
</Grid.Background>
<Image Source="{Binding PhotoFileInfo.FullName}" Width="300" Height="170" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I was just wondering if there was a better way of doing this so that I do not lose perfomace, maybe some kind of GridView?

Canvas that allows events for overlapped elements

I am looking for a way in WPF to recognize click events on all ZIndex levels. So this means I would like to recoginze a click also if another element (on the same canvas - only different ZIndex) is clicked.
What I want to achive with this functionality is that I have some ListBoxes on the same canvas and I would like to get the SelectionChanged event also when they overlap.
Edit: Added some code.
<DataTemplate DataType="{x:Type my:type}">
<ListBox SelectionMode="Extended" Background="Transparent"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Elements}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<ei:CallMethodAction TargetObject="{Binding ElementName=control, Path=DataContext}"
MethodName="SelectionChanged" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="500" IsItemsHost="True"
Height="500"
VerticalAlignment="Top" HorizontalAlignment="Left" Background="Transparent">
</Canvas >
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<DataTemplate>
In this example the SelectionChanged handler is only visible for one object (depending on ZIndex).

Grid "Tap" or "Click" event through a data template?

I have already tried searching the web for using commands in Windows Phone, but I can't find a scenario that fits my problem. I have a DataTemplate which creates some grids. For these grids, I want them to do something when their Click event is triggered. However, the Grid element doesn't have a Command property.
I don't nescessarily need to do it through commands, but I thought that was the way to go.
Here's what I want to do.
<ItemsControl VerticalAlignment="Top" Visibility="Collapsed" x:Name="RadioList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="12" Tag="{Binding}">
<!-- this is the grid I want to listen for clicks on -->
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
To bind a command to a grid you will need to use EventTrigger:
<ItemsControl VerticalAlignment="Top" Visibility="Collapsed" x:Name="RadioList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="12" Tag="{Binding}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding YourCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
with the following namespace definition:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Categories

Resources