简体   繁体   中英

Having an issue with my TextBox control template

I am working on a simple textBox template, which is just two borders, one with a gradient background. Now, my specific issue is that I want to be able to set the foreground color of the textBox to whatever color I want and have it work correctly. However, I can't seem to get both the disabled foreground and enabled foreground colors to work together. If I set the foreground to red for example, when I disable the textBox, the foreground doesn't get changed to my disabled color. I tried binding the foreground in the IsEnabled="true" trigger but that doesn't seem to work. The foreground always stays red, no matter if the textBox is enabled or not.

Can you please take a look at the below template and tell me what I am doing wrong? Also, please point out to me any other mistakes I may have made since I am new at creating templates.

Thanks a lot.

  <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
  <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
  <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
  <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />


<Style x:Key="TextBoxControlTemplate1" TargetType="{x:Type TextBox}">
  <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
  <Setter Property="AllowDrop" Value="true"/>
  <Setter Property="Background" Value="#00000000"/>
  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
  <Setter Property="VerticalContentAlignment" Value="Stretch"/>
  <Setter Property="FontFamily" Value="Segoe UI"/>
  <Setter Property="FontSize" Value="12"/>
  <Setter Property="Padding" Value="8,5,3,3"/>
  <Setter Property="BorderThickness" Value="0"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type TextBox}">
        <Grid>
          <Border BorderBrush="#FF000000" BorderThickness="2,2,2,2" CornerRadius="5,5,5,5" Padding="0,0,0,0" Width="Auto" Height="Auto" Background="#FF000000"/>
          <Border x:Name="Border" BorderBrush="#FFFFFFFF" BorderThickness="1,1,1,1" CornerRadius="5,5,5,5" Padding="0,0,0,0" Width="Auto" Height="Auto" Margin="2,2,2,2">
            <Border.Background>
              <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF000000" Offset="0"/>
                <GradientStop Color="#FF4D4D4D" Offset="1"/>
              </LinearGradientBrush>
            </Border.Background>
          </Border>
          <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
        </Grid>
        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
            <Setter Property="BorderBrush" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
            <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
          </Trigger>
          <Trigger Property="IsEnabled" Value="True">
            <Setter Property="Foreground" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>


<TextBox Text="TEST" TextWrapping="Wrap" Canvas.Top="293.761" Canvas.Left="112" Style="{DynamicResource TextBoxControlTemplate1}" Height="28.724" Width="232.25" IsTabStop="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" IsEnabled="True" Foreground="#FFFF0000"/>

There are a few different issues here all working against you. The first is that you're setting a specific Foreground value on your control instance, which has a higher priority than values set from Trigger s in your Style for properties of the control itself. This is different than properties set on elements inside the ControlTemplate , like "Border". The way you're using Trigger s to set the Border properties is illustrating that. Normally you would also want to use TemplateBinding s to pull in values set on your control instance as defaults, like Background , which are currently being ignored.

To switch between two values of a property on your styled control, like you want to do with Foreground , you can use a Setter and a Trigger in your Style to provide a default and alternate value. This is still subject to being overridden by a value set on an instance. If you want to disallow instances overriding a Trigger set it up like the "Border" Trigger where you're setting values on elements inside the ControlTemplate .

The last change I'd recommend is switching to StaticResource for the Brushes you're pulling into your Style . In this case it probably won't make a difference, but in some cases a default Style can get pulled into a context that doesn't have any reference to the surrounding Resources from the file it was declared in. Using Static will guarantee that it will include those Resources no matter where it gets used. You may not run into this but it's a good habit to get into when setting up Styles/Templates like this.

Here's your code with those improvements:

<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />

<Style x:Key="TextBoxControlTemplate1" TargetType="{x:Type TextBox}">
    <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="Background">
        <Setter.Value>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF000000" Offset="0"/>
                <GradientStop Color="#FF4D4D4D" Offset="1"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="FontFamily" Value="Segoe UI"/>
    <Setter Property="FontSize" Value="12"/>
    <Setter Property="Padding" Value="8,5,3,3"/>
    <Setter Property="BorderThickness" Value="2"/>
    <Setter Property="BorderBrush" Value="#FF000000"/>
    <Setter Property="Foreground" Value="Red" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Grid>
                    <!--Take advantage of containment when possible to let the layout engine help you!-->
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Padding="0" Background="#FF000000"/>
                    <Border x:Name="Border" BorderBrush="#FFFFFFFF" BorderThickness="1" CornerRadius="5" Padding="0" Margin="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"/>
                    <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Background" Value="{StaticResource DisabledBackgroundBrush}" TargetName="Border"/>
                        <Setter Property="BorderBrush" Value="{StaticResource DisabledBackgroundBrush}" TargetName="Border"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
        </Trigger>
    </Style.Triggers>
</Style>

And the TextBox using only the Style settings:

<TextBox Text="TEST" TextWrapping="Wrap" Canvas.Top="293.761" Canvas.Left="112" Style="{DynamicResource TextBoxControlTemplate1}" 
         Height="28.724" Width="232.25" IsTabStop="False" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />

A couple ideas to try:

  1. Get rid of one of your triggers. Having two opposing triggers may not be a good idea. I would set the default Background , BorderBrush , and Foreground directly on your Border declaration, and remove the Enabled="True" trigger. Then, debugging is only a matter of getting the Enabled="False" trigger right.

  2. Add the TargetName property to setter for your Enabled="False" trigger.

  3. This is a longshot, but use UIElement.IsEnabled instead of just IsEnabled , like this: <Trigger Property="UIElement.IsEnabled"> .

Hope something I've said here helps!

The possible reason is that your DisabledBackgroundBrush is not visible at the point where you use your style. Please try to add the styles into the ControlTemplate 's resources:

<ControlTemplate TargetType="{x:Type TextBox}">
    <ControlTemplate.Resources>
        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
        ...
    </ControlTemplate.Resources>
    ...

By the way, your control template doesn't honour the values of your properties. For example, you should perhaps use something like

<ScrollViewer Margin="{TemplateBinding Padding}" x:Name="PART_ContentHost"/>

in your control template.

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