Задача

У меня есть настраиваемый элемент управления, нацеленный на ToggleButton, который также будет работать с Button. Поэтому я хочу использовать общий ControlTemplate по умолчанию для обоих типов.

Стратегия, которую я пробовал, заключалась в том, чтобы установить TargetType="{x:Type ButtonBase}" в шаблоне, и это отлично работает, если явно задано для Button или ToggleButton.

Неявный

Библиотека настраиваемых элементов управления

В словаре ресурсов Generic.xaml в папке Themes в корне проекта ...

<Style TargetType="{x:Type ButtonBase}">
    <Setter Property="Template">
        <Setter.Value>
            <!--Modified Control Template-->
            <ControlTemplate TargetType="{x:Type ButtonBase}">

В классе элемента управления я установил метаданные для типа пользовательского элемента управления в его статическом конструкторе, используя FrameworkElement.DefaultStyleKey...

static ContentToggle()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentToggle),
       new FrameworkPropertyMetadata(typeof(ButtonBase)));
}

Использование проекта приложения WPF

App.xaml ...

<Application x:Class="Spec.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>

            <!--Get a reference to the window to establish View Context-->
            <RelativeSource x:Key="View" Mode="FindAncestor" 
                        AncestorType="{x:Type Window}" />

            <ResourceDictionary.MergedDictionaries>

                <!--Local Style-->
                <ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

MainWindow.xaml ...

<Window x:Class="Spec.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <b:ContentToggle Name="Toggle" Height="30" 
                         Content="{Binding options, RelativeSource={StaticResource View}}"
                         />
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public List<string> options { get; set; }
    public bool initialState { get; set; }

    public MainWindow ()
    {
        options = new List<string> { "Checked", "UnChecked" };

        initialState = false;

        InitializeComponent();
    }
}

Также существует файл с именем ButtonStyle.xaml, который определяет кисти, которые будут использоваться настраиваемым элементом управления. Он предоставляется в корне приложения объединенным словарем в App.xaml.

Результат

Шаблон ContentToggle instance имеет значение null, и для стилизованного элемента управления нет визуального элемента (когда я просматриваю элемент управления, он не имеет дочерних элементов).

Насколько я понимаю, для моего контроля будет использоваться автоматический стиль / шаблон ButtonBase. Что мне не хватает?

Явный

Пользовательский элемент управления работает должным образом, если стиль / шаблон явно объявлен в элементе управления. Следующее работает с целевым стилем, установленным на ButtonBase ...

Использование проекта приложения WPF

В App.xaml ...

<Application x:Class="Spec.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>

            <!--Get a reference to the window to establish View Context-->
            <RelativeSource x:Key="View" Mode="FindAncestor" 
                        AncestorType="{x:Type Window}" />

            <ResourceDictionary.MergedDictionaries>

                <!--custom control-->
                <ResourceDictionary Source="pack://application:,,,/ContentToggleButton;component/Themes/Generic.xaml" />

                <!--Local Style-->
                <ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

В MainWindow.xaml ...

<Window x:Class="Spec.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <b:ContentToggle Name="Toggle" Height="30" 
                         Content="{Binding options, RelativeSource={StaticResource View}}"
                         Style="{DynamicResource LocalButtonStyle}"
                         />
    </Grid>
</Window>

В ButtonStyle.xaml ...

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!--Custom Button backgrounds-->
    <LinearGradientBrush x:Key="Button.Static.Background" 
                             EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop Color="#E6ADAD"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.MouseOver.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop  Color="#FFF2F2"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.MouseOver.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop  Color="#F2FFF3"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop x:Name="GradientStop" Color="#ADE6B1"/>
    </LinearGradientBrush>

    <!--Establish the style colours-->
    <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070" />
    <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1" />
    <SolidColorBrush x:Key="Button.Pressed.Background" Color="#C4F6CE" />
    <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B" />
    <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4" />
    <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5" />
    <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383" />

    <!--Custom Style-->
    <Style x:Key="LocalButtonStyle" TargetType="{x:Type ButtonBase}" BasedOn="{StaticResource ButtonStyle}">
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Background" 
                    Value="{StaticResource Button.Static.Background}"/>
        <Setter Property="BorderBrush" 
                    Value="{StaticResource Button.Static.Border}"/>
    </Style>

</ResourceDictionary>

Структура решения (общая для обоих случаев)

enter image description here

1
Cool Blue 26 Ноя 2016 в 07:41

2 ответа

Лучший ответ

Я не знаю точно почему, но для ButtonBase поиск по темам вообще не работает (вероятно, это связано с тем, что он абстрактный). Даже если вы попытаетесь найти стиль для ButtonBase, например, FindResource или аналогичными методами - вы ничего не найдете, даже если вы сами определите его в Generic.xaml (для других элементов управления, таких как Button, вы найду стиль).

Теперь, чтобы решить вашу конкретную проблему, самый простой способ, который я вижу, - это определить стиль для ContentTogger, но определить шаблон для ButtonBase (вы можете переместить его в отдельный ресурс и повторно использовать):

<Style TargetType="{x:Type c:ContentToggle}">
    <Setter Property="Template">
        <Setter.Value>
            <!-- move this template out to separate resource -->
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
0
Evk 27 Ноя 2016 в 09:12

Прочитав вначале введение в вашу проблему, я предлагаю следующий подход. Это всего лишь образец, дающий основную идею.

< Сильный > Generic.xaml

<Style x:Key="CommonStyle" TargetType="{x:Type Button}">
    <Setter Property="Background" Value="Yellow"/>
    <Setter Property="FontSize" Value="25"/>
</Style>

<Style TargetType="{x:Type local:CustomButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomButton}">
                <Border Background="{TemplateBinding Background}" CornerRadius="10"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Button Style="{StaticResource CommonStyle}" Content="{TemplateBinding Content}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

CustomButton.cs

public class CustomButton : ToggleButton
{
    static CustomButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
    }           
}

Посмотрите, решит ли это вашу проблему, иначе я постараюсь развить этот ответ.

0
AnjumSKhan 27 Ноя 2016 в 07:27