WPF Styles / Шаблон наследования - PullRequest
6 голосов
/ 16 августа 2011

В данный момент я пытаюсь изучить WPF и пытаюсь сделать так, чтобы элемент управления .Net по умолчанию выглядел по-другому благодаря использованию стиля.Использование C # в качестве моего предпочтительного языка, хотя весь приведенный ниже код является разметкой WPF.

Сегодня я настроил gmail с новой темой (см. Изображение ниже) и, таким образом, поставил перед собой задачу, которую можно выполнить в WPF.

New GMail buttons

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

Правои левые кнопки очень похожи, но только с двумя отличиями.Они имеют угловой радиус 1 и поле 15 слева или справа, в то время как для средней кнопки они оба установлены на 0.

Вопросы!

Q1. Вместо того, чтобы копировать весь стиль и изменять только эти 2 атрибута, это можно сделать с помощью некоторого типа наследования.Где кнопки справа и слева основаны на существующем стиле, но он вносит эти 2 визуальных изменения.Я уже пробовал свойство BasedOn при создании нового стиля, но не смог отредактировать необходимые атрибуты.

Q2. Являются ли стили правильным способом решения этой проблемы в WPF.В WinForms вы можете создать пользовательский элемент управления, у которого есть видимое свойство, связанное с перечислением, т. Е. Вы нажимаете кнопку, и параметры стиля могут быть левыми, средними, правыми.

Q3. Самый сложный вопрос до последнего.Можно ли это сделать, если к кнопке применен мой стиль?Затем, когда вы установите его цвет фона, чтобы сказать синий.Затем кнопка поддерживает градиенты, но вместо того, чтобы они были не совсем белыми, теперь они имеют оттенок синего.т. е. кисть для линейного градиента фона основывается, а не перезаписывает цвет фона, примененный к кнопке.Или они должны иметь определенные стили, определенные.Лично я не могу видеть без какого-либо кода, стоящего за этим, что это может быть достигнуто, то есть создание градиентных кистей из одной кисти в разметке WPF.

то есть кнопки, как ниже синей кнопки и серой / нормальной кнопки

Google buttons 2

MyStyle

<Style x:Key="GoogleMiddleButton" TargetType="{x:Type Button}">
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                    <GradientStop Color="#F1F1F1" Offset="0"/>
                    <GradientStop Color="#F5F5F5" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="#666666"/>
        <Setter Property="FontFamily" Value="Arial"/>
        <Setter Property="FontSize" Value="13"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Name="dropShadowBorder"
                        BorderThickness="0,0,0,1"
                        CornerRadius="1"
                        >
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#00000000"/>
                        </Border.BorderBrush>
                    <Border Name="border" 
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}" 
                    CornerRadius="0" 
                    Background="{TemplateBinding Background}">
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#D8D8D8"/>
                        </Border.BorderBrush>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="border">
                                <Setter.Value>
                                    <SolidColorBrush Color="#939393"/>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                                <Setter.Value>
                                    <SolidColorBrush Color="#EBEBEB"/>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Foreground" Value="#333333"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                            <GradientStop Color="#F1F1F1" Offset="1"/>
                            <GradientStop Color="#F5F5F5" Offset="0"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>

ps Если вы заметили какие-либо ошибки новичка в WPF выше, не стесняйтесь указывать их мне.

Ответы [ 3 ]

8 голосов
/ 16 августа 2011

Я делал это в прошлом, определяя присоединенное свойство с именем ExtendedProperties.CornerRadius.Затем я могу установить его в своем стиле:

<Style TargetType="Button">
    <Setter Property="local:ExtendedProperties.CornerRadius" Value="0"/>
    ...

И использовать его из шаблона:

<Border CornerRadius="{Binding Path=(local:ExtendedProperties.CornerRadius), RelativeSource={RelativeSource TemplatedParent}">

Я мог бы затем переопределить его локально так же, как переопределить любое другое свойство:

<Button Content="Archive" local:ExtendedProperties.CornerRadius="5,0,0,5"/>
<Button Content="Span"/>
<Button Content="Delete" local:ExtendedProperties.CornerRadius="0,5,5,0"/>

В моем случае это дает мне (очевидно, моя тема мрачная):

enter image description here

И просто подправив пару моих темПрикрепленные свойства, я создал этот эффект:

enter image description here

Преимущество этого подхода заключается в том, что нет необходимости создавать подкласс Button.Вы также можете использовать это же прикрепленное свойство для определения угловых радиусов для других элементов управления (например, TextBox).И вы не ограничены только радиусом угла, конечно.Вы можете определить вложенные свойства для всех видов вещей, специфичных для вашей темы, которых нет в базовых элементах управления.

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

Итак, чтобы ответить на ваши конкретные вопросы:

Q1.Да, смотрите мой ответ выше.Вы можете либо переопределить локально, либо определить новые стили, которые переопределяют свойство.

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

Q3.Я бы сказал, что это возможно в коде, но не является интуитивно понятным для использования в качестве элемента управления.Я думаю, вам лучше определить дополнительные прикрепленные свойства, например.ExtendedProperties.HoverBackground, ExtendedProperties.PressedBackground - и использовать их из вашего шаблона точно так же.Тогда потребители вашего контроля получат больше контроля над кистями, используемыми, когда ваш контроль находится в различных состояниях.Я делал это в прошлом, но использовал более общие имена свойств (SecondaryBackground, TernaryBackground), чтобы я мог повторно использовать эти свойства в других контекстах.Опять же, документирование вашей темы полезно.

2 голосов
/ 16 августа 2011

Q1: не так далеко, как я знаю

Q2: я бы сказал, что стиль - это путь, и вы, безусловно, можете создать свой собственный класс, который получается из кнопки и выбирает радиус правого угла на основе того, является ли он левым, средним или правым.

Q3: Должен быть выполним с пользовательским преобразователем значений и вашим собственным стилем.

В заключение. В этом случае я мог бы испытать желание поместить градиент фона и радиус угла на окружающую панель стека. Кнопки будут прозрачными с текстом. Тогда вам не придется иметь дело с радиусом угла на отдельных кнопках.

Редактировать: добавлен код и стиль для ответа на вопрос 3 выше. К ОП; Я не уверен, что это именно то, что вы хотели, но, возможно, здесь есть что-то, что может вас заинтересовать.

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

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

Вот пользовательский преобразователь значений:

class SolidColorBrushToGradientConverter : IValueConverter
{
    const float DefaultLowColorScale = 0.95F;

    public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
    {
        var solidColorBrush = value as SolidColorBrush;

        if (!targetType.IsAssignableFrom (typeof (LinearGradientBrush)) || solidColorBrush == null)
        {
            return Binding.DoNothing;
        }

        var lowColorScale = ParseParameterAsDouble (parameter);

        var highColor = solidColorBrush.Color;
        var lowColor = Color.Multiply (highColor, lowColorScale);
        lowColor.A = highColor.A;

        return new LinearGradientBrush (
            highColor,
            lowColor,
            new Point (0, 0),
            new Point (0, 1)
            );
    }

    static float ParseParameterAsDouble (object parameter)
    {
        if (parameter is float)
        {
            return (float)parameter;
        }
        else if (parameter is string)
        {
            float result;
            return float.TryParse(
                (string) parameter, 
                NumberStyles.Float, 
                CultureInfo.InvariantCulture, 
                out result
                        )
                        ? result
                        : DefaultLowColorScale
                ;
        }
        else
        {
            return DefaultLowColorScale;
        }
    }

    public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

Затем я ссылаюсь на это в стиле, который я скопировал у вас (в основном то же самое, но я немного изменил структуру), важная часть строки такова:

Background="{Binding Path=Background,Mode=OneWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource SolidColorBrushToGradientConverter}, ConverterParameter=0.95}"

Это означает, что мы привязываемся к шаблонному родительскому фону (т.е. Button.Background), мы используем конвертер SolidColorBrushToGradientConverter с параметром 0,95 (это определяет, насколько темнее «низкий» цвет должен сравниваться с «высоким» цветом) .

Полный стиль:

<local:SolidColorBrushToGradientConverter x:Key="SolidColorBrushToGradientConverter" />

<Style x:Key="GoogleMiddleButton" TargetType="{x:Type Button}">
    <Setter Property="Background" Value="#F5F5F5" />
    <Setter Property="Foreground" Value="#666666"/>
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="FontSize" Value="13"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border 
                    Name="dropShadowBorder"                        
                    BorderThickness="0,0,0,1"                        
                    CornerRadius="1"
                    >
                    <Border.BorderBrush>
                        <SolidColorBrush Color="#00000000"/>
                    </Border.BorderBrush>
                    <Border Name="border"                     
                            BorderThickness="{TemplateBinding BorderThickness}"                    
                            Padding="{TemplateBinding Padding}"                     
                            CornerRadius="0"                     
                            Background="{Binding Path=Background,Mode=OneWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource SolidColorBrushToGradientConverter}, ConverterParameter=0.95}"
                            >
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#D8D8D8"/>
                        </Border.BorderBrush>
                        <ContentPresenter 
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                                   
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            />
                    </Border>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BorderBrush" TargetName="border">
                            <Setter.Value>
                                <SolidColorBrush Color="#939393"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                            <Setter.Value>
                                <SolidColorBrush Color="#EBEBEB"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" Value="#4A8FF7" />
                        <Setter Property="Foreground" Value="#F5F5F5" />
                        <Setter Property="BorderBrush" TargetName="border">
                            <Setter.Value>
                                <SolidColorBrush Color="#5185D8"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
1 голос
/ 16 августа 2011

Мне нравится, как выглядит этот вызов!

Q1: другие комментарии / ответы верны, шаблоны не могут быть изменены или унаследованы. Однако существуют способы передачи значений в шаблон для изменения их внешнего вида. Простым (хотя и слегка хакерским способом) было бы передать границу CornerRadius в шаблон, используя свойство Tag enter code here Лучшим способом может быть кнопка подкласса для добавления свойства location.

Q2: Да, вы на правильном пути со стилями / шаблонами

Q3: измените ваш шаблон, включив в него OpacityMask с желаемым градиентом. Затем вы можете либо поместить элемент за этой маской, либо заставить замаскированный элемент принять сам цвет фона. Полный пример показан ниже:

enter image description here

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <Style x:Key="GoogleButton" TargetType="{x:Type Button}">      
      <Setter Property="Background" Value="White"/>
      <Setter Property="Foreground" Value="#666666"/>
      <Setter Property="Tag">
        <Setter.Value>
          <CornerRadius>0</CornerRadius>
        </Setter.Value>
      </Setter>
      <Setter Property="FontFamily" Value="Arial"/>
      <Setter Property="FontSize" Value="13"/>
      <Setter Property="FontWeight" Value="Bold"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Border Name="dropShadowBorder"
                    BorderThickness="0,0,0,1"
                    CornerRadius="1"
                    BorderBrush="Transparent"
                    Background="White">
              <Grid>
                <Border Name="backgroundFill" 
                        BorderBrush="Red"
                        Background="{TemplateBinding Background}"
                        CornerRadius="{TemplateBinding Tag}">
                  <Border.OpacityMask>
                    <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                      <GradientStop Color="#FF000000" Offset="0"/>
                      <GradientStop Color="#00000000" Offset="1"/>
                    </LinearGradientBrush>
                  </Border.OpacityMask>
                </Border>
                <Border Name="border" 
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}" 
                    CornerRadius="{TemplateBinding Tag}" 
                    Background="Transparent">
                  <Border.BorderBrush>
                    <SolidColorBrush Color="#D8D8D8"/>
                  </Border.BorderBrush>
                  <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
              </Grid>
            </Border>            
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="BorderBrush" TargetName="border">
                  <Setter.Value>
                    <SolidColorBrush Color="#939393"/>
                  </Setter.Value>
                </Setter>
                <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                  <Setter.Value>
                    <SolidColorBrush Color="#EBEBEB"/>
                  </Setter.Value>
                </Setter>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="#333333"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
          <Setter Property="Background">
            <Setter.Value>
              <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                <GradientStop Color="#F1F1F1" Offset="1"/>
                <GradientStop Color="#F5F5F5" Offset="0"/>
              </LinearGradientBrush>
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>
  <Grid>
    <StackPanel Orientation="Horizontal"
                VerticalAlignment="Top">
      <Button Style="{StaticResource GoogleButton}" Content="Archive">
        <Button.Tag>
          <CornerRadius>2,0,0,2</CornerRadius>
        </Button.Tag>
      </Button>
      <Button Style="{StaticResource GoogleButton}" Content="Spam"
              Background="LightBlue"/>
      <Button Style="{StaticResource GoogleButton}" Content="Delete">
        <Button.Tag>
          <CornerRadius>0,2,2,0</CornerRadius>
        </Button.Tag>
      </Button>
    </StackPanel>
  </Grid>
</Window>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...