1

For example, I have a pretty simple custom control called LabeledTextBox that inherits from TextBox. It's just a TextBox with a watermark in it that has had its styling stripped down to a minimal appearance. I want to make it the base class for all of the other textboxes in my app.

enter image description here

I have another control, SearchTextBox, which inherits from that. It is quite similar, but has some controls on the right side and some triggers for the functionality of a typical search box:

enter image description here

Already I have a problem, because SearchTextBox has no label in the UI. If I replace the ScrollViewer with a LabeledTextBox and make it BasedOn="{StaticResource {x:Type ui:LabeledTextBox}}", that works, but only if I hook up with the Text property in SearchTextBox:

public override void OnApplyTemplate()
{
    LabeledTextBox ltb = GetTemplateChild("PART_LabeledTextBox") as LabeledTextBox;
    Binding binding = new Binding("Text")
    {
        Source = this,
        Mode = BindingMode.TwoWay,
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    };
    ltb.SetBinding(LabeledTextBox.TextProperty, binding);
    base.OnApplyTemplate();
}

So obviously, I am missing something here... Since it is based on LabeledTextBox, why doesn't SearchTextBox have a label by default in the UI (even though it does have the label property)? Or are you not meant to inherit styles like this?

I also notice that if I create a custom control that inherits from LabeledTextBox and whose style is empty (<Style TargetType="{x:Type ui:Test}" BasedOn="{StaticResource {x:Type ui:LabeledTextBox}}" />), then it looks just like a LabeledTextBox... So shouldn't there be something I can put in the template that basically says, "put everything from LabeledTextBox here, then add some extra stuff defined afterward," just like in normal class inheritance?

Relevant XAML:

LabeledTextBox in Generic.XAML:

<Style TargetType="{x:Type ui:LabeledTextBox}">
    <Setter Property="BorderBrush" Value="{StaticResource MutedColorBrush}" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    <Setter Property="VerticalContentAlignment" Value="Stretch" />
    <Setter Property="LabelText" Value="Label text..." />
    <Setter Property="LabelTextColor" Value="{StaticResource MutedColorBrush}" />
    <Setter Property="Margin" Value="0" />
    <Setter Property="Padding" Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ui:LabeledTextBox}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>

                        <Label x:Name="PART_Label"
                                   Grid.Column="0"
                                   Foreground="{TemplateBinding LabelTextColor}"
                                   Content="{TemplateBinding LabelText}"
                                   Padding="0"
                                   Margin="0"
                                   FontStyle="Italic"
                                   HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                   VerticalAlignment="Center"
                                   Visibility="Hidden"/>
                        <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0"
                                          Background="{TemplateBinding Background}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                          HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="Center"
                                          Margin="0" Padding="0"/>
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Text" Value="" />
                            <Condition Property="IsFocused" Value="False" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="PART_Label" Property="Visibility" Value="Visible" />
                        <Setter TargetName="PART_ContentHost" Property="Visibility" Value="Hidden" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

SearchTextBox in Generic.XAML:

<Style TargetType="{x:Type ui:SearchTextBox}" BasedOn="{StaticResource {x:Type ui:LabeledTextBox}}">
    <Setter Property="AllowDrop" Value="True" />
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
    <Setter Property="BorderBrush" Value="{StaticResource MutedColorBrush}" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    <Setter Property="LabelText" Value="Search for..." />
    <Setter Property="LabelTextColor" Value="{StaticResource MutedColorBrush}" />
    <Setter Property="Padding" Value="1" />
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
    <Setter Property="Source" Value="search.png" />
    <Setter Property="ButtonSource" Value="searchx-black.png" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ui:SearchTextBox}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
                        </Grid.ColumnDefinitions>

                        <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0"
                                          Background="{TemplateBinding Background}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                          HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="Center"
                                          Margin="0" Padding="0"/>
                        <Image x:Name="PART_Image" Grid.Column="1"
                               Source="{TemplateBinding Source}"
                               Visibility="Hidden"
                               HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                               Width="15" Height="15" />
                        <ui:SquareButton x:Name="PART_Button" Grid.Column="1" Width="15" Height="15" ImageHeight="10" ImageWidth="10" Padding="0" Focusable="False" Source="{TemplateBinding ButtonSource}" />
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="Text" Value="">
                        <Setter TargetName="PART_Image" Property="Visibility" Value="Visible" />
                        <Setter TargetName="PART_Button" Property="Visibility" Value="Hidden" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Daniel Ward
  • 274
  • 2
  • 12
  • It sounds like the problem could be that you are trying to do this exclusively through styles. It might work better as user controls (with the appropriate DPs). – BradleyDotNET Jun 12 '14 at 22:05
  • I would really rather avoid user controls if I can. What's the point of BasedOn if you can't change the control template? – Daniel Ward Jun 12 '14 at 22:09
  • If memory serves, when you set something like a control template it overrides anything it gets from BasedOn for it (like the base control template). Its not true inheritance. BasedOn sounds cool, but it isn't actually as powerful as you might like to think. – BradleyDotNET Jun 12 '14 at 22:11
  • So there's not really any way to do what I'm trying to do with inheritance without a workaround like in the OnApplyTemplate I've currently got? – Daniel Ward Jun 12 '14 at 22:15
  • I strongly suspect not. I'm not a super-expert on styles (and BasedOn) but my experience is that when you override, you **really** override. Maybe someone else can give you a better or more definitive answer though. – BradleyDotNET Jun 12 '14 at 22:16
  • No, you can't inherit templates this way. Only replace them. See for example here: http://stackoverflow.com/questions/2034511/how-to-inherit-a-control-template – vesan Jun 13 '14 at 03:25
  • What would be a good solution then for what I'm trying to accomplish? – Daniel Ward Jun 13 '14 at 04:40

0 Answers0