locked
Can't binding to Style property of Custom Button control in code RRS feed

  • Question

  • I want to bind the Style property of a custom Button(which is derived by Button) to a property in my view mode in code behind. Here is what I am doing:

    1. Create a custom type MyBtn and override OnApplyTemplate method:

        public sealed class MyBtn : Button
        {
            protected override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
    
                Debug.Assert(this.DataContext is Person);
    
                Debug.WriteLine("OnApplyTemplate: begin SetBinding()...");
    
                var c = (StyleConverter)Application.Current.Resources["StyleConverter"];
                var binding = new Binding() { Path = new PropertyPath("Id"), Converter = c };
                this.SetBinding(MyBtn.StyleProperty, binding);
    
                Debug.WriteLine("OnApplyTemplate: end SetBinding()...");
            }
        }


    2. Get a copy of Button Template, set its key to "MyBtnStyleBase", and change the TargetType to "local:MyBtn"(int two places):

           <Style x:Key="MyBtnStyleBase" TargetType="local:MyBtn">
                <Setter Property="Background"
                        Value="{ThemeResource ButtonBackgroundThemeBrush}" />
                <Setter Property="Foreground"
                        Value="{ThemeResource ButtonForegroundThemeBrush}" />
                <Setter Property="BorderBrush"
                        Value="{ThemeResource ButtonBorderThemeBrush}" />
                <Setter Property="BorderThickness"
                        Value="{ThemeResource ButtonBorderThemeThickness}" />
                <Setter Property="Padding"
                        Value="12,4,12,4" />
                <Setter Property="HorizontalAlignment"
                        Value="Left" />
                <Setter Property="VerticalAlignment"
                        Value="Center" />
                <Setter Property="FontFamily"
                        Value="{ThemeResource ContentControlThemeFontFamily}" />
                <Setter Property="FontWeight"
                        Value="SemiBold" />
                <Setter Property="FontSize"
                        Value="{ThemeResource ControlContentThemeFontSize}" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="local:MyBtn">
                            <Grid>
                                <VisualStateManager.VisualStateGroups>
                                    <VisualStateGroup x:Name="CommonStates">
                                        <VisualState x:Name="Normal" />
                                        <VisualState x:Name="PointerOver">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
                                                                               Storyboard.TargetName="Border">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonPointerOverBackgroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                                                               Storyboard.TargetName="ContentPresenter">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="Pressed">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
                                                                               Storyboard.TargetName="Border">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonPressedBackgroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                                                               Storyboard.TargetName="ContentPresenter">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonPressedForegroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="Disabled">
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
                                                                               Storyboard.TargetName="Border">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonDisabledBackgroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
                                                                               Storyboard.TargetName="Border">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonDisabledBorderThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                                                               Storyboard.TargetName="ContentPresenter">
                                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                                            Value="{ThemeResource ButtonDisabledForegroundThemeBrush}" />
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                    <VisualStateGroup x:Name="FocusStates">
                                        <VisualState x:Name="Focused">
                                            <Storyboard>
                                                <DoubleAnimation Duration="0"
                                                                 To="1"
                                                                 Storyboard.TargetProperty="Opacity"
                                                                 Storyboard.TargetName="FocusVisualWhite" />
                                                <DoubleAnimation Duration="0"
                                                                 To="1"
                                                                 Storyboard.TargetProperty="Opacity"
                                                                 Storyboard.TargetName="FocusVisualBlack" />
                                            </Storyboard>
                                        </VisualState>
                                        <VisualState x:Name="Unfocused" />
                                        <VisualState x:Name="PointerFocused" />
                                    </VisualStateGroup>
                                </VisualStateManager.VisualStateGroups>
                                <Border x:Name="Border"
                                        BorderBrush="{TemplateBinding BorderBrush}"
                                        BorderThickness="{TemplateBinding BorderThickness}"
                                        Background="{TemplateBinding Background}"
                                        Margin="3">
                                    <ContentPresenter x:Name="ContentPresenter"
                                                      AutomationProperties.AccessibilityView="Raw"
                                                      ContentTemplate="{TemplateBinding ContentTemplate}"
                                                      ContentTransitions="{TemplateBinding ContentTransitions}"
                                                      Content="{TemplateBinding Content}"
                                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                      Margin="{TemplateBinding Padding}"
                                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                                </Border>
                                <Rectangle x:Name="FocusVisualWhite"
                                           IsHitTestVisible="False"
                                           Opacity="0"
                                           StrokeDashOffset="1.5"
                                           StrokeEndLineCap="Square"
                                           Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
                                           StrokeDashArray="1,1" />
                                <Rectangle x:Name="FocusVisualBlack"
                                           IsHitTestVisible="False"
                                           Opacity="0"
                                           StrokeDashOffset="0.5"
                                           StrokeEndLineCap="Square"
                                           Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
                                           StrokeDashArray="1,1" />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

    3. Define a value converter which will return a Style object:

        public sealed class StyleConverter : IValueConverter
        {
            public Style BaseStyle { get; set; }
    
            public object Convert(object value, Type targetType, object parameter, string language)
            {
                System.Diagnostics.Debug.WriteLine("StyleConverter: value = {0}", value);
                var s = new Style(typeof(MyBtn));
                s.BasedOn = BaseStyle;
                s.Setters.Add(new Setter() { Property = MyBtn.BackgroundProperty, Value = new SolidColorBrush(Colors.Green) });
                s.Setters.Add(new Setter() { Property = MyBtn.ForegroundProperty, Value = new SolidColorBrush(Colors.Red) });
                return s;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, string language)
            {
                throw new NotImplementedException();
            }
        }


    4. Define a simple view model for testing:

        public class Person
        {
            public int Id { get; set; }
        }

    5. Create two more static resources in XAML:

            <local:Person x:Key="person" />
    
            <local:StyleConverter x:Key="StyleConverter"
                                  BaseStyle="{StaticResource MyBtnStyleBase}" />

    6. Put a MyBtn in main page:

    <Page x:Class="App1.MainPage"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:local="using:App1">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <local:MyBtn DataContext="{StaticResource person}"
                         Content="{Binding Id}" />
        </Grid>
    
    </Page>

    That's all.The above code would run without any runtime error. But I can't see my button on the page. It just would not show itself at all! But when I set the binding to Style property using XAML, everything is fine, just as expected. My question are: why I can't set the binding in code behind? What is wrong with my code? Really stuck here. Any idea would be appreciated.


    Sunday, November 10, 2013 2:56 PM

Answers

  • @Anne, thanks for the quick reply.

    I can't comment that line, because I need to make my new style inherit from the base style.

    I have fixed the problem. When I move the code from  OnApplyTemplate() to OnContentChanged(), everything begin to work as expected. It seems OnApplyTemplate() is not the right place to set the Template of a control. Probably this method is triggered just when a control's Template is set.

    Anyway, thanks!

     
    • Marked as answer by Anne Jing Tuesday, November 12, 2013 1:24 AM
    Monday, November 11, 2013 1:00 PM

All replies

  • Hi,

    I reproduce your project. You should comment the code “s.BasedOn =
    BaseStyle” in Convert method. Code like below:

      public object Convert(object value, Type targetType, object parameter, string language)
            {
                System.Diagnostics.Debug.WriteLine("StyleConverter: value = {0}", value);
                var s = new Style(typeof(MyBtn));
                //s.BasedOn = BaseStyle;
                s.Setters.Add(new Setter() { Property = MyBtn.BackgroundProperty, Value = new SolidColorBrush(Colors.Green) });
                s.Setters.Add(new Setter() { Property = MyBtn.ForegroundProperty, Value = new SolidColorBrush(Colors.Red) });
                return s;
            }

    Best Wishes!


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey. Thanks<br/> MSDN Community Support<br/> <br/> Please remember to &quot;Mark as Answer&quot; the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.



    • Edited by Anne Jing Monday, November 11, 2013 7:19 AM edit
    Monday, November 11, 2013 7:18 AM
  • @Anne, thanks for the quick reply.

    I can't comment that line, because I need to make my new style inherit from the base style.

    I have fixed the problem. When I move the code from  OnApplyTemplate() to OnContentChanged(), everything begin to work as expected. It seems OnApplyTemplate() is not the right place to set the Template of a control. Probably this method is triggered just when a control's Template is set.

    Anyway, thanks!

     
    • Marked as answer by Anne Jing Tuesday, November 12, 2013 1:24 AM
    Monday, November 11, 2013 1:00 PM