0

I have a UserControl with a TextBox and I want to expose TextBox.Text property. We must take into consideration that TextBox.Text and the DependencyProperty binded to it, are not always the same values. And I explain it a little bit deeper:

<UserControl x:Class="MySolution.MyUserControl"
             Name="MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <TextBlock>This is my label</TextBlock>
        <TextBox x:Name="myTextBox" Text="{Binding ElementName=MyControl, Path=BindingText, UpdateSourceTrigger=LostFocus}"></TextBox>
    </StackPanel>
</UserControl>

And this code-behind:

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace MySolution
{
    public partial class MyUserControl : UserControl
    {
        public MyUserControl()
        {
            InitializeComponent();
        }

        public string BindingText
        {
            get { return (string)GetValue(BindingTextProperty); }
            set { SetValue(BindingTextProperty, value); }
        }

        public static readonly DependencyProperty BindingTextProperty =
            DependencyProperty.Register(nameof(BindingText), typeof(string), typeof(MyUserControl),
                new FrameworkPropertyMetadata(null)
                {
                    BindsTwoWayByDefault = true
                });
}

In this simple example, if we run the application and we type "Hello" in myTextBox and we do not lose the focus, we would have that TextBox.Text is "Hello", but BindingText (our DependencyProperty) is still empty (until we lose the focus and the binding updates).

In other words, what I would like is to be able to bind to something like this:

public string Text
{
    get => myTextBox.Text;
    set => myTextBox.Text = value;
}

But I this does not work, I guess because it is not a DependencyProperty. Is it possible to create a DependencyProperty that exposes myTextBox.Text anyway?

Carlos
  • 1,638
  • 5
  • 21
  • 39
  • 2
    Why would you not set UpdateSourceTrigger=PropertyChanged? – Clemens Jan 25 '22 at 18:25
  • Alternatively, derive from TextBox and move the TextBlock to the ControlTemplate. – Clemens Jan 25 '22 at 18:26
  • @Clemens even with UpdateSourceTrigger=PropertyChanged there are moments when both properties would be different, e.g.: https://stackoverflow.com/questions/70592040/xaml-binding-with-delay-and-keybinding/ – Carlos Jan 25 '22 at 18:58
  • @Clemens could you explain a little bit more the ControlTemplate option, please? It sounds interesting... Thank you – Carlos Jan 25 '22 at 18:59
  • 1
    You would derive from TextBox and change the TextBox Template so that it contains the TextBlock. You will find the default ControlTemplate e.g. here: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/textbox-styles-and-templates?view=netframeworkdesktop-4.8 – Clemens Jan 25 '22 at 19:02
  • @Clemens I think I will explore that way. I will report you if I have success, and I will ask you for writing it as answer if so. – Carlos Jan 25 '22 at 19:03
  • For the "moments when both properties would be different" - you would simply not use a delayed Binding. Do not overcomplicate it. – Clemens Jan 25 '22 at 19:04
  • 1
    Make sure your custom ControlTemplate keeps the `` part. Do not put another TextBox into it, a common mistake. – Clemens Jan 25 '22 at 19:05
  • @Clemens Your suggestion was great for me, now I even wonder why I used a UserControl instead of extending an existing control in a lot of more places... If you write it as an answer, explaining it a little for other people in the future, I will mark it as accepted answer. Thank you – Carlos Jan 27 '22 at 08:54

1 Answers1

0

From my limited testing, this solution appears to be sufficient for your needs

Code behind:

public partial class MyUserControl
{
    public MyUserControl()
    {
        InitializeComponent();
    }
    public string Text
    {
        get => myTextBox.Text;
        set
        {
            SetValue(TextProperty, value);
        }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(nameof(Text), typeof(string), typeof(testControl1),
            new PropertyMetadata(default(string)));
}

XAML:

 <StackPanel>
    <TextBlock>This is my label</TextBlock>
    <TextBox x:Name="myTextBox" Text="{Binding ElementName=MyControl,Path=Text, Mode=OneWay}"/>
</StackPanel>
Nigel
  • 2,961
  • 1
  • 14
  • 32
  • I am little bit busy right now and I cannot test if it works for me. I will test it later, and if it works for me, I will mark your answer. Thank you – Carlos Jan 26 '22 at 08:25
  • `Box.Text = value;` is pointless, or even wrong. The setter of the Text property will not be called when the Text dependency property is set by data binding and a couple of other sources. There must not be anything else than the SetValue call in the setter. – Clemens Jan 27 '22 at 09:08
  • @Clemens gotcha. I knew there was something redundant in the setter and I thought it was the SetValue call, but then when I removed SetValue it no longer worked. I guess I don't understand dependency properties as well as I should – Nigel Jan 27 '22 at 18:22
  • They are however perfectly documented. – Clemens Jan 27 '22 at 18:43
  • @Carlos any luck? – Nigel Feb 08 '22 at 01:19