40

I've seen the following thread which is related to my question:

WPF ComboBox: background color when disabled

The above deals with changing the Content Template for a ComboBox. I am working with WPF, am somewhat new to Styles and Templates, and I want to change the dull gray background color of a disabled TextBox to some other color. We use TextBoxes frequently in our application and we find the default color settings difficult to read.

I've crafted the following solution attempt. But of course, it does not work. Can someone give me an opinion on why?

Upload Image

Community
  • 1
  • 1

7 Answers7

41

Unfortunately for the TextBox control, it appears like it's not as simple as just adding a trigger and changing the Background color when the trigger condition is true. You have to override the entire ControlTemplate to achieve this. Below is one example on how you might do this:

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="White" />
        <Style TargetType="TextBox">
            <Setter Property="Background" Value="White"/>
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
                                             BorderBrush="{TemplateBinding BorderBrush}" 
                                             Background="{TemplateBinding Background}" 
                                             SnapsToDevicePixels="true">
                            <ScrollViewer Name="PART_ContentHost" Background="{TemplateBinding Background}" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Value="{StaticResource DisabledBackgroundBrush}" Property="Background" />
                                <Setter Value="{StaticResource DisabledForegroundBrush}" Property="Foreground" />
                                <Setter TargetName="PART_ContentHost" Property="Background" Value="Blue"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>


    </Window.Resources>

    <Canvas>
        <TextBox Text="TextBox" IsEnabled="False"/>
        <TextBox Text="TextBox" IsEnabled="True" Canvas.Top="25"/>
    </Canvas>            
</Window>     

EDIT:

In response to your question, I tried adding the ComboBox style to my original answer above and I was able to integrate it without errors. I'm not sure though if it behaves like you wanted it to. I just copy-pasted what's in the link you specified.

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflow"
        Title="MainWindow" Height="350" Width="525"
        x:Name="window">
    <Window.Resources>
        <SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="Blue" />

        <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#CCC" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#CCC" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#AAA" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#BBB" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="0.1"/>
                    <GradientStop Color="#EEE" Offset="0.9"/>
                    <GradientStop Color="#FFF" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />

        <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />

        <!-- Border Brushes -->

        <LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#CCC" Offset="0.0"/>
                    <GradientStop Color="#444" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0" EndPoint="1,0">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#CCC" Offset="0.0"/>
                    <GradientStop Color="#444" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#777" Offset="0.0"/>
                    <GradientStop Color="#000" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#444" Offset="0.0"/>
                    <GradientStop Color="#888" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />

        <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

        <SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />

        <!-- Miscellaneous Brushes -->
        <SolidColorBrush x:Key="GlyphBrush" Color="#444" />

        <SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

        <ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="20" />
                </Grid.ColumnDefinitions>
                <Border
      x:Name="Border" 
      Grid.ColumnSpan="2"
      CornerRadius="2"
      Background="{StaticResource NormalBrush}"
      BorderBrush="{StaticResource NormalBorderBrush}"
      BorderThickness="1" />
                <Border 
      Grid.Column="0"
      CornerRadius="2,0,0,2" 
      Margin="1" 
      Background="{StaticResource WindowBackgroundBrush}" 
      BorderBrush="{StaticResource NormalBorderBrush}"
      BorderThickness="0,0,1,0" />
                <Path 
      x:Name="Arrow"
      Grid.Column="1"     
      Fill="{StaticResource GlyphBrush}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
      Data="M 0 0 L 4 4 L 8 0 Z"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
                </Trigger>
                <Trigger Property="ToggleButton.IsChecked" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
                    <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
                    <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                    <Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

        <ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
            <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
        </ControlTemplate>

        <Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
            <Setter Property="SnapsToDevicePixels" Value="true"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
            <Setter Property="MinWidth" Value="120"/>
            <Setter Property="MinHeight" Value="20"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ComboBox">
                        <Grid>
                            <ToggleButton 
            Name="ToggleButton" 
            Template="{StaticResource ComboBoxToggleButton}" 
            Grid.Column="2" 
            Focusable="false"
            IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
            ClickMode="Press">
                            </ToggleButton>
                            <ContentPresenter
            Name="ContentSite"
            IsHitTestVisible="False" 
            Content="{TemplateBinding SelectionBoxItem}"
            ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
            ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
            Margin="3,3,23,3"
            VerticalAlignment="Center"
            HorizontalAlignment="Left" />
                            <TextBox x:Name="PART_EditableTextBox"
            Style="{x:Null}" 
            Template="{StaticResource ComboBoxTextBox}" 
            HorizontalAlignment="Left" 
            VerticalAlignment="Center" 
            Margin="3,3,23,3"
            Focusable="True" 
            Background="Transparent"
            Visibility="Hidden"
            IsReadOnly="{TemplateBinding IsReadOnly}"/>
                            <Popup 
            Name="Popup"
            Placement="Bottom"
            IsOpen="{TemplateBinding IsDropDownOpen}"
            AllowsTransparency="True" 
            Focusable="False"
            PopupAnimation="Slide">
                                <Grid 
              Name="DropDown"
              SnapsToDevicePixels="True"                
              MinWidth="{TemplateBinding ActualWidth}"
              MaxHeight="{TemplateBinding MaxDropDownHeight}">
                                    <Border 
                x:Name="DropDownBorder"
                Background="{StaticResource WindowBackgroundBrush}"
                BorderThickness="1"
                BorderBrush="{StaticResource SolidBorderBrush}"/>
                                    <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
                                        <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                                    </ScrollViewer>
                                </Grid>
                            </Popup>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasItems" Value="false">
                                <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                            </Trigger>
                            <Trigger Property="IsGrouping" Value="true">
                                <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                            </Trigger>
                            <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
                                <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
                                <Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
                            </Trigger>
                            <Trigger Property="IsEditable"
               Value="true">
                                <Setter Property="IsTabStop" Value="false"/>
                                <Setter TargetName="PART_EditableTextBox" Property="Visibility"    Value="Visible"/>
                                <Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
            </Style.Triggers>
        </Style>

        <Style TargetType="TextBox">
            <Setter Property="Background" Value="White"/>
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
                                             BorderBrush="{TemplateBinding BorderBrush}" 
                                             Background="{TemplateBinding Background}" 
                                             SnapsToDevicePixels="true">
                            <ScrollViewer Name="PART_ContentHost" Background="{TemplateBinding Background}" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Value="{StaticResource DisabledBackgroundBrush}" Property="Background" />
                                <Setter Value="{StaticResource DisabledForegroundBrush}" Property="Foreground" />
                                <Setter TargetName="PART_ContentHost" Property="Background" Value="{StaticResource DisabledBackgroundBrush}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <StackPanel>
        <TextBox IsEnabled="False">TextBox</TextBox>
        <ComboBox IsEnabled="False"/>
    </StackPanel>
</Window>
ASanch
  • 10,233
  • 46
  • 32
  • The above example did work in my tests. Thank you! I wondered if I might add a follow up. I need similar functionality for the ComboBox, and this appears to be handed to me on a silver platter with this link: http://msdn.microsoft.com/en-us/library/ms752094%28v=VS.85%29.aspx However, I am having difficulty combining the two. If I place both the Style above for the TextBox, and the Content Template from the referenced link into one "Window.Resources" file, it produces syntax errors. Can you give me guidance on this? – Code-an the Barbarian Sep 20 '10 at 19:38
  • What errors are you getting? Well, I'll try to integrate the ControlTemplate for the ComboBox to my example above and see if I can make it work. I'll get back to you on this. – ASanch Sep 20 '10 at 20:10
  • Check out the XAML in my EDIT above. – ASanch Sep 20 '10 at 20:16
  • KarmicPuppet, You are a genius. All the above works. I must have somehow mishandled the Xml editing, and not closed a tag or something. This issue is resolved, I wonder if you can take it on yourself to answer a high level question? Can you recommend a reference that would help a person gain a mastery of Xaml styles/Control Templates? Browsing sites is helpful, but none of it seems to give the level of knowledge that would allow a person to craft the above solution from a "blank" page. Thank you again for the assistance. – Code-an the Barbarian Sep 21 '10 at 11:35
  • Well, to tell you the truth, I really don't know of any specific references that I could recommend. I personally have been used to these styles/controltemplates business by writing a bunch of custom controls for a lot of demo-apps/prototypes and my reference materials are usually either MSDN or a Google search of specific topics. So, sorry I couldn't help you out on that. (by the way, is there any chance you can accept my answer above as correct? thanks). – ASanch Sep 21 '10 at 15:41
36

You can use the below snippet :

Instead of checking for IsEnable property, use IsReadonly property of TextBox control.

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Background" Value="LightSkyBlue" />
    <Style.Triggers>
        <Trigger Property="IsReadOnly" Value="True">
            <Setter Property="Background" Value="Red" />
        </Trigger>
    </Style.Triggers>
</Style>

If You need to apply it for all the textbox controls, use the above code. For specific textbox, just set the key and apply the style to that Textbox.

edtheprogrammerguy
  • 5,957
  • 6
  • 28
  • 47
Hitesh Patil
  • 361
  • 3
  • 2
  • In my case that won't work, because the control is part of a group of controls in a panel that is disabled. When the panel is disabled, the textfield is implicitly disabled, and as soon as it's disabled it reverts to, for example, a solid white background instead of the translucent background I set. – Triynko Feb 13 '14 at 19:16
  • Setting IsReadonly will mean Windows 7/10 will still render the blue "input control" border around the TexBox on mouseover which may not be a desired behavior. Just something to be aware of. – user1151923 Dec 03 '20 at 11:41
8

For this situation I like to set Focusable=false and set the background color to my desired value (in a data-bound trigger). This is maybe a little hacky, but so is rewriting the control template for the whole TextBox. An alternative to Focusable is IsReadyOnly, but that doesn't work for as many controls. It does ensure the caret disappears, though.

Elmo
  • 6,409
  • 16
  • 72
  • 140
Steve Westbrook
  • 1,698
  • 2
  • 20
  • 21
4

You never use the ControlTemplate you defined. Also, you want a Style, not (necessarily) a ControlTemplate.

I think you want something like the following:

<Canvas.Resources>
        <SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="White" />
        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
                    <Setter Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
                </Trigger>
            </Style.Triggers>
        </Style>
</Canvas.Resources>
Wonko the Sane
  • 10,623
  • 8
  • 67
  • 92
  • 1
    Wonko the Sane (that is a great screen name), ... I could not get the above example to work (it didn't appear to modify anything). It is elegant, however, and much shorter than the response below, so it would be great ... but no luck. Thank you for your response. – Code-an the Barbarian Sep 20 '10 at 19:37
  • @user452763 (not quite as memorable a screen name) - yes, karmicpuppet is correct. I believe that it has to do with the way the TextBox control is comprised of other controls. – Wonko the Sane Sep 20 '10 at 20:00
3

If you look at the template of the textbox, you will notice that the template has a trigger for the IsEnabled property False, and sets it's border element "Bd" background color to SystemColors.ControlBrushKey.

If you override this color in a style, it will achieve what you want to do.

<Style TargetType="{x:Type TextBox}">
  <Style.Resources>
    <SolidColorBrush 
       x:Key="{x:Static SystemColors.ControlBrushKey}" 
       Color="{StaticResource MyNewTextBoxBackgroundColor}" />
  </Style.Resources>
</Style>
bedo
  • 866
  • 8
  • 15
2

Try to avoid redefining control templates where you can. They tend to add a lot of code overhead and can become difficult to maintain over time.

I would use the following code in the Loaded event:

ClassicBorderDecorator o = VisualTreeHelper.GetChild(this.textBox1, 0) as ClassicBorderDecorator;
if (o != null)
{
    o.Background = new SolidColorBrush(Colors.Transparent);
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Greg Sansom
  • 20,442
  • 6
  • 58
  • 76
0
By adding <Window.Resources> after <Window> and before <Grid> will make your text box behave like normal winforms textbox.

<Window x:Class="..." Height="330" Width="600" Loaded="Window_Loaded" WindowStartupLocation="CenterOwner">

<Window.Resources>
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="IsReadOnly" Value="True">
                <Setter Property="Background" Value="LightGray" />
            </Trigger>
            <Trigger Property="IsReadOnly" Value="False">
                <Setter Property="Background" Value="White" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid>

Code taken from following web page:

wpf: Selecting the Text in TextBox with IsReadOnly = true?

And style modified to match winforms. (Their appearance is enabled = false, not readonly = true)

And of course your textbox must have IsReadOnly="True" attribute set.

Community
  • 1
  • 1
TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62