Стратегия реализации редактора сложных кривых с помощью XAML / WPF - PullRequest
7 голосов
/ 11 марта 2011

Я хочу реализовать довольно сложный CurveEditor, который должен поддерживать обычные требования, такие как:

  • свободно масштабируемая и подвижная ось
  • различные типы интерполяции на точку кривой (линейная, кубическая, сплайн)
  • Касательные (соединенные и разорванные)
  • Выбор одной или нескольких точек для редактирования (перемещение, масштабирование, удаление) с помощью Fence или Click
  • Показывать маркеры и блики только для выбранных точек кривой

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, Unleashed Development WPF Control Development и несколько других)
  2. Должны ли второстепенные элементы, такие как Tangents, быть отдельными элементами UserControls?
  3. Какой контейнер лучше всего подходит для размещения отдельных «Кривых», «Правых точек» и «Касательных». Прямо сейчас я использую Canvas и Canvas.SetLeft / SetTop для позиционирования дочерних элементов, но это кажется "странным".
  4. Должен ли я использовать "Shapes", такие как Path или DrawingVisual-Classes, для реализации фактического представления. Путь прост, но я обеспокоен производительностью с сотнями точек CurvePoints.
  5. Должен ли я использовать Transforms для вращения касательных или это просто хорошо, чтобы сделать некоторую триангуляционную математику в коде позади файлов?
  6. Имеет ли структура приблизительный смысл, или вы предлагаете совершенно другой подход?

1 Ответ

7 голосов
/ 14 марта 2011
  1. у вас, кажется, есть нужные инструменты, WPF Unleashed отлично, но я думаю, у вас уже есть этот.

  2. сделать отдельные пользовательские элементы управления в одном из следующих случаев:

    • вы используете один и тот же xaml повсюду (СУХОЙ)
    • ваш xaml-файл становится слишком большим (выведите некоторые компоненты)
    • вам нужно наследовать от какого-то класса
  3. это зависит от того, сколько кода вы хотите.
    как вы предложили в своем комментарии, вы можете использовать ItemsControl в качестве контейнера для любого выбора между несколькими элементами. так что это также может быть сделано на уровне кривых, а не только на уровне точек на кривой. В зависимости от того, как вы хотите обрабатывать рисование реальных линий и кривых, вы можете даже использовать ItemsControl для них. (примечание: у вас не будет виртуализации из коробки, хотя ваши предметы не будут иметь постоянной высоты)

  4. Путь в порядке с сотнями точек кривой. Если у вас есть 10.000, я бы сказал, что у вас могут быть проблемы.
  5. не могу представить, как здесь использовать преобразование, возможно, внутри Adorner.
  6. ваша структура выглядит хорошо. Вы сможете реализовать все это. Я предложу, однако, как я бы это сделал:

в первую очередь используйте MVVM.

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

  • CurveControl

    • Холст (фон = прозрачный)
      • CurveName
    • ListBox (панель = Canvas (Background = прозрачный)) (ItemsSource = точек кривой) (ItemTemplate => EditPointControl)

EditPointControl

  • Холст
    • Thumb (Template = Ellipse) (Name = CenterHandle) (с некоторыми визуальными состояниями для выбора и скрытия касательных)
    • Большой палец (Шаблон = Эллипс) (Имя = LeftHandle)
    • Большой палец (Шаблон = Эллипс) (Имя = RightHandle)
    • Линия (привязка X / Y к центральной точке и LeftHandlePoint)
    • Линия (привязка X / Y к центральной точке и RightHandlePoint)

Я указывал установить ItemTemplate для ListBox. Однако вы можете стилизовать список по своему усмотрению (я думаю, что стандартный стиль включает в себя границу, вы можете удалить ее или установить borderize = 0) и установить вместо ItemTemplate ItemContainerStyle и привязать к IsSelected, чтобы вы могли иметь контроль над IsSelected из вашей 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

здесь вы можете подписаться на PropertyChanged объекта CurvePointViewModel и пересчитать путь, который вы открываете.

Я бы, вероятно, улучшил бы это, когда я иду, но это было бы моим первым предположением.

Есть некоторые детали, которые вы можете не упустить. Например: стиль для больших пальцев может быть видимым кругом посередине и невидимым большим вокруг с фоном = прозрачный. таким образом, вы можете сделать видимый круг маленьким, но пользователь должен использовать палец в области вокруг маленького круга.

EDIT

Вот пример для большого пальца:

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

, так как вы хотите расположить это в определенной точке на холсте, установив Margin равным минус половине ширины и высоты, центр круга будет помещен в эту точку. Кроме того, имея этот внутренний эллипс с прозрачной заливкой и полем -6, вы получите радиус в 6 раз больше вокруг внутреннего (меньшего) круга, куда вы можете перетаскивать большой палец.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...