WPF тематические лучшие практики

Это вопрос передового опыта , касающийся wpf тематики и, в частности, скининга . Это скорее вопрос, основанный на мнении, так как у меня нет проблем с выполнением этой работы, а скорее общий вопрос о том, охватывают ли мои выводы все сценарии, и сталкивались ли кто-то еще с такими же мыслями по этому вопросу и каковы были их подход.

Подводя итог, наша команда должна определить, как дать нашей системе возможность тематизироваться .

Мы разбили эту способность на 2 категории:

1) Стили наших элементов управления, которые мы просто называем Theme .

2) Ресурсы, которые они используют для настройки своего внешнего вида и называемые Скин ", в том числе кисти, и все виды структур размеров, таких как CornerRadius, BorderThickness и т. д.

Способ установки скина для системы - это простой случай последнего добавления словаря скинов в ресурсы нашего приложения.

  <Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Default.skin.xaml" />
            <ResourceDictionary Source="Theme.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Другой скин в последний раз сливается в наше приложение.

  protected override void OnStartup(StartupEventArgs e)
  {
       base.OnStartup(e);

       string skin = e.Args[0];
       if (skin == "Blue")
       {         .
            ResourceDictionary blueSkin = new ResourceDictionary();
            blueSkin.Source = new Uri("Blue.skin.xaml", UriKind.Relative);

            Application.Current.Resources.MergedDictionaries.Add(blueSkin);
       }
  }

Inside Theme.xaml:

   <!-- Region TextBox ControlTemplate -->

<ControlTemplate TargetType="{x:Type TextBox}" x:Key="TextBoxTemplate">
    <Border  Background="{TemplateBinding Background}"  
         BorderBrush="{TemplateBinding BorderBrush}" 
         BorderThickness="{TemplateBinding BorderThickness}"
         CornerRadius="{StaticResource TextBoxCornerRadius}" >
      <Border x:Name="shadowBorder" BorderBrush="{StaticResource TextBoxShadowBrush}"                                   
        CornerRadius="{StaticResource TextBoxInnerShadowCornerRadius}" 
        BorderThickness="{StaticResource TextBoxInnerShadowBorderThickness}" 
        Margin="{StaticResource TextBoxInnerShadowNegativeMarginForShadowOverlap}" >
            <ScrollViewer x:Name="PART_ContentHost"  Padding="{TemplateBinding Padding}" 
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
        </Border>
    </Border>

    <ControlTemplate.Triggers>
        <Trigger Property="BorderThickness" Value="0">
            <Setter TargetName="shadowBorder" Property="BorderThickness" Value="0" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<!-- EndRegion -->

<!-- Region TextBox Style -->

<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">     
    <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorderBrush}" />
    <Setter Property="Background" Value="{StaticResource TextBoxBackgroundBrush}" />
    <Setter Property="BorderThickness" Value="{StaticResource TextBoxBorderThickness}" />

    <Setter Property="Padding" Value="{StaticResource TextBoxPadding}" />
    <Setter Property="Template" Value="{StaticResource TextBoxTemplate}"/>
  <Style.Triggers>

        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="{StaticResource TextBoxIsMouseOverBackgroundBrush}" />
            <Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseOverBorderBrush}" />
        </Trigger>
        <Trigger Property="IsFocused" Value="True">
            <Setter Property="Background" Value="{StaticResource TextBoxIsMouseWithinBackgroundBrush}" />
            <Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseWithinBorderBrush}" />
        </Trigger>

    </Style.Triggers>
</Style>

<!-- EndRegion -->

В TextBox ControlTemplate есть элементы, привязанные к DependencyProperties с использованием TemplateBinding, и некоторые, такие как CornerRadius и InnerCornerRadius, InnerBorderThickness и InnerBorderBrush, которым присваивается их значение из ресурсов.

Какой будет лучший подход?

создание производного элемента управления с соответствующими свойствами зависимости который будет ссылаться на соответствующие ресурсы, а затем привязывать к ним элементы в шаблоне элемента управления.

Или

элементы внутри шаблона ссылаются на эти ресурсы сами.

Использование подхода со свойством зависимости:

Преимущества:

1) Ясность, у нас есть более понятный API для нашего элемента управления и лучшее понимание того, как наш элемент управления выглядит и ведет себя так, как он работает.

2) Шаблон не должен изменяться, чтобы быть настраиваемым. Все контролируется с помощью стиля.

3) Триггеры также изменяют внешний вид элемента управления без необходимости переопределения шаблона элемента управления, без необходимости триггеров ControlTemplate.

4) "Blendabilty", используя смесь, я могу очень легко настроить свой элемент управления.

5) Сами стили наследуются. поэтому, если я хочу изменить только один аспект элемента управления, все, что мне нужно сделать, это наследовать от стиля по умолчанию.

Недостатки:

1) Реализация еще одного пользовательского элемента управления.

2) Реализация многочисленных свойств зависимостей, некоторые из которых не имеют ничего общего с элементом управления и предназначены только для удовлетворения того, что мы имеем в нашем шаблоне.

  • Просто уточнить это означает наследовать от TextBox что-то вроде InnerShadowTextBox и реализация свойств зависимости с его помощью для всего вышеперечисленного.

Это усилится, если в моем шаблоне будет множество элементов, которые необходимо настроить.

Что-то вроде этого чудовища:

  <Style x:Key="{x:Type cc:ComplexControl}" TargetType="{x:Type cc:ComplexControl}">
    <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type cc:ComplexControl}">
                    <Grid>
                        <Ellipse Fill="Red" Margin="0" Stroke="Black" StrokeThickness="1"/>
                        <Ellipse Fill="Green"  Margin="6" Stroke="Red" StrokeThickness="1"/>
                        <Ellipse Fill="Blue" Margin="12"/>
                        <Ellipse Fill="Aqua" Margin="24" />
                        <Ellipse Fill="Beige" Margin="32"/>
                        <StackPanel Orientation="Horizontal" Width="25" Height="25"
                                    VerticalAlignment="Center" HorizontalAlignment="Center">
                            <Rectangle Fill="Black" Width="2" />
                            <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
                            <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
                            <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
                        </StackPanel>

                    </Grid>
                </ControlTemplate>
            </Setter.Value>
    </Setter>
</Style>

Для чего потребуются многочисленные ресурсы:

 <SolidColorBrush x:Key="Ellipse1Fill">Red</SolidColorBrush>
 <SolidColorBrush x:Key="Ellipse2Fill">Green</SolidColorBrush>
 <SolidColorBrush x:Key="Ellipse3Fill">Blue</SolidColorBrush>
 <SolidColorBrush x:Key="Ellipse4Fill">Aqua</SolidColorBrush>
 <SolidColorBrush x:Key="Ellipse5Fill">Beige</SolidColorBrush>
 <SolidColorBrush x:Key="Ellipse1Stroke">Beige</SolidColorBrush>
 <sys:Double x:Key="Ellipse1StrokeThickness>1</sys:Double>
      ......... and many more 

В любом случае, у меня был бы большой список ресурсов. Но со свойствами зависимости. Мне также нужно было бы определить необходимость находить смысл в каждой маленькой части, которая иногда не намного больше, чем «выглядит хорошо» и не имеет большого отношения к элементу управления или «Что если завтра я захочу изменить шаблон».

Использование подхода, при котором на ресурсы ссылаются из шаблона элемента управления.

Преимущества:

1) Простая в использовании, побочные шаги, описанные уродством в недостатках, описанных выше в подходе Dp, при этом обеспечивая «взлом», который включает тему.

Недостатки:

1) Если я захочу дополнительно настроить свой элемент управления, например добавить триггер, который влияет на внутреннюю границу моего TextBox, мне просто нужно создать новый шаблон элемента управления.

2) Непонятный API. Допустим, я хотел бы изменить BorderBrush внутренней границы в конкретном представлении.

         <TextBox>
             <TextBox.Resources>
                  <SolidColorBrush x:Key="InnerBorderBrush" Color="Red" />
             </TextBox.Resources>
          </TextBox> 

Что не так уж плохо, если подумать об этом ... мы иногда делаем это с реализациями Selector, которые внутренне используют определенные ресурсы, чтобы избавиться от неактивного выделения и цветов подсветки, например, так:

   <ListBox>
       <ListBox.Resources>
          <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
          <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Transparent"/>
          <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent"/>
          <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Transparent"/>
       </ListBox.Resources>
 </ListBox>

Выводы .

Гибрид, описанный в стиле TextBox выше, - это путь.

1) Свойства зависимостей будут введены только для аспектов элемента управления, которые относятся к логике элемента управления, включая определенные части шаблона.

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

3) Шаблоны управления должны стремиться быть минималистичными и использовать существующие свойства зависимостей. Как фон, передний план, BorderBrush и т. Д.

Буду очень признателен за ваш вклад и мысли по этому вопросу, спасибо заранее.

11 голосов | спросил eran otzap 15 AM000000120000005131 2015, 00:01:51

1 ответ


0

Как сказал Ксавье, это может быть лучшим вопросом для Code Review. Но я передам некоторые ключевые мысли по вашему вопросу, хотя многие из них будут касаться личного (или командного) стиля и требований.

После создания нескольких десятков тем я рекомендовал бы по возможности использовать пользовательские элементы управления. Со временем ремонтопригодность несколько снижается.

Если вам требуются незначительные изменения стиля, лучше использовать DataTemplates и Data Trigger, если позволяет ситуация. Таким образом, вы меняете стиль в чистом виде.

Кроме того, вы можете использовать свойство BasedOn. Создайте свой «базовый» стиль и используйте несколько стилей с атрибутом BasedOn = "{myBaseStyle}. Это позволит вам использовать множество параметров, не загромождая код.

Как правило, я всегда рекомендую иметь больше кистей /цветов /ресурсов в отличие от большего количества стилей или шаблонов. У нас обычно есть наша иерархия, установленная для шаблонов colors-> brushes-> styles->. Это помогает повторно использовать цвета, сохраняя разделение с помощью кистей.

Использование DynamicResource в отличие от StaticResource также полезно в некоторых ситуациях, когда вы загружаете ресурсы динамически.

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

ответил Dax Pandhi 9 +03002015-10-09T07:06:08+03:00312015bEurope/MoscowFri, 09 Oct 2015 07:06:08 +0300 2015, 07:06:08

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132