Пользовательская кнопка изображения и переключатель/кнопка переключения из общего базового класса

Я хотел бы создать набор настраиваемых элементов управления, которые в основном представляют собой кнопки изображения (это немного сложнее, но это основной эффект, который я ищу), для которых я видел несколько разных примеров. Тем не менее, я хотел бы еще больше расширить это, чтобы также разрешить переключатели/переключатели.

Что я хотел бы сделать, так это иметь общий абстрактный класс с именем ImageButtonBase, который имеет реализации по умолчанию для ImageSource и Text и т. д. Это делает обычную реализацию ImageButton довольно простой.

Проблема, с которой я сталкиваюсь, заключается в создании ее аромата RadioButton. Как я понимаю, есть как минимум три варианта:

  1. Было бы легко создать что-то производное от RadioButton, но тогда я не смогу использовать созданный мной абстрактный класс.
  2. Я мог бы изменить абстрактный класс на интерфейс, но тогда я потеряю абстрактные реализации и фактически буду иметь дублирование кода.
  3. Я мог бы вывести из своего абстрактного класса и повторно реализовать свойства и события типа RadioButton (IsChecked, GroupName и т. д.), но это определенно не кажется хорошей идеей.

Примечание. Я видел Как заставить группу переключателей действовать как переключатели в WPF?, но то, что я хочу сделать, немного сложнее.

Мне просто интересно, есть ли у кого-нибудь пример реализации или что-то, что можно было бы адаптировать к такому сценарию. Я вижу плюсы и минусы каждой из приведенных выше идей, но у каждой есть потенциальные ловушки.

Спасибо, ВТС


person Wonko the Sane    schedule 10.05.2010    source источник
comment
Меня это сбивает с толку, вы хотите создать единый элемент управления, который может быть кнопкой (связанной с какой-либо командой?), кнопкой-переключателем (привязанной к некоторому логическому типу?) или переключателем (привязанной к некоторому интегральному типу)? Я не думаю, что есть ОДНО решение, чтобы получить их все, не вводя слишком много отклонений от поведения "если-то-иначе" и других запахов кода. Если это так, вам, вероятно, лучше создать отдельный класс для каждой цели. Вы все еще можете позволить им расширить некоторый интерфейс маркера, если хотите дать им некоторые общие свойства.   -  person Simon D.    schedule 11.05.2010
comment
Нет, мне больше нужно несколько элементов управления, основанных на одном абстрактном классе. Фреймворк делает то же самое — есть элемент управления ButtonBase, который наследуется элементами управления Button и ToggleButton. Затем RadioButton является производным от ToggleButton. Я хочу создать абстрактный класс, производный от ButtonBase, с моими дополнительными свойствами. Мой класс Button и классы RadioButton будут производными от этого. Чего я стараюсь избежать, если это возможно, так это переопределения всех свойств и событий RadioButton, которые я бы получил бесплатно.   -  person Wonko the Sane    schedule 11.05.2010


Ответы (1)


Я думаю, что ваше лучшее решение здесь - использовать присоединенные свойства вместо подкласса. Это позволит вам не только съесть свой торт, но и съесть его.

Вместо того, чтобы писать это:

<my:CustomButton ImageSource="Abc" Text="Def" ... />

вы бы написали это:

<Button my:ButtonLook.ImageSource="Abc" my:ButtonLook.Text="Def" ... />

Это будет работать с привязкой данных и всем остальным. Чтобы реализовать это, создайте свой класс «ButtonLook», производный от DependencyObject, и создайте два прикрепленных свойства, используя фрагмент кода «propa» в Visual Studio. Затем установите PropertyChangedCallback для каждого, чтобы сконструировать обновление свойства ContentControl.Content для всего, к чему они присоединены.

Альтернативное решение — встроить скрытый RadioButton в подкласс CustomRadioButton, дать ему пустой шаблон, чтобы сделать его невидимым, и связать свойства IsChecked и GroupName:

<ControlTemplate TargetType="my:CustomRadioButton">
  <Grid>
    <RadioButton IsChecked="{Binding ToggleButton.IsChecked, RelativeSource={RelativeSource TemplatedParent}}"
                 GroupName="{TemplateBinding GroupName}">
      <RadioButton.Template><ControlTemplate /></RadioButton.Template>
    </RadioButton>
    ... visual part here ...

Убедитесь, что ваши подклассы CustomToggleButton и CustomRadioButton используют AddOwner для создания DependencyProperties, таких как IsChecked и GroupName, а не для создания новых.

person Ray Burns    schedule 12.05.2010
comment
Я пытаюсь использовать маршрут прикрепленных свойств, но мне непонятно, что вы имеете в виду в отношении PropertyChangedHandler. Единственная другая проблема, которая у меня есть с этим решением, заключается в том, что я все еще хотел бы обернуть их в пользовательский (или пользовательский) элемент управления, чтобы его можно было использовать повторно (мой фактический абстрактный класс имеет гораздо больше свойств, и это быстро получить беспорядок при добавлении ко многим элементам управления по всему проекту.Конечно, я мог бы создавать свойства в пользовательском элементе управления и привязывать их к присоединенным свойствам, но это просто возвращает меня в ту же ситуацию, что и моя исходная проблема. - person Wonko the Sane; 15.05.2010
comment
Я имел в виду PropertyChangedCallback. Как и в DependencyProperty.RegisterAttached(..., new PropertyMetadata { PropertyChangedCallback = (obj, e) => { your_code-here; }});, я обновлю ответ. Но из остальной части вашего комментария мне кажется, что мое второе предложение может быть более подходящим для ваших нужд. - person Ray Burns; 15.05.2010
comment
Итак, чтобы убедиться, что я понятен, мне все равно придется определять какие-либо свойства, события и т. д. RadioButton (или ToggleButton) в моем пользовательском классе? - person Wonko the Sane; 17.05.2010
comment
Да, если вы согласитесь с моим вторым предложением, это то, что вам нужно будет сделать. - person Ray Burns; 18.05.2010
comment
Еще видимо застрял. Моя реализация не работает как RadioButton. ‹ControlTemplate TargetType=my:CustomRadioButton› ‹Grid› ‹RadioButton IsChecked={Binding IsChecked, RelSource={RelSource TemplParent}}› ‹RadioButton.Template›‹ControlTemplate /›‹/RadioButton.Template› ‹/RadioButton› ‹ToggleButton IsChecked {Binding Path=IsChecked, Rel={RelSource TempParent}}› ‹!-- Content Here --› ‹/ToggleButton› ‹/Grid› ‹/ControlTemplate› DepProp IsCheckedProperty = ToggleButton.IsCheckedProperty.AddOwner(typeof(CustomRadioButton)); - person Wonko the Sane; 20.05.2010