How to hand over the value of UpdateSourceTrigger to UserControl or update it at runtime?
Asked Answered
H

2

7

I am facing a problem with UpdateSourceTrigger property. I have a UserControl named LabelWithTextBox, where UpdateSourceTrigger (On Text Property) is not defined (therefore has default value). This should stay like this because of performance (Text should update when Focus is out of the TextBox).

But now I have a case where the UserControl should be used and the update should happen as the user types, therefore I want to set UpdateSourceTrigger to PropertyChanged.

Ideally the best solution would be if the UpdateSourceTrigger property could be inherited. The user uses in his View the UserControl and defines UpdateSourceTrigger = PropertyChanged, this information is handed over to my UserControl and everything works as expected. Does anyone know how I can archive this ?

What other options do I have ? How can I change the UpdateSourceTrigger Property at runtime ?

Here is the relevant UserControl (Code Behind) Code:

public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
    "Text",
    typeof(string),
    typeof(LabelWithTextBox),
    new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public string Text
{
    get { return (string)this.GetValue(TextProperty); }
    set { this.SetValue(TextProperty, value); }
}

And here is the UserControl (Xaml) Code:

<Grid Grid.Column="1">
            <telerik:RadWatermarkTextBox TextChanged="TextBoxBase_OnTextChanged"
                                         Name="TextBox"
                                         Text="{Binding Text, Mode=TwoWay, ElementName=userControl}"
                                         WatermarkContent="{Binding Placeholder, Mode=TwoWay, ElementName=userControl}"
                                         TextWrapping="{Binding TextWrap, Mode=TwoWay, ElementName=userControl}"
                                         AcceptsReturn="{Binding AcceptsReturn, Mode=TwoWay, ElementName=userControl}"
                                         VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility, Mode=TwoWay, ElementName=userControl}"
                                         MinLines="{Binding MinLines, Mode=TwoWay, ElementName=userControl}"
                                         MaxLines="{Binding MaxLines, Mode=TwoWay, ElementName=userControl}"
                                         IsReadOnly="{Binding IsReadOnly, ElementName=userControl}"/>
    ....

If add UpdateSourceTrigger = PropertyChanged to Text everything will work as expected. But I do not want that.

Here is for example how someone could use the UserControl in his View. What I am looking for is a way to hand over the value of the UpdateSourceTrigger to my UserControl, but how ?

<controls:LabelWithTextBox
                    Grid.Row="1"
                    Margin="0,5,0,0"
                    Label="Excel Blattname:"
                    SharedSizeGroup="LabelsX"
                    Text="{Binding SheetName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Hujsak answered 19/3, 2019 at 12:44 Comment(7)
You could have a DependencyProperty for that in your control, then use the value in code behind to set the TextBox. Just bear in mind that the DependencyProperty when using Binding doesn't trigger the backing CLR property.Affecting
@Affecting could you provide me with a small example ? I have now the dependency property, how should I now change accordingly the UpdateSourceTrigger ?Hujsak
Why have you made a Text a DependencyProperty in your user control? If it is "source", then it can be a usual property. What do you want to achieve?Concertgoer
@Concertgoer Obviously to make it bindable. Having dependency properties in UserControls is the standard approach.Buster
@Hujsak What you want to do here is hard to achieve with an "intermediate" dependency property in a UserControl. You could instead perhaps derive from RadWatermarkTextBox, or declare a TextBinding property of type BindingBase (which would be a regular CLR property instead of a dependency property). The Binding would be passed to the RadWatermarkTextBox by SetBinding(RadWatermarkTextBox.TextProperty, TextBinding).Buster
@Buster Does Devid want just expose a property of an ui element used in user control? I miss an MCVE here. If so, then one a possibility to create one more dependency property and update it in user control on LostFocus of RadWatermarkTextBox Concertgoer
@Buster I am open to any solution. Can you explain a bit more your solution, how can the user now define the UpdateSourceTrigger from outside the UserControl ?Hujsak
B
4

UpdateSourceTrigger is a property of the Binding, not the control. In order to pass it to the control, you could pass the entire Binding to an appropriate property.

Your control could expose a TextBinding property:

public partial class LabelWithTextBox : UserControl
{
    public LabelWithTextBox()
    {
        InitializeComponent();
    }

    private BindingBase textBinding;

    public BindingBase TextBinding
    {
        get { return textBinding; }
        set
        {
            textBinding = value;
            TextBox.SetBinding(RadWatermarkTextBox.TextProperty, textBinding);
        }
    }
}

to which you would assign a Binding like

<controls:LabelWithTextBox
  TextBinding="{Binding SheetName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

that is then directly passed to the RadWatermarkTextBox.

The drawback is of course that you can only assign Binding and nothing else.


You may however also use a Text dependency property and inspect its value when the control is loaded. If the value is a Binding, you could use it like this:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    var binding = GetBindingExpression(TextProperty)?.ParentBinding;

    if (binding != null)
    {
        TextBox.SetBinding(RadWatermarkTextBox.TextProperty, binding);
    }
}
Buster answered 19/3, 2019 at 14:52 Comment(0)
C
2

It's not clear enough for me what do you want to achieve

You can set Binding to RadWatermarkTextBox in code, not in XAML.

EDIT:

Now I see what you want :)

Add it to your user control:

public bool UpdateOnPropChanged
{
    get
    {
        return _updateOnPropChanged;
    }
    set
    {
        _updateOnPropChanged = value;
        this.TextBox.SetBinding(TextBox.TextProperty, new Binding("Text") { RelativeSource=new RelativeSource(RelativeSourceMode.FindAncestor) { AncestorType=typeof(LabelWithTextBox) }, UpdateSourceTrigger = _updateOnPropChanged ? UpdateSourceTrigger.PropertyChanged : UpdateSourceTrigger.LostFocus });
    }
}
private bool _updateOnPropChanged;

And you can use it so:

<controls:LabelWithTextBox UpdateOnPropChanged="false"/>
Concertgoer answered 19/3, 2019 at 13:53 Comment(5)
As the user types the text in the TextBox the Binding should be immediately updated and not after the Focus is Lost (this is what happens by default). That is why I need the UpdateSourceTrigger property set to PropertyChanged. Now I created a reusable UserControl which has by default UpdateSourceTrigger set to Default, this should stay like this (performance reasons). I am looking for a way to change this property (UpdateSourceTrigger ) on demand. There is no TextChanged. LostFocus is not the right event for this.Hujsak
@Hujsak Does the user type in some TextBox outside the user control and you want to pass the data to the user control, or does the user type in a user control and you want to expose the data via a dependency property?Concertgoer
@Hujsak RadWatermarkTextBox seems to derive from a TextBox, so there must be TextChanged event.Concertgoer
You have to imagine that the user is using my UserControl in their View for whatever reason. A User A wants a immediate update as he types and User B wants to update only when focus is lost. Both are using the same UserControl (in my example LabelWithTextBox, which provides a Label to the left and a TextBox to the right).Hujsak
Upon further inspection there is a TextChanged event. But I don't see how I can use to solve my problemHujsak

© 2022 - 2024 — McMap. All rights reserved.