使用XAML / WPF实现复杂曲线编辑器的策略

时间:2011-03-11 18:12:08

标签: wpf user-interface xaml architecture user-controls

我想实现一个相当复杂的CurveEditor,它必须支持通常的要求,如:

  • 可自由伸缩和可移动的轴
  • 每个曲线点的不同插值类型(线性,立方,样条)
  • 切线(连接和断开)
  • 通过围栏或点击
  • 选择一个或多个要编辑(移动,缩放,删除)的点
  • 仅显示所选曲线点的手柄和高光

sample illustration of the CurveEditorComponent

我不想操纵实际的WPF曲线,但是现有的带有键/值/切线的模型设置并从我们的实现中采样曲线的精确形状。

我已经积累了一些实现自定义UserControls和模板的经验。但我想确保,我不会错过任何明显的解决方案。我计划使用这个通用的XAML树:

  • CurveEditor - 包含所有内容的窗口
    • MainThumb :启用拖动和缩放编辑范围
    • XAxis :UserControl在左侧呈现一定比例
    • YAxis :UserControl在底部有一定比例
    • 曲线:画布保持曲线
      • 曲线:用于单条曲线的UserControl
        • CurveName - 曲线标签
        • CurveLine - DrawingVisual,它将通过对样条函数的内部实现进行采样来呈现实际曲线。
        • CurveEditPoints - 包含所有编辑点的画布
          • CurveEditPoint - 单个编辑点的UserControl
            • LeftTangent - 左侧切线控制柄的UserControl
              • LeftTangentThumb - 用于修改句柄
            • RightTangent - 右侧切线控制柄的UserControl
              • RightTangentThumb - 用于修改句柄
          • CurvePointCenter - 可视化实际点,选择状态和插值类型。
            • CurvePointThumb - 拇指选择并拖动点

我知道,这是一个非常复杂的问题,我不是要求实际实施。我对以下问题很感兴趣:

  1. 你能推荐一些可能对我有帮助的教程或书籍(我已经有了Illustrated WPF,WPF Control Development Unleashed,以及其他一些)
  2. 像Tangents这样的次要元素应该是单独的UserControls吗?
  3. 什么容器最适合托管个人“曲线”,“EditPoints”和“Tangents”。现在,我使用Canvas和Canvas.SetLeft / SetTop定位孩子,但感觉“奇怪”。
  4. 我应该使用像Path或DrawingVisual-Classes这样的“Shapes”来实现实际表示。路径是直截了当的,但我担心数百个CurvePoints的性能。
  5. 我应该使用变换来旋转切线,还是可以在文件后面的代码中进行一些三角测量算法?
  6. 结构是否大致有意义,或者您是否建议采用完全不同的方法?

1 个答案:

答案 0 :(得分:7)

  1. 你似乎有合适的工具,WPF Unleashed非常好,但我猜你已经有了这个。

  2. 在以下某种情况下制作单独的UserControl:

    • 你在整个地方使用相同的xaml(DRY)
    • 你的xaml文件太大了(得到一些组件)
    • 需要继承某个班级
  3. 这取决于你想要多少代码。

  4. 路径可以使用数百个CurvePoints。如果你有10.000,我会说你可能会遇到问题。
  5. 无法想象这里应该如何使用变换,也许是在Adorner中。
  6. 你的结构看起来不错。你将能够实现所有这一切。我会建议我怎么做:
  7. 首先使用MVVM。

    • CurveEditor
      ListBox(Panel = Canvas)(ItemsSource = Curves)(ItemTemplate = CurveControl)

    • CurveControl

      • 画布(背景=透明)< =我不确定标准是否为白色,但您不想与其他曲线重叠...
        • CurveName
        • 列表框(面板=画布(背景=透明))(的ItemsSource = CurveParts)
        • 列表框(面板=画布(背景=透明))(的ItemsSource = CurvePoints)(的ItemTemplate => EditPointControl)
    • EditPointControl

      • 帆布
        • Thumb(Template = Ellipse)(Name = CenterHandle)(带有一些用于选择和隐藏切线的Visualstates)
        • Thumb(Template = Ellipse)(Name = LeftHandle)
        • Thumb(Template = Ellipse)(Name = RightHandle)
        • 线(将X / Y绑定到Centerpoint和LeftHandlePoint)
        • 线(将X / Y绑定到Centerpoint和RightHandlePoint)

    我已声明为ListBox设置ItemTemplate。然而,您可以根据需要设置列表框的样式(我认为标准样式包含边框,您可能希望删除它或设置bordersize = 0)并设置而不是ItemTemplate ItemContainerStyle并绑定到IsSelected以便您可以控制从您的ViewModel中选择Is(从here看我的意思)。

    所以viewmodel看起来像这样:

    - CurveEditorViewModel
        - ObservableCollection<CurveViewModel> Curves
    
    
    - CurveViewModel
        - string Label
        - (Point LabelPlacement)
        - bool IsSelected
        - ObservableCollection<CurvePointViewModel> CurvePoints
        - ObservableCollection<CurvePartViewModel> CurveParts
    
    
    - CurvePointViewModel
        - Point Position
        - bool IsSelected
        - Point LeftHandle
        - Point RightHandle
    
    - CurvePartViewModel
        - CurvePointViewModel StartPoint
        - CurvePointViewModel EndPoint
        - Path CurvePath
    

    在这里,您可以订阅CurvePointViewModel的PropertyChanged并重新计算您正在公开的路径。

    当我走的时候,我可能会改进它,但那是我的第一个猜测。

    您可能需要注意一些细节。例如:拇指的样式可能是中间的可见圆圈,而围绕它的不可见的较大的圆圈,背景=透明。这样你可以让你的可见圆变小,但让用户在小圆周围的区域使用tumb。

    修改

    这是Thumb的示例:

            <Thumb Width="8" Height="8" Cursor="Hand" Margin="-4">
                <Thumb.Template>
                    <ControlTemplate TargetType="Thumb">
                        <Grid>
                            <Ellipse Fill="Transparent" Margin="-6"/>
                            <Ellipse Stroke="Red" StrokeThickness="2"/>
                        </Grid>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>
    

    因为你想将它定位在画布上的特定点上,将边距设置为负半宽度和高度将圆心的中心放在该点上。此外,如果内部椭圆具有透明填充并且边距为-6,则您将在内部(较小)圆圈周围获得6px更大的半径,您可以在其中拖动拇指。