2016-02-17 42 views
3

我目前正在編輯像應用程序一樣的圖形。到目前爲止,我有節點和連接顯示,我可以移動它們等。 我希望能夠選擇連接一個節點到另一個節點的曲線,橙色的。WPF可選曲線

enter image description here

選擇曲線本身,是不是真正的問題,它只是有點棘手,因爲它是一個相當薄的物體(原始行程厚度爲2)。爲了使選擇更容易,我必須加強筆畫。我想保持較小的筆畫,因爲我認爲它看起來更好。 我的想法是繪製兩條曲線,一條曲線的高度較大,但透明度高,而實際的顏色曲線則是透明的。這將允許我基本上有一個寬度,我需要點擊實際曲線來選擇它。

現在,曲線(Connection)是一個繼承自Shape的類,爲了讓它們中的兩個我將它們包裝在自定義控件中。它做了竅門,我不喜歡它的事實是,我必須包裝可能自定義形狀的所有屬性,以便將數據傳播到我的連接,例如我必須包裝開始和結束點屬性。 我可能也可以實現該槽綁定,但這基本上是將問題從代碼隱藏到XAML。

有沒有更好的方法來實現我想要做的?我不是WPF中的專家,所以我可能忽略了一些簡單的解決方案。任何有關此事的反饋將不勝感激。

M.

+0

你有很多測試掩蓋了實際問題:你真正的問題是什麼?「我如何點擊曲線並將其註冊爲點擊」? –

回答

1

好,首先我覺得你的方式做到這一點是easyest之一。但是你讓我感興趣,我探索了另一種選擇元素的方法。當然可能需要很多工作來調整它,但也許會讓你走上另一種方式。

我的想法是基於​​。它會在您指出的點執行命中測試,並返回在該點中找到的依賴對象。所以我所做的就是聽MouseRightButtonDown事件(在我的例子中是在一個Window中),並且從按下鼠標右鍵的點開始,我計算一個形成圓形網格的點的網格。然後我HitTest每個點,如果我找到一個已知的命名路徑,我可以安全地選擇它。

此(太長了)的解釋後,來了一個示例代碼:

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(獲取點的圓形網格):

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

有趣的方法,我沒有想到它,它有點「蠻力」,但我認爲它會做的伎倆。我唯一擔心的是,如果線條很薄,我將需要生成一個密集的網格,它以二次方式進行縮放。現在好的一點是,我仍然可以對一小部分進行採樣,並且這個問題不會隨着我在場景中擁有的節點/連接的數量而增加。如果WPF使用具有獨特IDE的離屏渲染,那麼點擊測試可能不會很沉重。我會試着看看它出來的是什麼!謝謝! –

+0

@MarcoGiordano嗯,在我製作的測試項目中,我生成了一個8像素半徑的網格,步長爲2像素,工作速度非常快。你還必須考慮你的曲線不會是一條直線,而是一個圓形網格,網格上的一個點位於曲線上的可能性很大。此外,你可以改變這一點來歸檔「磁鐵效應」,也就是說,當你檢測到你的曲線時,將鼠標指針移到檢測到的點上。這可能是非常酷:) – Pikoh

-1

一個快速的想法可能是呈現現有的曲線下方的透明厚曲線。 WPF的點擊測試系統仍然可以獲取透明度的點擊,並給你帶來一些看不見的「輝光」效果,用戶仍然可以點擊進行交互。

+0

是的,這是我上面提到的確切方法,問題是,我將需要包裝在另一個類內的兩個對象,並使其表現爲一個,又名包裝所有的屬性和東西,我沒有發現它那麼優雅,但可能是個人品味,這就是爲什麼我正在尋找替代解決方案。 –