Changing custom TextBox's background property if Text value has changed

906 Views Asked by At

I have a custom TextBox control which is inheriting System.Windows.Controls.TextBox and implementing INotifyPropertyChanged interface .

public partial class V3TextBox : TextBox, INotifyPropertyChanged

It has a custom OriginalValue property and is overriding base Text property.

The way I imagined for it to work is to bind Text and OriginalValue to two different string properties and to set its Background to , let's say, yellow if those two strings are not the same and back to white if they become the same again.

These are my Text and PropertyChanged properties:

private Binding PropertyChangedBinding = new Binding()
{
    Path = new PropertyPath("ChangedPropertyBackground")
};

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

private string text { get; set; } = "";
public new string Text
{
    get
    {
        return text;
    }
    set
    {
        text = value;
        if (value == originalValue)
            BindingOperations.ClearBinding(this, BackgroundProperty);
        else
            SetBinding(BackgroundProperty, PropertyChangedBinding);
        OnPropertyChanged("Background");
    }
}

Now, the problem is probably with setting DependencyProperty for my OriginalValue property.

They look like this:

private string originalValue;
public string OriginalValue
{
    get
    {
        return (string)GetValue(TestProperty);
    }
    set
    {
        originalValue = value;
        SetValue(TestProperty, value);
    }
}

public static readonly DependencyProperty TestProperty =
    DependencyProperty.Register("OriginalValue", typeof(string),
    typeof(V3TextBox), new FrameworkPropertyMetadata(default(string)));

private void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    SetValue(TestProperty, e.NewValue);
}

Control usage in XAML looks like this:

<Ctrls:V3TextBox x:Name="txtBxDiscountNote"
    Text="{Binding EditedNote, Mode=TwoWay, NotifyOnTargetUpdated=True}"
    OriginalValue="{Binding OriginalNote, Mode=TwoWay}"/>

DataContext is set in XAML.

The problem is that the OriginalValue property is never changed, it is always null and the code for changing Background is triggered only when Text property is changed programmatically, not via GUI input. Would this be easier to implement with IValueConverter? There will be around 30 of these controls on a single form.

2

There are 2 best solutions below

0
On BEST ANSWER

Something like this should work:

public class V3TextBox : TextBox
{
    public static readonly DependencyProperty OriginalValueProperty =
        DependencyProperty.Register("OriginalValue", typeof(string),  typeof(V3TextBox));

    public static readonly DependencyProperty ChangedBackgroundProperty =
        DependencyProperty.Register("ChangedBackground", typeof(Brush), typeof(V3TextBox));

    public string OriginalValue
    {
        get { return (string)GetValue(OriginalValueProperty); }
        set { SetValue(OriginalValueProperty, value); }
    }

    public Brush ChangedBackground
    {
        get { return (Brush)GetValue(ChangedBackgroundProperty); }
        set { SetValue(ChangedBackgroundProperty, value); }
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);

        if (Text == OriginalValue)
        {
            ClearValue(BackgroundProperty);
        }
        else
        {
            Background = ChangedBackground;
        }
    }
}

Use it like this:

<local:V3TextBox OriginalValue="Hello" ChangedBackground="Yellow"/>
4
On

This is my current code:

public partial class V3TextBox : TextBox
{
    public static readonly DependencyProperty OriginalValueProperty =
        DependencyProperty.Register("OriginalValue", typeof(string), typeof(V3TextBox));
    public string OriginalValue
    {
        get
        {
            return (string)GetValue(OriginalValueProperty);
        }
        set
        {
            SetValue(OriginalValueProperty, value);
        }
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);

        if (Text == OriginalValue)
            SetResourceReference(BackgroundProperty, "V3CtrlDefaultBackground");
        else
            SetResourceReference(BackgroundProperty, "V3CtrlChangedBackground");
    }


    public V3TextBox()
    {
        InitializeComponent();
    }
}

and I am using it like this:

<Ctrls:V3TextBox x:Name="txtBxLocID"
    OriginalValue="{Binding LocationID, Mode=OneTime}"
    Text="{Binding LocationID, Mode=TwoWay}"/>

That means, I am binding Text and OriginalValue to the same property, but the latter has setting Mode=OneTime.

So OriginalValue is being set only once, and is maintaining its value as long as it is alive and I am able to compare it to the current Text value.

@Clemens I am using some of your code, so it will be marked as the right answer, just tell me if you have any more suggestions to my code? I would need to set one more property in XAML with your code so I am not using DependencyProperty for Background Brush. Maybe if there is a way to just set Text property in XAML, set it as OriginalValue in code for the first time and then compare it for further changes? Can the code behind be somehow aware of the fact binding is being called for the first time or something like that? It would be nice if the XAML could be written without OriginalValue property. OriginalValue is not seen in the XAML of the V3TextBox itself, it would also be fine to just One-way bind it to Text.