Artur J.
Artur J.

Reputation: 71

Why I can't override Property in control when Style Property is set?

I created custom TextBlock control:

[ContentProperty("Text")]
[TemplatePart(Name = TextBlockName, Type = typeof(TextBlock))]
public class CustomTextBlock : Control
{
    #region Constraints

    private const string TextBlockName = "Container";

    #endregion

    #region Private Fields

    private TextBlock _tbValue;

    #endregion

    #region TextProperty Depenancy Property

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(CustomTextBlock),
        new PropertyMetadata(null, TextChangedCallback));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    private static void TextChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var control = (CustomTextBlock)dependencyObject;
        control.UpdateText();
    }

    #endregion

    #region TextWrapping Dependency Property

    public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
        "TextWrapping",
        typeof(TextWrapping),
        typeof(CustomTextBlock), null);


    public TextWrapping TextWrapping
    {
        get { return (TextWrapping)GetValue(TextWrappingProperty); }
        set { SetValue(TextWrappingProperty, value); }
    }

    #endregion

    public static readonly DependencyProperty TextStyleProperty =
   DependencyProperty.Register("TextStyle", typeof(Style), typeof(CustomTextBlock), new PropertyMetadata(null));

    public Style TextStyle
    {
        get { return (Style)GetValue(TextStyleProperty); }
        set { SetValue(TextStyleProperty, value); }
    }

    #region TextAlignment Dependency Property

    public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
        "TextAlignment",
        typeof(TextAlignment),
        typeof(CustomTextBlock),
        null);


    public TextAlignment TextAlignment
    {
        get { return (TextAlignment)GetValue(TextAlignmentProperty); }
        set { SetValue(TextAlignmentProperty, value); }
    }

    #endregion

    #region Constructor

    public CustomTextBlock()
    {
        DefaultStyleKey = typeof(CustomTextBlock);
    }

    #endregion

    #region Methods

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _tbValue = GetTemplateChild(TextBlockName) as TextBlock;
        UpdateText();
        UpdateTextStyle();
    }

    private void UpdateTextStyle()
    {
        if (_tbValue != null && TextStyle != null)
            _tbValue.Style = TextStyle;
    }

    private void UpdateText()
    {
        if (this._tbValue != null)
        {
            this._tbValue.Inlines.Clear();
            if (!String.IsNullOrEmpty(this.Text))
            {
                // some stuff
            }
        }
    }

    #endregion
} 

The template looks like:

<Style  TargetType="customControls:CustomTextBlock" >
    <Setter Property="Foreground" Value="{StaticResource DefaultFontBrush}"/>
    <Setter Property="FontSize" Value="30"/>
    <Setter Property="FontFamily" Value="{StaticResource DefaultFont}"/>
    <Setter Property="TextWrapping" Value="NoWrap"/>
    <Setter Property="TextAlignment" Value="Left"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="customControls:CustomTextBlock">
                <TextBlock Name="Container"
                           TextWrapping="{TemplateBinding TextWrapping}"
                           TextAlignment="{TemplateBinding TextAlignment}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Everything is fine but there is a problem when I try to use TextStyle and other properties. My expectations are that this particular properties override properties in TextStyle, but they not.

 <control:CustomTextBlock 
               TextStyle="{StaticResource StyleWithYellowForeground}"
                Foreground="Red"
                FontSize="20"
                Text="Sample text"
                />

Style "StyleWithYellowForeground" has a few setters but it has there Foreground set to yellow. In my declaration I set Foreground to red because I want to override "Foreground" it this style but it doesn't work (it's still has yellow foreground).

Do you know how to fix this ?


My point is to achieve some priority of declaring styles (from the most important which overrides others):

1)Inline properties set in control declaration 2)TextStyle property set in control declaration 3)Defaut properties set in default style of control

Upvotes: 3

Views: 1775

Answers (1)

yasen
yasen

Reputation: 3580

The problem is that setting TextStyle changes the Style of the TextBlock itself, so the TextBlock itself has the Foreground set to Yellow. Changing the Foreground of the whole control does not affect that, because it is only a container of the TextBlock. The style of a control overrides values from its container. To change the Foreground, you would need to change the Foreground property of the actual TextBlock.

What are you trying to achieve? Maybe it can be simplified somehow... You may be able to make the Foreground of the whole control override the Foreground of the TextBlock, but that does not seem like a good idea.

Edit

Here are 2 ways to solve this:

  • Add a property ForegroundOverride and once it's set to something, set the TextBlock's Foreground property, which will override the value from the Style. If it's not set, the value from the Style will be used.

  • Set the TextStyle like this:

    <local:CustomTextBlock.TextStyle>
        <Style TargetType="TextBlock" BasedOn="{StaticResource StyleWithYellowForeground}">
            <Setter Property="Foreground" Value="Red" />
        </Style>
    </local:CustomTextBlock.TextStyle>
    

    This way, you can reuse the styles that you want and also override the properties that you want. It is not as cool as simply setting Foreground but it will work fine.

Edit 2

Maybe you can try using the default TextBlock (in which all styles and in-line properties will work), and use attached/blend behaviours to achieve what you want? Here is some info about them: http://www.jayway.com/2013/03/20/behaviors-in-wpf-introduction/.

Upvotes: 1

Related Questions