Mostafa Mostafa - 3 months ago 10
C# Question

Binding Shape FillColor to Button BackgroundBrush

I wanna create Close/Maximize/Minimize Buttons for my app. So I wrote this piece of Style:

<Style x:Key="CloseButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="BackgroundColor1" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0"
To="#FFFF1111"/>
<ColorAnimation
Storyboard.TargetName="BackgroundColor2" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0"
To="#FFFF1111"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse Name="BackgroundColor1" Margin="4,0,0,0" Width="18" Height="18">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="{StaticResource ColorBorder}" Offset="0.2"/>
<GradientStop Color="WhiteSmoke" Offset=".9"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Name="BackgroundColor2" Margin="4,0,0,0" Width="18" Height="18">
<Ellipse.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="{StaticResource ColorBorder}" Offset="1"/>
<GradientStop Color="WhiteSmoke" Offset="0"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>

<Ellipse Margin="4,0,0,8" Width="7" Height="7">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="1,0" EndPoint="0,1">
<GradientStop Color="WhiteSmoke" Offset="0"/>
<GradientStop Color="Transparent" Offset="0.7"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>

<Ellipse Margin="4,0,0,0" Width="18" Height="18" StrokeThickness="2" StrokeLineJoin="Round">
<Ellipse.Stroke>
<LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Transparent" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>

<Ellipse Margin="4,0,0,0" Width="18" Height="18" StrokeThickness="1" StrokeLineJoin="Round">
<Ellipse.Stroke>
<LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
<GradientStop Color="#FF333333" Offset="1"/>
<GradientStop Color="Transparent" Offset="0.5"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>


Right now I have 3 duplicate of this Style, With changes in MouseOver color, like this:

<ColorAnimation Storyboard.TargetName="BackgroundColor1" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
<!-- Just "To" Color changes is each button -->
To="#FFFF1111"/>


But I wanna just bind this color to Button Background color, So that I don't have to have 3 duplicate Style. I did some RelativeSource Binding but didn't work.

Q1: How can i Bind this Gradient to button Background?

Q2: Is there any other Type of controller that could do this AND have color properties?

Thanks in advance.

Edit: Transition code:

<VisualStateGroup.Transitions>
<VisualTransition To="MouseOver" GeneratedDuration="0:0:0.2"/>
<VisualTransition From="MouseOver" GeneratedDuration="0:0:0.2"/>
</VisualStateGroup.Transitions>

Answer

If it were me I would probably do it something more like below so you could pass in Brush, Color, ColorName etc easier instead of swap out GradientStop's and just deal with one whole object at a time. Since Fill will only accept Color. Brush and Fill are different beasts. ;)

Something like;

Style;

<Style x:Key="CloseButton2" TargetType="{x:Type Button}">
   <Setter Property="Background" Value="Red"/>
   <Setter Property="HorizontalContentAlignment" Value="Center"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type Button}">
            <Grid>
               <!-- Left this here in case you want to use it later for something. Just set Visibility. -->
               <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                       Background="{TemplateBinding Background}" SnapsToDevicePixels="true" Visibility="Collapsed">
                  <ContentPresenter x:Name="contentPresenter" Focusable="False"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" 
                                    RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
               </Border>

               <!-- Your new magic. -->
               <Border x:Name="buttonLight" Opacity="0"
                       Background="{TemplateBinding Background}" CornerRadius="50" 
                       Width="18" Height="18" Margin="4,0,0,0"/>


               <Ellipse Name="BackgroundColor1" Margin="4,0,0,0" Width="18" Height="18">
                  <Ellipse.Fill>
                     <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                        <GradientStop Color="Transparent" Offset="0.2"/>
                        <GradientStop Color="WhiteSmoke" Offset=".9"/>
                                    </LinearGradientBrush>
                  </Ellipse.Fill>
               </Ellipse>
               <Ellipse Name="BackgroundColor2" Margin="4,0,0,0" Width="18" Height="18">
                  <Ellipse.Stroke>
                     <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                        <GradientStop Color="Transparent" Offset="1"/>
                        <GradientStop Color="WhiteSmoke" Offset="0"/>
                                    </LinearGradientBrush>
                  </Ellipse.Stroke>
               </Ellipse>
               <Ellipse Margin="4,0,0,8" Width="7" Height="7">
                  <Ellipse.Fill>
                     <LinearGradientBrush StartPoint="1,0" EndPoint="0,1">
                        <GradientStop Color="WhiteSmoke" Offset="0"/>
                        <GradientStop Color="Transparent" Offset="0.7"/>
                                    </LinearGradientBrush>
                  </Ellipse.Fill>
               </Ellipse>
               <Ellipse Margin="4,0,0,0" Width="18" Height="18" StrokeThickness="2" StrokeLineJoin="Round">
                  <Ellipse.Stroke>
                     <LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
                        <GradientStop Color="Black" Offset="0"/>
                        <GradientStop Color="Transparent" Offset="1"/>
                     </LinearGradientBrush>
                  </Ellipse.Stroke>
               </Ellipse>
               <Ellipse Margin="4,0,0,0" Width="18" Height="18" StrokeThickness="1" StrokeLineJoin="Round">
                  <Ellipse.Stroke>
                     <LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
                        <GradientStop Color="#FF333333" Offset="1"/>
                        <GradientStop Color="Transparent" Offset="0.5"/>
                     </LinearGradientBrush>
                  </Ellipse.Stroke>
               </Ellipse>
            </Grid>
            <ControlTemplate.Triggers>

               <Trigger Property="IsMouseOver" Value="true">
                  <Setter Property="Opacity" TargetName="buttonLight" Value="1"/>
               </Trigger>

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

Then, instance examples;

<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">

   <Button Style="{DynamicResource CloseButton2}"/>
   <Button Background="Green" 
           Style="{DynamicResource CloseButton2}" Margin="0,10"/>
   <Button Background="Blue" 
           Style="{DynamicResource CloseButton2}"/>

</StackPanel>

Hope this helps, cheers.

MORE INFORMATION;

You can absolutely still apply your transition, for this; you would just delete the triggers (this part in the previous example);

<!-- DELETE THIS PART -->
    <ControlTemplate.Triggers>

       <Trigger Property="IsMouseOver" Value="true">
          <Setter Property="Opacity" TargetName="buttonLight" Value="1"/>
       </Trigger>

    </ControlTemplate.Triggers>

Then just plop in your VisualStateManager somewhere inside of the Grid which I prefer to do at the top right under the <Grid> tag;

    <!-- Invoke VisualStateManager to handle it instead of Trigger as requested. -->
    <VisualStateManager.VisualStateGroups>
       <VisualStateGroup x:Name="CommonStates">
          <VisualStateGroup.Transitions>
             <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.2"/>
             <VisualTransition From="MouseOver" GeneratedDuration="0:0:0.2"/>
          </VisualStateGroup.Transitions>
          <VisualState x:Name="Normal"/>
          <VisualState x:Name="MouseOver">
             <Storyboard>
                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" 
                                               Storyboard.TargetName="buttonLight"
                                               Duration="0:0:1">
                   <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                </DoubleAnimationUsingKeyFrames>
             </Storyboard>
          </VisualState>
          <VisualState x:Name="Pressed"/>
          <VisualState x:Name="Disabled"/>
       </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

...and that's it, you're done, and still have the color functionalities etc. :)