[WPF] How to develop and editable Textblock ?

Here is another request that a friend of mine asked me recently. To achieve this, I’ve just create a custom control that contains a TextBlock and a TextBox (which is hidden) controls.

When user double clic on the TextBlock, the Textbox appears.

So here is my proposition: in the file generic.xaml, put this:

 

<Style TargetType="{x:Type local:EditableTextBlock}">

    <Setter Property="Template">

        <Setter.Value>

            <ControlTemplate TargetType="{x:Type local:EditableTextBlock}">

                <Grid x:Name="PART_GridContainer"

                      Background="{TemplateBinding Background}"

                      Width="{TemplateBinding Width}"

                      Height="{TemplateBinding Height}">

                    <TextBlock x:Name="PART_TbDisplayText"

                               Visibility="Visible"

                               Background="{Binding Mode=TwoWay, Path=TextBlockBackgroundColor, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}"

                               Foreground="{Binding Mode=TwoWay, Path=TextBlockForegroundColor, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}"

                               Text="{Binding Mode=TwoWay, Path=Text, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}" />

                    <TextBox x:Name="PART_TbEditText"

                             Visibility="Hidden"

                             Background="{Binding Mode=TwoWay, Path=TextBoxBackgroundColor, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}"

                             Foreground="{Binding Mode=TwoWay, Path=TextBoxForegroundColor, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}"

                             Text="{Binding Mode=TwoWay, Path=Text, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}" />

                </Grid>

            </ControlTemplate>

        </Setter.Value>

    </Setter>

</Style>

 

Then, here is the content that should go in the behind file:

 

[TemplatePart(Type = typeof(Grid), Name = EditableTextBlock.GRID_NAME)]

[TemplatePart(Type = typeof(TextBlock), Name = EditableTextBlock.TEXTBLOCK_DISPLAYTEXT_NAME)]

[TemplatePart(Type = typeof(TextBox), Name = EditableTextBlock.TEXTBOX_EDITTEXT_NAME)]

public class EditableTextBlock : Control

{

    #region Constants

    private const string GRID_NAME = "PART_GridContainer";

    private const string TEXTBLOCK_DISPLAYTEXT_NAME = "PART_TbDisplayText";

    private const string TEXTBOX_EDITTEXT_NAME = "PART_TbEditText";

    #endregion

    #region Member Fields

    private Grid m_GridContainer;

    private TextBlock m_TextBlockDisplayText;

    private TextBox m_TextBoxEditText;

    #endregion

    #region Dependency Properties

    public string Text

    {

        get { return (string)GetValue(TextProperty); }

        set { SetValue(TextProperty, value); }

    }

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock), new UIPropertyMetadata(string.Empty));

    public Brush TextBlockForegroundColor

    {

        get { return (Brush)GetValue(TextBlockForegroundColorProperty); }

        set { SetValue(TextBlockForegroundColorProperty, value); }

    }

    public static readonly DependencyProperty TextBlockForegroundColorProperty = DependencyProperty.Register("TextBlockForegroundColor", typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(null));

    public Brush TextBlockBackgroundColor

    {

        get { return (Brush)GetValue(TextBlockBackgroundColorProperty); }

        set { SetValue(TextBlockBackgroundColorProperty, value); }

    }

    public static readonly DependencyProperty TextBlockBackgroundColorProperty = DependencyProperty.Register("TextBlockBackgroundColor", typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(null));

    public Brush TextBoxForegroundColor

    {

        get { return (Brush)GetValue(TextBoxForegroundColorProperty); }

        set { SetValue(TextBoxForegroundColorProperty, value); }

    }

    public static readonly DependencyProperty TextBoxForegroundColorProperty = DependencyProperty.Register("TextBoxForegroundColor", typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(null));

    public Brush TextBoxBackgroundColor

    {

        get { return (Brush)GetValue(TextBoxBackgroundColorProperty); }

        set { SetValue(TextBoxBackgroundColorProperty, value); }

    }

    public static readonly DependencyProperty TextBoxBackgroundColorProperty = DependencyProperty.Register("TextBoxBackgroundColor", typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(null));

    #endregion

    #region Constructor

    static EditableTextBlock()

    {

        DefaultStyleKeyProperty.OverrideMetadata(typeof(EditableTextBlock), new FrameworkPropertyMetadata(typeof(EditableTextBlock)));

    }

    #endregion

    #region Overrides Methods

    public override void OnApplyTemplate()

    {

        base.OnApplyTemplate();

        this.m_GridContainer = this.Template.FindName(GRID_NAME, this) as Grid;

        if(this.m_GridContainer != null)

        {

            this.m_TextBlockDisplayText = this.m_GridContainer.Children[0] as TextBlock;

            this.m_TextBoxEditText = this.m_GridContainer.Children[1] as TextBox;

            this.m_TextBoxEditText.LostFocus += this.OnTextBoxLostFocus;

        }

    }

    protected override void OnMouseDoubleClick(MouseButtonEventArgs e)

    {

        base.OnMouseDoubleClick(e);

        this.m_TextBlockDisplayText.Visibility = Visibility.Hidden;

        this.m_TextBoxEditText.Visibility = Visibility.Visible;

    }

    #endregion

    #region Event Handlers

    private void OnTextBoxLostFocus(object sender, RoutedEventArgs e)

    {

        this.m_TextBlockDisplayText.Visibility = Visibility.Visible;

        this.m_TextBoxEditText.Visibility = Visibility.Hidden;

    }

    #endregion

}

 

As you can see, it’s pretty simple but very useful :)

Happy coding !

Bye.

15 Comments

  • Hey Thomas,
    A better (and non so intrusive) way, would be to make a style for the TextBox without any border / background when it has no-focus.

  • Looks complicated for me..

  • @Koistya `Navin: Why complicated ? It's just a control composed by a TextBox and a TextBlock and, when user double clic on the Textblock, I simple change the visibility of the Textbox: all the rest is just for the style...

  • {Binding Mode=TwoWay, Path=TextBoxBackgroundColor, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}

    I think I love XAML most of all for its simplicity.

  • Oups, I wasn't observant enaough. Yep, your solutions looks good. I'am just too much into ASP.NET MVC and sometimes misunderstand others :) LOL

  • @Koistya `Navin: I now understand better why you were talking about JQuery :)

    BTW, your solution looks good too !

  • Hello,

    Where do you route the MouseDoubleClick event onto the control? I can see the += LostFocus event, but not the MouseDoubleClick.

  • Hi,

    If you check at the source code, you can see that I override the method OnMouseDoubleClick ;)


    Bye.

  • thank for your code,but when i bind the text of that dont show anything
    this is my usage code:







    please help me.

  • I have the same issue.

  • I didn't try it but perhaps use Text="{TemplateBinding Text}" instead of Text="{Binding Mode=TwoWay, Path=Text, RelativeSource={RelativeSource AncestorType={x:Type local:EditableTextBlock}}}"
    binding to Text is by default TwoWay if i remember correct...

  • I am using this control, but if you place the control in a LISTBOX, the double click event doesn't flip to edit mode. Any idea why this would work. Note: works fine if you place the control in a ItemsControl.

    ex:





    vs.




  • How can I bind the Text of the TextBlock to one property of a ViewModel and the Text of the TextBox to another property on the same ViewModel??

    Tnx

  • A hybrid approach like this one would be good to keep the performance of a textblock until it's clear that the user needs editing. You'd have to lazy load the textbox though. May try it.

  • "Why complicated ? It's just a control composed by a TextBox and a TextBlock and, when user double clic on the Textblock, I simple change the visibility of the Textbox: all the rest is just for the style..."

    Yes - it's this very 'all the rest' that's complicated :) I don't criticize your solution at all. I just think it shows how clunky WPF is. So much fuss, so many lines of code for achieving something that basic. Quite alarming, giving that one of the main purposes of the framework is to help us in developing sophisticated UI quickly.

Comments have been disabled for this content.