2016-02-17 4 views
3

В настоящее время я работаю над графиком, подобным редактору. Пока у меня есть узлы и соединение, я могу их перемещать и т. Д. Я хотел бы иметь возможность выбирать кривые, соединяющие один узел с другим, оранжевый.WPF выбираемая кривая

enter image description here

Выбор самого кривой, не является реальной проблемой, это просто немного сложнее, так как это довольно тонкий предмет (оригинальная толщина хода была 2). Мне пришлось откорректировать ход, чтобы сделать его несколько легче выбрать. Я хотел бы уменьшить ход, потому что я думаю, что он выглядит лучше. Моя идея состояла в том, чтобы нарисовать две кривые, одну с высокой толщиной толщины, но прозрачную, поверх нее, фактическая цветная кривая. Это позволило бы мне в основном иметь толерантность в том, насколько близко мне нужно щелкнуть до фактической кривой, чтобы выбрать ее.

Прямо сейчас кривая (Соединение) является классом, наследующим от Shape, чтобы иметь два из них, которые я завернул в пользовательский элемент управления. Это сделало трюк, что мне не нравится в том, что мне пришлось обернуть все свойства на пользовательской форме, чтобы распространять данные на оба моих подключения, например, мне пришлось обернуть начало и конец точечное свойство. Возможно, я также смогу достичь этих привязок желобов, но это в основном перемещает проблему из кода в XAML.

Это лучший способ достичь того, что я пытаюсь сделать? Я не эксперт в WPF, поэтому, возможно, я пропустил какое-то простое решение. Любая обратная связь по этому вопросу была бы весьма признательна.

М.

+0

У вас много тестов, маскирующих актуальный вопрос: каков ваш реальный вопрос?«Как я могу щелкнуть по кривой и зарегистрировать ее как щелчок»? –

ответ

1

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

Моя идея основана на VisualTreeHelper.HitTest. Он выполняет тест на попадание в указанную вами точку и возвращает объекты зависимостей, найденные в этой точке. Итак, что я сделал, это прослушивание события MouseRightButtonDown (в моем примере в окне), и, начиная с того момента, когда нажата кнопка правой мыши, я вычисляю сетку точек, образующих круговую сетку. Затем я HitTest каждую из этих точек, и если я нахожу известный именованный Path, я могу смело выбирать его.

После этого (слишком долго) объяснений, вот пример кода:

List<DependencyObject> hitResultsList = new List<DependencyObject>(); 
private void WrapPanel_MouseRightButtonDown(object sender, MouseButtonEventArgs e) 
{ 
    Window wp = sender as Window; 
    // Retrieve the coordinate of the mouse position. 
    Point pt = e.GetPosition((UIElement)sender); 

    bool elementFound = false; 
    foreach (Point point in GetPointsInCircle(pt, 8, pt, new Point(2, 2))) 
    { 
     // Clear the contents of the list used for hit test results. 
     Debug.Print(point.ToString()); 
     hitResultsList.Clear(); 
     // Set up a callback to receive the hit test result enumeration. 
     VisualTreeHelper.HitTest(wp, null, 
       new HitTestResultCallback(MyHitTestResult), 
       new PointHitTestParameters(point)); 

     // Perform actions on the hit test results list. 
     foreach (DependencyObject d in hitResultsList) 
     { 
      if (d is Path) 
      { 
       Path p = d as Path; 
       if (p.Name == "link1") 
       { 
        elementFound = true; //Here we found the Path with name link1, we could then select it 
        break; 
        } 
       } 
     } 
     if (elementFound) break; 
    } 

} 

MyHitTestResult:

// Return the result of the hit test to the callback. 
public HitTestResultBehavior MyHitTestResult(HitTestResult result) 
{ 
    // Add the hit test result to the list that will be processed after the enumeration. 
    hitResultsList.Add(result.VisualHit); 

    // Set the behavior to return visuals at all z-order levels. 
    return HitTestResultBehavior.Continue; 
} 

GetPointsInCircle (Gets круговой сетки точек):

private static IEnumerable<Point> GetPointsInCircle(Point circleCenter, float radius, Point gridCenter, Point gridStep) 
{ 
    if (radius <= 0) 
    { 
     throw new ArgumentOutOfRangeException("radius", "Argument must be positive."); 
    } 
    if (gridStep.X <= 0 || gridStep.Y <= 0) 
    { 
     throw new ArgumentOutOfRangeException("gridStep", "Argument must contain positive components only."); 
    } 

    // Loop bounds for X dimension: 
    int i1 = (int)Math.Ceiling((circleCenter.X - gridCenter.X - radius)/gridStep.X); 
    int i2 = (int)Math.Floor((circleCenter.X - gridCenter.X + radius)/gridStep.X); 

    // Constant square of the radius: 
    float radius2 = radius * radius; 

    for (int i = i1; i <= i2; i++) 
    { 
     // X-coordinate for the points of the i-th circle segment: 
     double x = gridCenter.X + i * gridStep.X; 

     // Local radius of the circle segment (half-length of chord) calulated in 3 steps. 
     // Step 1. Offset of the (x, *) from the (circleCenter.x, *): 
     double localRadius = circleCenter.X - x; 
     // Step 2. Square of it: 
     localRadius *= localRadius; 
     // Step 3. Local radius of the circle segment: 
     localRadius = (float)Math.Sqrt(radius2 - localRadius); 

     // Loop bounds for Y dimension: 
     int j1 = (int)Math.Ceiling((circleCenter.Y - gridCenter.Y - localRadius)/gridStep.Y); 
     int j2 = (int)Math.Floor((circleCenter.Y - gridCenter.Y + localRadius)/gridStep.Y); 

     for (int j = j1; j <= j2; j++) 
     { 
      yield return new Point(x, gridCenter.Y + j * gridStep.Y); 
     } 
    } 
} 

Литература:

HitTest on MSDN
Get all points on a uniform discrete grid inside a circle's radius (Adapted)

+0

Интересный подход, я не думал об этом, это немного «грубая сила», но я думаю, что это будет трюк. единственное беспокойство в том, что если линия тонкая, мне нужно будет создать плотную сетку, которая масштабируется квадратично. Теперь хорошо, что я могу все еще пробовать небольшую часть, и проблема не масштабируется с количеством узлов/соединений, которые у меня есть в сцене, которая хороша. Тест Hit не может быть тяжелым, если WPF использует offscreen render с уникальными именами. Я попытаюсь посмотреть, что из этого выйдет! благодаря! –

+0

@MarcoGiordano Ну, в тестовом проекте, который я создал, я создаю сетку с радиусом 8 пикселей с шагом в 2 пикселя, и он работает довольно быстро. Вы должны думать также, что ваша кривая не будет обычно прямой линией и быть сеткой круга, ваши шансы, что точка на сетке сверху на вашей кривой, очень вероятны. Кроме того, вы можете немного изменить это для архивирования «эффекта магнита», то есть, когда вы обнаружите свою кривую, переместите указатель мыши на обнаруженную точку. Это может быть очень здорово :) – Pikoh

-1

Один быстрая мысль может быть, чтобы сделать прозрачную толще кривой под существующие кривыми. Тест-система WPF может по-прежнему получать клики по прозрачности и дает вам эффект наличия невидимого «свечения» вокруг визуально визуализированного изображения, которое пользователь все еще может щелкнуть, чтобы взаимодействовать.

+0

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