0

2 Shape arrays inside 2 ItemsControls

I want to develop a kind of Vector Image Editor using WPF. Every shape with the same form is placed in an ItemsControl. There are at least 4 kind of shapes (line, rectangle, bitmap etc). The problem is:

I cannot click the shape from layer below the other layer.

The requirement:

  1. ItemsControl ItemsPanelTemplate must be Canvas with size cannot be 0.
  2. Every shape can be click anytime without activate IsHitTestVisible at the clickable shape.

The questions:

How can I enable to click shape below other shape in upper ItemsControl?

Edit 1: Add code snippet

<Grid>
    <!--Array of Ellipses-->
    <ItemsControl ItemsSource="{Binding EllipseSource}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=Distance}" />
                <Setter Property="Canvas.Width" Value="10" />
                <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                <Setter Property="Canvas.Height" Value="10" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <Ellipse MouseDown="EllipseOnMouseDown" />
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <!--Array of Rectangles-->
    <ItemsControl ItemsSource="{Binding RectangleSource}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=Distance}" />
                <Setter Property="Canvas.Width" Value="10" />
                <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                <Setter Property="Canvas.Height" Value="10" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <Rectangle MouseDown="RectangleOnMouseDown" />
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>
Yohanes Nurcahyo
  • 601
  • 8
  • 19
  • So with two overlap items, you want to get click event only with lower items, ignore the upper items? – NoName Feb 22 '16 at 09:13
  • Are you looking for `Panel.ZIndex` Attached Property [MSDN](https://msdn.microsoft.com/en-us/library/system.windows.controls.panel.zindex(v=vs.110).aspx) – Gopichandar Feb 22 '16 at 09:15
  • @Gopichandar `Panel.ZIndex` change how a shape hide other shapes, which is overlap each other. – NoName Feb 22 '16 at 09:17
  • Something like [this](http://stackoverflow.com/a/5450993/2819451). May be you can make the `ZIndex` dynamic based on the events. – Gopichandar Feb 22 '16 at 09:20
  • @Sakura I should be able to click on any shapes where ever they are. So the upper items are also important and should not be ignored. – Yohanes Nurcahyo Feb 22 '16 at 09:21
  • Right. I'll try to make a sample for you! – NoName Feb 22 '16 at 09:23
  • @Gopichandar, If I set Panel.ZIndex to an ItemsControl with different values then I cannot click other Shapes. I want all shapes can be clicked anytime where ever I want. – Yohanes Nurcahyo Feb 22 '16 at 09:24
  • @YohanesNurcahyo Can you provide us the sample code that you tried and failed. That will help us for clear understanding. – Gopichandar Feb 22 '16 at 09:27
  • @Gopichandar, I cannot copy all the code because it is to big. But I will try to make them shorter to make it simple to understand. – Yohanes Nurcahyo Feb 22 '16 at 09:32

1 Answers1

1

Note:

  • name your Grid control as grid
  • Change your function RectangleOnMouseDown and EllipseOnMouseDown to shape_MouseLeftButtonDown

You can use: XAML:

<Grid name="grid">
    <!--Array of Ellipses-->
    <ItemsControl ItemsSource="{Binding EllipseSource}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=Distance}" />
                <Setter Property="Canvas.Width" Value="10" />
                <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                <Setter Property="Canvas.Height" Value="10" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <Ellipse MouseDown="shape_MouseLeftButtonDown" />
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <!--Array of Rectangles-->
    <ItemsControl ItemsSource="{Binding RectangleSource}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=Distance}" />
                <Setter Property="Canvas.Width" Value="10" />
                <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                <Setter Property="Canvas.Height" Value="10" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <Rectangle MouseDown="shape_MouseLeftButtonDown" />
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Code:

List<DependencyObject> hitResultsList = new List<DependencyObject>();

// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
    hitResultsList.Add(result.VisualHit);
    return HitTestResultBehavior.Continue;
}

private void shape_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Point pt = Mouse.GetPosition(grid);
    hitResultsList.Clear();
    VisualTreeHelper.HitTest(grid, null, new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(pt));

    foreach (var ee in hitResultsList)
    {
        if(ee is Ellipse)
        {
            MessageBox.Show("rectangle clicked!");
            var ellipse = ee as Ellipse;
            // Do something with ellipse
        }
        if(ee is Rectangle)
        {
            MessageBox.Show("ellipse clicked!");
            var rec = ee as Rectangle;
            // Do something with rectangle
        }

    }
}
NoName
  • 7,940
  • 13
  • 56
  • 108
  • That's a good Idea, mouse problem is solved but the problem is the WPF capability of controls below other control are still lost. For example if Ellipse below Rectangle and Ellipse has other a WPF control like TextBox. f the mouse cursor above Ellips, the mouse cursor doesn't change to text input cursor and cannot edit directly the TextBox. But I will use your solution with different approach. Instead MouseLeftButtonDown event I will use MouseMove event and if there is an ellipse below mouse cursor then its ItemsControl.IsHitTestVisible =true and other ItemsControl.IsHitVisible=false. – Yohanes Nurcahyo Feb 23 '16 at 08:05
  • That not not very hard to implement. – NoName Feb 23 '16 at 08:27