Выбор объекта на холсте WPF?

У меня есть холст WPF с некоторыми объектами Ellipse (отображаемыми в виде кругов). Каждый круг взят из экземпляра класса коллекции, который на самом деле является классом пользовательского шаблона отверстий. Каждый шаблон имеет определенное количество кругов, и каждый круг затем добавляется на холст с помощью итерации по коллекции с использованием приведенного ниже кода.

Итак, холст заполнен кучей кругов, и каждый круг принадлежит определенному экземпляру паттерна. Вы можете увидеть скриншот здесь: http://twitpic.com/1f2ci/full

Теперь я хочу добавить возможность щелкнуть по кругу на холсте и определить, к какой коллекции он принадлежит, чтобы затем выполнить дополнительную работу с выбранным узором, к которому принадлежит этот круг.

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);
        }
    }
}

person MattSlay    schedule 23.02.2009    source источник


Ответы (3)


Все FrameworkElements имеют свойство Tag типа object, которое можно использовать для хранения произвольной информации. Вы можете присвоить HolePattern свойству Tag и легко использовать его позже для получения связанной коллекции.

i.e.:

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

позже в событии клика:

event(object sender,....)
{
   Ellipse e = sender as Ellipse;
   HolePattern hp = e.Tag as HolePattern;
   ...
}
person Mark Synowiec    schedule 23.02.2009
comment
Удивительная помощь ... вы меня начали и открыли мне глаза. По вашим инструкциям я фактически присвоил Hole Ellipse.Tag, а затем в классе Hole у меня есть ссылка на HolePattern, которому принадлежит Hole. Это позволяет мне карабкаться по дыровому дереву по мере необходимости. Этот сайт потрясающий! - person MattSlay; 24.02.2009

Так что вы, наверное, уже читали мой ответ, где я сказал, что у меня это работает. И это работает отлично (за исключением того, что требует большой точности с мышью), но я хочу спросить: действительно ли разумно добавлять обработчик событий к КАЖДОМУ эллипсу, который добавляется на холст? Теперь я не знаю, что это за болото памяти, или, может быть, WPF и Windows легко справляются с этим.

В практическом случае, я думаю, даже на экране с несколькими рисунками было бы не более 30-50 отверстий, но все же; ПЯТЬДЕСЯТ обработчиков событий? Это только кажется страшным. И на самом деле каждая «Отверстие» визуально представлена ​​двумя концентрическими кругами и текстовой меткой (см. скриншот здесь: http://twitpic.com/1f2ci/full ), и я знаю, что пользователь ожидает, что сможет щелкнуть любой из этих элементов, чтобы выбрать отверстие. Это означает обработчик событий на 3 элемента для каждой дыры. Теперь мы можем говорить о 100 или более обработчиках событий.

Кажется, что должно быть решение, в котором вы могли бы иметь только один обработчик событий на холсте и читать ссылку на элемент под мышью, а затем работать с этим, чтобы получить свойство .Tag этого элемента и так далее.

person MattSlay    schedule 24.02.2009

Я думал, что опубликую свое окончательное и более усовершенствованное решение на случай, если оно поможет кому-то еще.

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.
            }
        }
    }
}
person MattSlay    schedule 24.02.2009