我想实现一个相当复杂的曲线编辑器,它必须支持通常的要求,例如:
- 可自由缩放和可移动的轴
- 每个曲线点有不同的插值类型(线性、三次、样条曲线)
- 切线(连接和断开)
- 选择一个或多个点进行编辑(移动、缩放、删除)通过栅栏或单击
- 仅显示选定曲线点的手柄和高亮显示
我不我不想操作实际的 WPF 曲线,而是使用具有键/值/切线集的现有模型,并从我们的实现中采样曲线的精确形状。
我已经积累了一些实现自定义用户控件和模板的经验。但我想确保我不会错过任何明显的解决方案。我计划拥有这个通用的 XAML 树:
- CurveEditor - 包含所有内容的窗口
- MainThumb:启用拖动和缩放编辑器范围
- XAxis:用户控件在左侧渲染一些比例
- YAxis:用户控件在底部渲染一些比例
- 曲线:容纳曲线的画布
- 曲线:单条曲线的用户控件
- CurveName - 曲线标签
- CurveLine - DrawingVisual 将通过采样样条函数的内部实现来渲染实际曲线。
- CurveEditPoints - 保存所有编辑点的画布
- CurveEditPoint - 用于单个编辑点的用户控件
- LeftTangent - 左切线手柄的用户控件
- LeftTangentThumb - 用于修改手柄
- RightTangent - 右切线手柄的用户控件
- RightTangentThumb - 用于修改手柄
- CurvePointCenter - 实际点的可视化、选择状态和插值类型。
- CurvePointThumb - 用于选择并拖动点的拇指
我知道,这是一个相当复杂的问题,我并不要求实际的实现。我对以下问题感兴趣:
- 您能否推荐任何可能对我有帮助的教程或书籍(我已经获得了 Illustrator WPF、WPF Control Development Unleashed 以及其他一些)
- 像 Tangents 这样的次要元素应该是单独的 UserControls 吗?
- 什么容器最适合托管各个“曲线”、“编辑点”和“切线”。现在,我使用 Canvas 和 Canvas.SetLeft/SetTop 来定位子项,但这感觉“奇怪”。
- 我应该使用像 Path 或 DrawingVisual-Classes 这样的“形状”来实现实际的表示吗?路径是直截了当的,但我担心数百个曲线点的性能。
- 我应该使用变换来旋转切线还是可以在文件后面的代码中进行一些三角测量数学?
- 该结构大致有意义吗,或者您是否建议采用完全不同的方法?
I want to implement a rather complex CurveEditor that has to support the usual requirements like:
- freely scalable and moveable axis
- different interpolation types per curve point (Linear, Cubic, Spline)
- Tangents (joined and broken)
- Selecting one or several points to edit (move, scale, delete) via Fence or Click
- Only show handles and highlights for selected curve points
I don't want to manipulate actual WPF curves but an existing model with key/value/tangents sets and sample the precise shape of the curve from our implementation.
I already gathered some experience on implementing custom UserControls and Templates. But I want to make sure, I don't miss any apparent solution. I planned to have this general XAML-tree:
- CurveEditor - Window holding all content
- MainThumb : Enable dragging and scaling the editor range
- XAxis : UserControl rending some scale on the left side
- YAxis : UserControl rending some scale on the bottom
- Curves : Canvas holding the curves
- Curve : UserControl for a single curve
- CurveName - Label of the curve
- CurveLine - DrawingVisual that will render the actual curve by sampling the internal implementation of the spline function.
- CurveEditPoints - Canvas that holds all edit points
- CurveEditPoint - UserControl for a single edit point
- LeftTangent - UserControl for the left tangent handle
- LeftTangentThumb - For modifying the handle
- RightTangent - UserControl for the right tangent handle
- RightTangentThumb - For modifying the handle
- CurvePointCenter - Visualisation of the actual point, select state and interpolation type.
- CurvePointThumb - Thumb to select and drag point around
I know, this is quite a complex question and I am not asking for an actual implementation. I am interested in the following questions:
- Can you recommend any tutorials or books that might help me (I already got Illustrated WPF, WPF Control Development Unleashed, and a couple of other)
- Should minor elements like the Tangents be individual UserControls?
- What container is best suited for hosting the individual "Curves", "EditPoints" and "Tangents". Right now, I use Canvas and Canvas.SetLeft/SetTop to position the children, but that feels "strange".
- Should I use "Shapes" like Path or DrawingVisual-Classes to implement actual representation. Path is straight forward, but I am concerned about performance with hundreds of CurvePoints.
- Should I use Transforms to rotate the tangents or is just fine to do some triangulation math in the code behind files?
- Does the structure roughly make sense, or do you suggest a completely different approach?
发布评论
评论(1)
您似乎手头有合适的工具,WPF Unleashed 非常棒,但我想您已经拥有了。
在以下情况之一中创建单独的用户控件:
这取决于你想要多少代码隐藏。
正如您在评论中所建议的,您可以使用 ItemsControl 作为容器,用于需要在多个项目之间进行选择的地方。因此这也可以在曲线级别上完成,而不仅仅是在曲线上的点级别上完成。根据您想要如何处理实际直线和曲线的绘制,您甚至可以拥有一个 ItemsControl。 (附带说明:不过,您不会拥有开箱即用的虚拟化,因为您的项目不会具有恒定的高度)
首先使用 MVVM。
曲线编辑器
ListBox(Panel=Canvas)(ItemsSource=Curves)(ItemTemplate=CurveControl)
CurveControl
EditPointControl
我已声明为 ListBox 设置 ItemTemplate。但是,您可以根据需要设置列表框的样式(我认为标准样式包括边框,您可能需要删除它或设置 bordersize=0)并设置 ItemContainerStyle 并绑定到 IsSelected,以便您可以从 ViewModel 控制 IsSelected(查看 此处了解我的意思)。
所以视图模型看起来像这样:
在这里你可以订阅 CurvePointViewModel 的 PropertyChanged 并重新计算你公开的路径。
我可能会不断改进,但这只是我的第一个猜测。
您可能需要注意一些细节。例如:拇指的样式可能是中间有一个可见的圆圈,周围有一个不可见的大圆圈,背景=透明。这样你就可以让你的可见圆圈变小,但让用户在小圆圈周围的区域使用拇指。
编辑:
这是拇指的示例:
当您想将其放置在画布上的特定点时,将边距设置为减去宽度和高度的一半会将圆的中心放置在该点上。此外,如果内部椭圆具有透明填充且边距为 -6,您将在内部(较小)圆周围获得一个大 6px 的半径,您可以在其中拖动拇指。
you seem to have the right tools at hand, WPF Unleashed is excellent, but I guess you have that one already.
make individual UserControls in one of these cases:
this depends on how much codebehind you want.
you can, as you suggested in your comment, use an ItemsControl as a container for wherever you need selection between multiple items. so this could also be done on the level of Curves, not just on the level of points on the curve. Depending on how you want to handle drawing of the actual lines and curves you can even have an ItemsControl for those. (on a side note: you will not have virtualization out of the box though, as your items won't have a constant height)
first of all use MVVM.
CurveEditor
ListBox(Panel=Canvas)(ItemsSource=Curves)(ItemTemplate=CurveControl)
CurveControl
EditPointControl
I have stated to set ItemTemplate for the ListBox. You can however style the listbox however you want (I think the standard style includes a border, you might want to remove that or set bordersize=0) and set instead of ItemTemplate the ItemContainerStyle and bind to IsSelected so you have control over IsSelected from your ViewModel (look here for what I mean).
So the viewmodel looks like this:
in here you can subscribe to CurvePointViewModel's PropertyChanged and recalculate the Path you're exposing.
I'd probably improve on it as I go but that'd be my first guess.
There are some details you might want to watch out for. eg: the style for the thumbs might be a visible circle in the middle and an invisible bigger one around that with background=transparent. that way you can have your visible circle small, but have the user use the tumb in an area around the small circle.
EDIT:
here is an Example for the Thumb:
as you want to position this at a specific point on a canvas setting the Margin to minus half the width and height will place the center of the circle on that point. Furthermore, having that inner ellipse with a transparent fill and Margin of -6 you will get a 6px bigger radius around the inner (smaller) circle where you can drag the thumb.