Machinarius
Machinarius

Reputation: 3731

WPF Label to Textbox

Im pretty noob in WPF but im seriously trying to master it :p

Ive been trying to create a control where one label/textblock is displayed but once the user hovers/clicks the control, a textbox is shown instead so the value can be edited.

What i have been trying is binding the Visible property to a boolean in the code-behind, which is updated using delegates for MouseOver and MouseLeave and Got/LostFocus, but it didnt work. Also i tried using a simple style which also bound the Visible property to the boolean in code-behind... didnt work either. Ultimately, i followed what WPF: Label to TextBox when selected suggested, using a ControlTemplate and a Trigger, like this:

    <Style x:Key="TransformerBox" TargetType="{x:Type TextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Visibility" Value="Visible"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And:

    <Canvas x:Name="CnvCantidad" Grid.Row="2" Grid.Column="1">
        <TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Canvas.Left="16" Canvas.Top="8" Width="16" 
                 x:Name="TxtCantidad" Style="{StaticResource TransformerBox}" Height="23" Visibility="Visible"/>
        <Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="0" Canvas.Left="16" Canvas.Top="6" 
               x:Name="LblCantidad"/>
    </Canvas>

But in all the cases explained before, the TextBox was never visible no matter what :/

How should i create the ControlTemplate so the TextBox is visible when the user hovers the Label/TextBlock?

Upvotes: 2

Views: 10550

Answers (4)

Fredrik Hedblad
Fredrik Hedblad

Reputation: 84674

Edited the Style for a Label a bit to make a TextBox appear when IsMouseOver is True. This is better then two Controls for re-usability.

<Style x:Key="EditableLabelStyle" TargetType="{x:Type Label}">
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Padding" Value="5"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Label}">
                <Grid>
                    <TextBox Name="textBox"
                             Grid.ZIndex="1"
                             Padding="1,3,0,0"
                             Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Label}}, Path=Content, UpdateSourceTrigger=PropertyChanged}"
                             Opacity="0"/>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter TargetName="textBox" Property="Opacity" Value="1"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Use it like this

<Label Style="{StaticResource EditableLabelStyle}" HorizontalAlignment="Center" VerticalAlignment="Center" Content="0" Canvas.Left="16" Canvas.Top="6"  
       x:Name="LblCantidad"/>

Upvotes: 8

biju
biju

Reputation: 18040

You can do this by setting the ContentTemplate inside a trigger, or by setting the textbox as a resource and setting it using a trigger.Check this sample

<Label Height="30" BorderBrush="Gray" BorderThickness="1">
    <Label.Resources>
        <TextBox x:Key="ContenTextBoxt" HorizontalAlignment="Stretch"/>
    </Label.Resources>
    <Label.Style>
        <Style TargetType="{x:Type Label}">
            <Setter Property="Padding"
                                    Value="0" />
                <Setter Property="HorizontalContentAlignment"
                                    Value="Stretch" />
                <Setter Property="VerticalContentAlignment"
                                    Value="Stretch" />
                <Setter Property="Content"
                                    Value="No mouse over" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver"
                                             Value="True">
                    <Setter Property="Content" Value="{StaticResource ContenTextBoxt}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Label.Style>
    </Label>

Upvotes: 0

BrokenGlass
BrokenGlass

Reputation: 161012

If you are dealing with booleans and visibility you should consider a ValueConverter.

Here's an example on how to bind to a boolean (IsTextVisible) in your model and have it map to visibility collapsed or visible.

XAML:

 <TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Canvas.Left="16" 
  Canvas.Top="8" Width="16" 
  x:Name="TxtCantidad" Style="{StaticResource TransformerBox}" Height="23" 
  Visibility="{Binding IsTextVisible, 
               Converter={StaticResource BoolToVisibilityConverter}}"/>

ValueConverter code:

public class BoolToVisibilityConverter : IValueConverter
{
    object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool isVisible = Convert.ToBoolean(value);

        return isVisible ? Visibility.Visible : Visibility.Collapsed;
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Also you have to import the converter namespace in your XAML

xmlns:converter="clr-namespace:Foo.Converter"

and assign it a key

<converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>

EDIT:

If you're binding directly to the code behind for your purposes you can set the DataContext for the Window in the XAML this way

 DataContext="{Binding RelativeSource={RelativeSource Self}}"

Also make sure you are binding to a property

public bool IsTextVisible {get;set;}

In the long run you will want to look into the following topics:

  1. Data Binding in WPF
  2. MVVM in WPF

Upvotes: 0

Darko Kenda
Darko Kenda

Reputation: 4960

There are a couple of ways to do it.

Here's one way, it just makes the TextBox hidden until you hover the mouse over the Label OR the TextBox (otherwise the mouse is not hovered over the Label anymore when the TextBox appears). You might have to tweak it, but you should get the idea (note that it doesn't actually hide the Label, just displays TextBox over it):

<Canvas x:Name="CnvCantidad" Grid.Row="2" Grid.Column="1">
        <Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="0" Canvas.Left="16" Canvas.Top="6" 
           x:Name="LblCantidad"/>
        <TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Canvas.Left="16" Canvas.Top="8" Width="16" 
             x:Name="TxtCantidad" Height="23">
            <TextBox.Style>
                <Style TargetType="{x:Type TextBox}">
                    <Setter Property="Visibility" Value="Collapsed" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=LblCantidad, Path=IsMouseOver}" Value="True">
                            <Setter Property="Visibility" Value="Visible" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding ElementName=TxtCantidad, Path=IsMouseOver}" Value="True">
                            <Setter Property="Visibility" Value="Visible" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>
    </Canvas>

Upvotes: 1

Related Questions