Prevent a readonly textbox from being grayed out in Silverlight

17,350

Solution 1

There are a couple of options in Silverlight 2, the simplest would be to use a TextBlock since that's only ever readonly.

If you need a TextBox then what you need to do is give it a different style that doesn't do the grey affect.

To do this open up blend. right click on your TextBox and select Edit Control Parts (Template) -> Edit a Copy... Call the new style whatever you want.

You then want to edit this new style and delete the border called "ReadOnlyVisualElement" and delete the storyboard that alters the opacity property of that border.

Hope this helps.

Added Style XAML

    <Style x:Key="ReadOnlyStyle" TargetType="TextBox">
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Background" Value="#FFFFFFFF"/>
        <Setter Property="Foreground" Value="#FF000000"/>
        <Setter Property="Padding" Value="2"/>
        <Setter Property="BorderBrush">
            <Setter.Value>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FFA3AEB9" Offset="0"/>
                    <GradientStop Color="#FF8399A9" Offset="0.375"/>
                    <GradientStop Color="#FF718597" Offset="0.375"/>
                    <GradientStop Color="#FF617584" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TextBox">
                    <Grid x:Name="RootElement">
                        <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup x:Name="CommonStates">
                                <vsm:VisualState x:Name="Normal"/>
                                <vsm:VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
                                            <SplineColorKeyFrame KeyTime="0" Value="#FF99C1E2"/>
                                        </ColorAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="ReadOnly">
                                    <Storyboard>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                            <vsm:VisualStateGroup x:Name="FocusStates">
                                <vsm:VisualState x:Name="Focused">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="Unfocused">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                            <vsm:VisualStateGroup x:Name="ValidationStates">
                                <vsm:VisualState x:Name="Valid"/>
                                <vsm:VisualState x:Name="InvalidUnfocused">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="InvalidFocused">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <System:Boolean>True</System:Boolean>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                        </vsm:VisualStateManager.VisualStateGroups>
                        <Border x:Name="Border" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1">
                            <Grid>
                                <Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
                                    <ScrollViewer x:Name="ContentElement" BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}"/>
                                </Border>
                            </Grid>
                        </Border>
                        <Border x:Name="DisabledVisualElement" IsHitTestVisible="False" Opacity="0" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}"/>
                        <Border x:Name="FocusVisualElement" Margin="1" IsHitTestVisible="False" Opacity="0" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}"/>
                        <Border x:Name="ValidationErrorElement" Visibility="Collapsed" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1">
                            <ToolTipService.ToolTip>
                                <ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                                    <ToolTip.Triggers>
                                        <EventTrigger RoutedEvent="Canvas.Loaded">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
                                                        <DiscreteObjectKeyFrame KeyTime="0">
                                                            <DiscreteObjectKeyFrame.Value>
                                                                <System:Boolean>true</System:Boolean>
                                                            </DiscreteObjectKeyFrame.Value>
                                                        </DiscreteObjectKeyFrame>
                                                    </ObjectAnimationUsingKeyFrames>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                    </ToolTip.Triggers>
                                </ToolTip>
                            </ToolTipService.ToolTip>
                            <Grid Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12" Background="Transparent">
                                <Path Fill="#FFDC000C" Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z"/>
                                <Path Fill="#ffffff" Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8"/>
                            </Grid>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

I would get the preview of Blend, coding the above by hand would be a large amount of unnecessary work.

Solution 2

Nothing seems to work in the xaml (as usual), so the best solution I've come up with is make a textbox readonly myself without the IsReadOnly property.

public class ReadOnlyTextBox : TextBox
{
    protected override void OnKeyDown(KeyEventArgs e)
    {
        e.Handled = true;
        base.OnKeyDown(e);
    }
}

Solution 3

If you just want an equivalent of a block of text in HTML, that can be selected (which for some reason even Silverlight 4 is missing) you can shorten Graeme's answer slightly:

<Style x:Key="ReadOnlyStyle" TargetType="TextBox">
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Background" Value="#FFFFFFFF"/>
    <Setter Property="Padding" Value="2"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid x:Name="RootElement">
                    <vsm:VisualStateManager.VisualStateGroups>
                        <vsm:VisualStateGroup x:Name="CommonStates">
                            <vsm:VisualState x:Name="Normal"/>
                            <vsm:VisualState x:Name="MouseOver"/>
                            <vsm:VisualState x:Name="Disabled" />
                            <vsm:VisualState x:Name="ReadOnly"/>
                        </vsm:VisualStateGroup>
                        <vsm:VisualStateGroup x:Name="FocusStates">
                            <vsm:VisualState x:Name="Focused">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </vsm:VisualState>
                            <vsm:VisualState x:Name="Unfocused">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </vsm:VisualState>
                        </vsm:VisualStateGroup>
                    </vsm:VisualStateManager.VisualStateGroups>
                    <Border x:Name="Border" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1">
                        <Grid>
                            <Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
                                <ScrollViewer x:Name="ContentElement" BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}"/>
                            </Border>
                        </Grid>
                    </Border>
                    <Border x:Name="DisabledVisualElement" IsHitTestVisible="False" Opacity="0" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}"/>
                    <Border x:Name="FocusVisualElement" Margin="1" IsHitTestVisible="False" Opacity="0" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

You may even be able to remove the disabled states.

Solution 4

Here's an enhanced version of @Struan's answer.

I assume you want to allow Select all and Copy if you're wanting a read only textbox. You need to handle keypresses such as Ctrl+A and Ctrl+C.

Disclaimer: this is not a fully complete set of keys - you may need to add more, but this will allow for copy at least.

public class ReadOnlyTextBox : TextBox
{
    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down)
        {
            base.OnKeyDown(e);
            return;
        }

        if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control ||
            (Keyboard.Modifiers & ModifierKeys.Apple) == ModifierKeys.Apple)
        {
            if (e.Key == Key.A || e.Key == Key.C)
            {
                // allow select all and copy!
                base.OnKeyDown(e);
                return;
            }
        }

        e.Handled = true;
        base.OnKeyDown(e);
    }
}

And here's a simple Style I'm using that indicates to the user that the item is selectable, but is smaller than a typical textbox.

<Style TargetType="my:ReadOnlyTextBox">
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Padding" Value="3,0,3,0"/>
    <Setter Property="Background" Value="Transparent"/>
</Style>
Share:
17,350
Struan
Author by

Struan

Updated on June 03, 2022

Comments

  • Struan
    Struan almost 2 years

    In Silverlight, How do I make a TextBox with IsReadOnly="True" not become grayed out. The gray effect looks horrible with my app and I would like to disable it, or change its appearance/color.

  • Struan
    Struan almost 15 years
    I am not using a TextBlock because I still want to be able to copy Text from the textbox and paste it somewhere else. I don't have Blend (I am using Visual Web Developer 2008 Express) so I can't try your second suggestion.
  • NJE
    NJE over 14 years
    This helped with an issue I had trying to make a readonly combobox. In my case I also had to handle the OnKeyUp with the same code to stop a KeyUp event handler being fired.
  • MordechayS
    MordechayS about 14 years
    Another +1 for 'as usual'. One problem with this approach is you get the border and caret when you click on the textbox.
  • Richard Beier
    Richard Beier over 13 years
    Thanks Struan - I ended up going this route. I modified your code to set e.Handled = true only if the Ctrl key is not pressed, and if the key is not an arrow key or tab key. That way people can still tab through the control, and can still hit Ctrl+C to copy.
  • StephenDonaldHuffPhD
    StephenDonaldHuffPhD over 10 years
    Oh, I should add that you have to then be careful when accessing the control as a child component to make certain you are typing it to a Button rather than a TextBox, but I assume most developers asking this question would understand this.
  • Nergon
    Nergon over 9 years
    5 years later. still helpful