Home > Enterprise >  Set default text only for disabled textbox in WPF
Set default text only for disabled textbox in WPF

Time:06-29

I have a case when I need to set default text only for disabled TextBox controls in my WPF application. That is enabled controls may contain empty text, but when control is disabled with empty text there should be displayed binded value e.g. "No data".

Is there any proper way to do this in WPF?

CodePudding user response:

You can use behavior like this:

public enum DisplayDefaultTextMode
{
    TextBoxTextEmpty,
    TextBoxDisabledAndTextEmpty
}

public class DefaultTextBoxValueBehavior : Behavior<TextBox>
{
    public DisplayDefaultTextMode DisplayMode { get; set; } = DisplayDefaultTextMode.TextBoxDisabledAndTextEmpty;

    public string DefaultText
    {
        get => (string)GetValue(DefaultTextProperty);
        set => SetValue(DefaultTextProperty, value);
    }

    public static readonly DependencyProperty DefaultTextProperty =
        DependencyProperty.Register(
            nameof(DefaultText),
            typeof(string),
            typeof(DefaultTextBoxValueBehavior),
            new PropertyMetadata(string.Empty));

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Loaded  = onl oaded;

        AssociatedObject.TextChanged  = OnTextChanged;
        AssociatedObject.IsEnabledChanged  = OnIsEnabledChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.Loaded -= onl oaded;
        AssociatedObject.TextChanged -= OnTextChanged;
        AssociatedObject.IsEnabledChanged -= OnIsEnabledChanged;
    }

    private void onl oaded(object sender, RoutedEventArgs e) => SetDefaultTextIfNeeded();

    private void OnTextChanged(object sender, TextChangedEventArgs e)
    {
        if (AssociatedObject.Text?.Length == 0 && e.Changes.Any(c => c.RemovedLength > 0))
        {
            //ignore since we expect the user to cleare the field for futher input
        }
        else
            SetDefaultTextIfNeeded();
    }

    private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) => SetDefaultTextIfNeeded();

    private void SetDefaultTextIfNeeded()
    {
        if (CheckShouldSetDefaultText())
            SetDefaultText();
    }

    private bool CheckShouldSetDefaultText()
    {
        if (DisplayMode == DisplayDefaultTextMode.TextBoxTextEmpty)
            return string.IsNullOrEmpty(AssociatedObject.Text);
        else
            return string.IsNullOrEmpty(AssociatedObject.Text) && !AssociatedObject.IsEnabled;
    }

    private void SetDefaultText()
    {
        AssociatedObject.TextChanged -= OnTextChanged;
        AssociatedObject.Text = DefaultText;
        AssociatedObject.TextChanged  = OnTextChanged;
    }
}

Usage example:

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

<TextBox>
    <i:Interaction.Behaviors>
        <behaviors:DefaultTextBoxValueBehavior
            DisplayMode="TextBoxDisabledAndTextEmpty"
            DefaultText="Default text"/>
        </i:Interaction.Behaviors>
</TextBox>

N.B! You can define DisplayMode property in the behavior to set up default text appearence. If you set DisplayDefaultTextMode.TextBoxTextEmpty default text will be set if texbox text is null or empty. And if you set DisplayDefaultTextMode.TextBoxDisabledAndTextEmpty defualt text will be set only to the disabled textbox with empty text.

Hope it will be helpful for you.

CodePudding user response:

You don't need a converter for this, it can be done in pure XAML.

First, place the cursor over a TextBox, and in the Properties pane select Miscellaneous -> Template -> Convert to New Resource. That will template out your control, giving you something to edit. Simply change the PART_ContentHost element to a TextBlock containing your default text:

<SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>
<ControlTemplate x:Key="TextBoxTemplate1" TargetType="{x:Type TextBoxBase}">
    <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
        <!--<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>-->
        <TextBlock Text="Nothing to see here!" />
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="true">
            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="true">
            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

And now just use a style to apply that ControlTemplate to whenever the control is disabled:

<Style TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="TextBox.IsEnabled" Value="false">
            <Setter Property="Template" Value="{DynamicResource TextBoxTemplate1}" />
        </Trigger>
    </Style.Triggers>
</Style>

So now when you put something like this in your XAML:

<StackPanel Orientation="Vertical">
    <TextBox Width="150" Text="This textbox is enabled" />
    <TextBox Width="150" Text="This textbox is disabled" IsEnabled="False" />
</StackPanel>

...you get this:

enter image description here

  • Related