tags:

views:

1323

answers:

3

I have a WPF Canvas with some Ellipse objects on it (displayed as circles). Each circle is from a collection class instance which is actually a custom hole pattern class. Each pattern has a certain number of circles, and each circle then gets added to the canvas using an iteration over the collection using the code below.

So, the canvas is populated with a bunch of circles and each circle belongs to a certain pattern instance. You can see a screenshot here: http://twitpic.com/1f2ci/full

Now I want to add the ability to click on a circle on the canvas, and be able to determine the collection it belongs to, so that I can then do some more work on the selected pattern to which that circle belongs.

public void DrawHoles()
{
   // Iterate over each HolePattern in the HolePatterns collection... 
   foreach (HolePattern HolePattern in HolePatterns)
    {
        // Now iterate over each Hole in the HoleList of the current HolePattern...
        // This code adds the HoleEntity, HoleDecorator, and HoleLabel to the canvas
        foreach (Hole Hole in HolePattern.HoleList)
        {

            Hole.CanvasX = SketchX0 + (Hole.AbsX * _ZoomScale);
            Hole.CanvasY = SketchY0 - (Hole.AbsY * _ZoomScale);
            canvas1.Children.Add(Hole.HoleEntity);
        }
    }
}
+1  A: 

All FrameworkElements have a Tag property which is of type object that can be used to hold arbitrary information. You could assign the HolePattern to the Tag property and easily use that later to get the associated collection.

ie:

... Hole.HoleEntity.Tag = HolePattern as object; canvas1.Children.Add(Hole.HoleEntity);

later on in the click event:

event(object sender,....) { Ellipse e = sender as Ellipse; HolePattern hp = e.Tag as HolePattern; ... }

hope this helps! mark

Mark Synowiec
Awesome help... you got me started and opened my eyes. With your instructions, I actually assigned the Hole to the Ellipse.Tag, then on the Hole class, I have a reference to the HolePattern that the Hole belongs. This allows me to climb the hole tree as needed. This site is awesome!
MattSlay
A: 

So you probably already read my reply where I said I had it working. And it does work perfectly, (except that it requires great precision with the mouse), but I want to ask this: is it really smart to add an event handler to EVERY ellipse that gets added to a canvas? Now I don't know what kind of memory bog that could be, or maybe it is a piece of cake for WPF and Windows to handle.

In a practical case, I guess there would be not more that 30-50 holes even on a screen that had multiple patterns, but still; FIFTY event handlers? It just seems scary. And actually, each "Hole" is visually represented by two concentric circles and a text label (see the screenshow here: http://twitpic.com/1f2ci/full ), and I know the user would expect to be able to click on any one of those elements to select a hole. That means an event handler on 3 elements for every hole. Now we could be talking about 100 or more event handlers.

It seems like there should be a solution where you could have just one event handler on the Canvas and read the element reference under the mouse, then work off of that to get the .Tag property of that elment, and so on.

MattSlay
A: 

I thought I'd post my final and more refined solution in case it helps anyone else.

void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    int ClickMargin = 2;// Adjust here as desired. Span is in both directions of selected point.
    var ClickMarginPointList = new Collection<Point>();
    Point ClickedPoint = e.GetPosition(canvas1);
    Point ClickMarginPoint=new Point();
    for (int x = -1 * ClickMargin; x <= ClickMargin; x++)
    {
        for (int y = -1 * ClickMargin; y <= ClickMargin; y++)
        {
            ClickMarginPoint.X = ClickedPoint.X + x;
            ClickMarginPoint.Y = ClickedPoint.Y + y;
            ClickMarginPointList.Add(ClickMarginPoint);
        }
    }

    foreach (Point p in ClickMarginPointList)
    {
        HitTestResult SelectedCanvasItem = System.Windows.Media.VisualTreeHelper.HitTest(canvas1, p);
        if (SelectedCanvasItem.VisualHit.GetType().BaseType == typeof(Shape))
        {
            var SelectedShapeTag = SelectedCanvasItem.VisualHit.GetValue(Shape.TagProperty);
            if (SelectedShapeTag!=null &&  SelectedShapeTag.GetType().BaseType == typeof(Hole))
            {
                Hole SelectedHole = (Hole)SelectedShapeTag;
                SetActivePattern(SelectedHole.ParentPattern);
                SelectedHole.ParentPattern.CurrentHole = SelectedHole;
                return; //Get out, we're done.
            }
        }
    }
}
MattSlay