Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF可选曲线_C#_Wpf_Xaml_Drawing_Bezier - Fatal编程技术网

C# WPF可选曲线

C# WPF可选曲线,c#,wpf,xaml,drawing,bezier,C#,Wpf,Xaml,Drawing,Bezier,我目前正在开发一个类似于图形的编辑器应用程序。到目前为止,我得到了节点和连接显示,我可以移动它们等。 我希望能够选择连接一个节点到另一个节点的曲线,橙色的曲线 选择曲线本身并不是真正的问题,它只是有点棘手,因为它是一个相当薄的对象(原始笔划厚度为2)。我不得不增加中风的脂肪,使它更容易选择。我想保持较小的冲程,因为我认为它看起来更好。 我的想法是画两条曲线,一条具有高笔划厚度但透明,再加上实际的彩色曲线。这将允许我基本上有一个公差,即我需要点击实际曲线来选择它的距离 现在,curve(Conn

我目前正在开发一个类似于图形的编辑器应用程序。到目前为止,我得到了节点和连接显示,我可以移动它们等。 我希望能够选择连接一个节点到另一个节点的曲线,橙色的曲线

选择曲线本身并不是真正的问题,它只是有点棘手,因为它是一个相当薄的对象(原始笔划厚度为2)。我不得不增加中风的脂肪,使它更容易选择。我想保持较小的冲程,因为我认为它看起来更好。 我的想法是画两条曲线,一条具有高笔划厚度但透明,再加上实际的彩色曲线。这将允许我基本上有一个公差,即我需要点击实际曲线来选择它的距离

现在,curve(Connection)是一个从Shape继承的类,为了有两个,我将它们包装在一个自定义控件中。它做到了,我不喜欢的是,为了将数据传播到我的两个连接,我必须在may custom shape上包装所有属性,例如,我必须包装起点和终点属性。 我可能也可以通过绑定实现这一点,但这基本上将问题从代码隐藏转移到XAML

有没有更好的方法来实现我的目标?我不是WPF的专家,所以我可能忽略了一些简单的解决方案。对此问题的任何反馈都将不胜感激


M.

首先,我认为你的方法是最简单的。但是你让我感兴趣,我已经探索了另一种选择元素的方法。当然,调整它可能需要大量的工作,但可能会让你走上另一条路

我的想法基于
VisualTreeHelper.HitTest
。它在您指定的点执行命中测试,并返回在该点找到的依赖项对象。因此,我所做的是监听MouseRightButtonDown事件(在我的窗口示例中),然后从按下鼠标右键的点开始,计算形成圆形网格的点网格。然后我点击测试每个点,如果我找到一个已知的命名路径,我可以安全地选择它

在这个(太长)解释之后,下面是一个示例代码:

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

}
GetPointsInCircle(获取点的圆形栅格):

私有静态IEnumerable GetPointsInCircle(点圆中心、浮动半径、点网格中心、点网格步长)
{

如果(radius一个快速的想法可能是在现有曲线下渲染一条透明的较厚曲线。WPF的命中测试系统可能仍然会拾取透明度上的点击,并给你一种效果,即在视觉渲染的周围有一些不可见的“发光”,用户仍然可以点击以进行交互。

有趣的方法,我没有想想看,这有点“蛮力”但我认为这会起作用。我唯一担心的是,如果线很细,我将需要生成一个密集的网格,以二次方式进行缩放。现在好的是,我可能仍然会采样一小部分,而问题不会随着场景中的节点/连接的数量而缩放,这很好。命中测试可能不会很重如果WPF使用具有独特IDE的屏幕外渲染,我将尝试看看结果如何!谢谢!@MarcoGiordano好吧,在我做的测试项目中,我用2像素的步长生成了一个8像素半径的网格,它工作得非常快。你还必须考虑到,你的曲线通常不会是一条直线,也不会是一个圆形网格,网格上的一个点位于曲线顶部的可能性很大。此外,您可以稍微更改此设置以存档“磁铁效应”,即,当您检测到曲线时,将鼠标指针移动到检测到的点。这可能非常酷:)您有很多测试来掩盖实际问题:您的实际问题是什么?“如何单击曲线并将其注册为单击“?是的,这正是我上面提到的方法,问题是我需要将两个对象包装到另一个类中,使其作为一个类来运行,也就是包装所有的属性和内容,我觉得这不是很优雅,但可能是个人的爱好,这就是为什么我在寻找替代解决方案。
// 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;
}
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);
        }
    }
}