简体   繁体   中英

How to use BasedOn to template a toggle button in Generic.xaml?

wpf C# xaml

In my Generic.xaml, I have many styles of the form:

  <Style x:Key="ToggleButtonStyle12" TargetType="{x:Type ToggleButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Grid>
                        <Path x:Name="path1" Data="{StaticResource InsideQuarter3}" Fill="DarkOrange" Stroke="Black" />
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="path1" Property = "Opacity" Value="0.4"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="true">
                            <Trigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource Blink_On}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard Storyboard="{StaticResource Blink_Off}"/>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

The only difference between all these toggle buttons is the Path definition and the Fill color.

Could this style be templated/simplified in such a way where only the Path and Fill color need be supplied?

That is something along the lines of :

<Style x:Key="ToggleButtonStyle12" BasedOn(??????)>
    <Setter Property = "Path" Value="InsideQuarter3"/>
    <Setter Property = "Fill" Value="DarkOrange"/>
</Style>

Thank you for any help.

Edit#1 Well, I thought I had it--I was wrong. The below code will correctly set the Path Data and Fill properties. However, only the first created ToggleButton keeps the "MouseOver" and other ControlTemplate.Triggers. I need ALL the toggle buttons in the RingControl to respect their own triggers.

  public static class ButtonProperties
    {
        public static Color GetMyForegroundColor(DependencyObject obj)
        {
            return (Color)obj.GetValue(MyForegroundColorProperty);
        }

        public static void SetMyForegroundColor(DependencyObject obj, Color value)
        {
            obj.SetValue(MyForegroundColorProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyForegroundColor.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyForegroundColorProperty =
            DependencyProperty.RegisterAttached("MyForegroundColor", typeof(Color), typeof(ButtonProperties), new PropertyMetadata(Colors.Black));



        public static Geometry GetData(DependencyObject obj)
        {
            return (Geometry)obj.GetValue(DataProperty);
        }

        public static void SetData(DependencyObject obj, Geometry value)
        {
            obj.SetValue(DataProperty, value);
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.RegisterAttached("Data", typeof(Geometry), typeof(ButtonProperties), new PropertyMetadata(null));



        public static Brush GetFill(DependencyObject obj)
        {
            return (Brush)obj.GetValue(FillProperty);
        }

        public static void SetFill(DependencyObject obj, Brush value)
        {
            obj.SetValue(FillProperty, value);
        }

        // Using a DependencyProperty as the backing store for Fill.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FillProperty =
            DependencyProperty.RegisterAttached("Fill", typeof(Brush), typeof(ButtonProperties), new PropertyMetadata(null));

    }

Generic.xaml -- BaseButtonStyle

  <Style x:Key="BaseButtonStyle" TargetType="{x:Type ToggleButton}">
        <Setter Property="local:ButtonProperties.MyForegroundColor" Value="Blue"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Grid>
                    <TextBlock Text="Some Text">
                        <TextBlock.Foreground>
                            <SolidColorBrush Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ButtonProperties.MyForegroundColor)}" />
                        </TextBlock.Foreground>
                    </TextBlock>

                        <Path x:Name="path1" Data="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ButtonProperties.Data)}" 
                              Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ButtonProperties.Fill)}" 
                              Stroke="Black"/>

                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="path1" Property = "Opacity" Value="0.4"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="true">
                            <Trigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource Blink_On}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard Storyboard="{StaticResource Blink_Off}"/>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Generic.xaml -- ModifiedButtonStyle1

 <Style x:Key="ModifiedButtonStyle1" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource BaseButtonStyle}">
        <Setter Property="local:ButtonProperties.MyForegroundColor" Value="Red" />
        <Setter Property="local:ButtonProperties.Data" Value="{StaticResource Arc0}" />
        <Setter Property="local:ButtonProperties.Fill" Value="LightGreen"/>
    </Style>
    <Style x:Key="ModifiedButtonStyle2" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource BaseButtonStyle}">
        <Setter Property="local:ButtonProperties.MyForegroundColor" Value="Red" />
        <Setter Property="local:ButtonProperties.Data" Value="{StaticResource Arc45}" />
        <Setter Property="local:ButtonProperties.Fill" Value="LightPink"/>
    </Style>
    <Style x:Key="ModifiedButtonStyle3" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource BaseButtonStyle}">
        <Setter Property="local:ButtonProperties.MyForegroundColor" Value="Red" />
        <Setter Property="local:ButtonProperties.Data" Value="{StaticResource Arc90}" />
        <Setter Property="local:ButtonProperties.Fill" Value="LightCoral"/>
    </Style>

Generic.xaml -- Using the ModifiedButtonStyles in a custom control, RingControl

 <Style TargetType="{x:Type local:RingButtons2}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:RingButtons2}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">

                    <Viewbox>
                        <Grid>
                            <!--Outer Rim -->
                            <Path Data="{StaticResource OuterRim}"  Fill="Silver" Stroke="Black" />
                            <Path Data="{StaticResource OuterWheelBackground}" Fill="White" Stroke="Black" />

                            <ToggleButton x:Name="PART_Button1" Style="{StaticResource ModifiedButtonStyle1}"/>
                            <ToggleButton x:Name="PART_Button2" Style="{StaticResource ModifiedButtonStyle2}"/>
                            <ToggleButton x:Name="PART_Button3" Style="{StaticResource ModifiedButtonStyle3}"/>
  ........................................................
  </Grid>
                        </Viewbox>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Using the RingControl2 in the user interface:

       <w:RingButtons2/>

It would appear that clicking anywhere in the RingButtons2 control results only in the first defined togglebutton responding -- not any of the others.

How can I fix this so that each of the togglebuttons acts independently of the others and respects its own controltemplate triggers?

Thanks again.

Edit#2

Upon removing the TextBlock definition from the BaseButtonStyle,

 <TextBlock Text="Some Text">
                        <TextBlock.Foreground>
                            <SolidColorBrush Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ButtonProperties.MyForegroundColor)}" />
                        </TextBlock.Foreground>
                    </TextBlock>

ALL WORKS! Why is this??

Thanks.

If the target control has dependency properties that you can use for those custom bindings then you can use TemplateBinding, here's an example:

    <Style x:Key="BaseButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Foreground" Value="Blue" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <TextBlock Text="Some Text" Foreground="{TemplateBinding Foreground}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="ModifiedButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseButtonStyle}">
        <Setter Property="Foreground" Value="Red" />
    </Style>

Which you would then refer to like so:

<Grid>
    <Button Style="{StaticResource ModifiedButtonStyle}" />
</Grid>

However, in the example you've given above you're using a Path and a Fill inside a template, which I'm guessing don't have associated properties. In this case your options are to either create a new control and add those properties to it or, preferably, use attached properties. In the case of the latter you'd create an attached property like this:

public static class ButtonProperties
{
    public static Color GetMyForegroundColor(DependencyObject obj)
    {
        return (Color)obj.GetValue(MyForegroundColorProperty);
    }

    public static void SetMyForegroundColor(DependencyObject obj, Color value)
    {
        obj.SetValue(MyForegroundColorProperty, value);
    }

    // Using a DependencyProperty as the backing store for MyForegroundColor.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyForegroundColorProperty =
        DependencyProperty.RegisterAttached("MyForegroundColor", typeof(Color), typeof(ButtonProperties), new PropertyMetadata(Colors.Black));
}

And then you'd reference and override it like this in your XAML:

<Style x:Key="BaseButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="behaviors:ButtonProperties.MyForegroundColor" Value="Blue"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <TextBlock Text="Some Text">
                    <TextBlock.Foreground>
                        <SolidColorBrush Color="{Binding RelativeSource={RelativeSource TemplatedParent},
                            Path=(behaviors:ButtonProperties.MyForegroundColor)}" />
                    </TextBlock.Foreground>
                </TextBlock>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="ModifiedButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseButtonStyle}">
    <Setter Property="behaviors:ButtonProperties.MyForegroundColor" Value="Red" />
</Style>

These are more-or-less the same, it's just that in the first case you're using existing properties already inside the control (or creating a new control that has them) whereas in the second case you're declaring and attaching them to an existing control externally.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM