7

Just a short question :
In wpf, how do I set and get updatesourcetrigger of a textbox in codebehind ?
Thanks

Update :
I follow AngleWPF's code :

        var bndExp = BindingOperations.GetBindingExpression(this, TextBox.TextProperty);

        var myBinding
          = bndExp.ParentBinding;

        var updateSourceTrigger = myBinding.UpdateSourceTrigger;

But I got the exception :

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in PresentationFramework.dll Additional information: Exception has been thrown by the target of an invocation.

JatSing
  • 4,857
  • 16
  • 55
  • 65

3 Answers3

16

What do you mean by UpdateSourceTrigger of TextBox? You mean to say UpdateSourceTrigger of TextBox.TextProperty's Binding?

E.g. if you have a TextBox named myTextBox having its Text property bound to some source then you can easily get it's UpdateSourceTrigger and Binding object via GetBindingExpression() call.

   var bndExp
     = BindingOperations.GetBindingExpression(myTextBox, TextBox.Textproperty);

   var myBinding
     = bndExp.ParentBinding; 

   var updateSourceTrigger
     = myBinding.UpdateSourceTrigger;

But it is tricky to set UpdateSourceTrigger for an already used binding. E.g. in the above case you wont be able to set the myBinding.UpdateSourceTrigger to something else. This is not allowed when a binding object is already in use.

You may have to deep clone the binding object and set new UpdateSourceTrigger to it and assign it back to the TextBox. Cloning does not exist for Binding class. You may have to write your own cloning code for the same.

  var newBinding = Clone(myBinding); //// <--- You may have to code this function.
  newBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
  myTextBox.SetBinding(TextBox.TextProperty, newBinding);

Alternately ou can also try to detatch the existing Binding and update it and assign it back...

  myTextBox.SetBinding(TextBox.TextProperty, null);
  myBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
  myTextBox.SetBinding(TextBox.TextProperty, myBinding);

Let me know if any of these tips helps.

WPF-it
  • 19,625
  • 8
  • 55
  • 71
  • Hi AngelWPF, I follow your code but got the exception. In fact, I want to set all of my textbox.TextProperty's Binding UpdateSourceTrigger into PropertyChanged so I though I could do it in my textbox baseclass (all of my textboxs are inherited from one baseclass) – JatSing Nov 03 '11 at 08:06
  • AngleWPF, Exception:Thrown: "Object reference not set to an instance of an object." (System.NullReferenceException) A System.NullReferenceException was thrown: "Object reference not set to an instance of an object." – JatSing Nov 03 '11 at 09:03
  • Did u put a debug pointer in the code and see which variable is coming as null? `bndExp` will come null if there is no existing binding done on `TextProperty` of the `TextBox`. Also I hope `this` is a `TextBox` object. In which function inside your custom text box are you checking this code? – WPF-it Nov 03 '11 at 09:44
  • I tried putting try catch around the code and realize that the exception doesn't come from there. – JatSing Nov 03 '11 at 12:58
  • 2
    I know this answer is 7-years old, but, in case it helps anyone, the cloning option worked for me, and, with the help of [this StackOverflow answer](https://stackoverflow.com/questions/32541/how-can-you-clone-a-wpf-object#33036), it was possible to clone the binding in 3 lines. Since there is not much room in these comments, this is the "ugly" one-liner method to do the cloning: `public static Binding Clone(Binding binding) => (Binding)XamlReader.Load(XmlReader.Create(new StringReader(XamlWriter.Save(binding))));`. This 1 line requires using `System.Xml`, `System.Windows.Markup`, & `System.IO` – Noah Bridge Jan 09 '20 at 18:30
  • As a P.S. to the previous comment, I must say that, after cloning, I lost the functionality of a trigger in the textbox's XAML that would switch the property that was bound to the textbox's `Text` depending on the textbox's focus state. Therefore, I was forced to simulate the switch in code by re-cloning the `Binding` in the textbox's `GotFocus` and `LostFocus` events, modifying the bound property in the clone. – Noah Bridge Jan 09 '20 at 18:37
2

A another way to implement it is setting binding in TextBox loaded eventhandler. Below is the xaml of TextBox:

    <TextBox Grid.Row="0" 
             x:Name="txtName"
             Text="{Binding Name}"
             Loaded="TxtName_OnLoaded" />

Now in code behind for TxtName_OnLoaded eventhandler will will create new Binding object and will initialize it as per our needs. Also we will add ValidationRule into it.

    private void TxtName_OnLoaded(object sender, RoutedEventArgs e)
    {
      ApplicationViewModel appVm = this.DataContext as ApplicationViewModel;
      TextBox TxtName = sender as TextBox;

      if (TxtName == null)
        return;

      Binding newBinding = new Binding("Name");
      newBinding.ValidatesOnDataErrors = true;
      newBinding.ValidatesOnExceptions = true;
      newBinding.NotifyOnValidationError = true;
      newBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
      validator.ErrorMessage = "Labware name should be unique.";
      validator.ApplicationViewModel = appVm;
      if (!newBinding.ValidationRules.Contains(validator))
        newBinding.ValidationRules.Add(validator);
      TxtName.SetBinding(TextBox.TextProperty, newBinding);
    }

As you can see in above implementation we have created an object of Binding with new binding path. Also assigned UpdateSourceTrigger to newly created Binding object.

A binding can have multiple validation rules. We will add a validation rule into it. Now we can set the binding to the TextProperty of textbox.

Benefits: You can bind multiple dependency objects to the properties of Validation Rule object from code behind which is not possible with xaml. For example:

I used it to validate inputs on TextChanged event, where I compare input with the list of Items stored as public ObservableCollection property, bound with Grid, in ApplicationViewModel. Code of ValidationRule is as follows:

    public class UniqueValueValidator : ValidationRule
  {
    public string ErrorMessage { get; set; }
    public ApplicationViewModel ApplicationViewModel { get; set; }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
      if (value == null)
        return null;

      var lws = ApplicationViewModel.Inputs.Where(s => s.Name.Equals(value.ToString())).FirstOrDefault();
      if (lws != null)
        return new ValidationResult(false, ErrorMessage);


      return new ValidationResult(true, null);
    }
  }

Above code takes input and checks availability in the "Inputs" observable collection. Rule give false ValidationResult if value exists. Through this implementation I check uniqueness of inputs on run-time.

Hope you guys have enjoyed it.

S.Mishra
  • 3,394
  • 28
  • 20
0

I think this the right way to do this:

 Binding binding = new Binding();

 binding.Source = new CustomerViewModel();;
 binding.Path = new PropertyPath("Customer.Name");
 binding.Mode = BindingMode.TwoWay;
 binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
 txtCustName.SetBinding(TextBox.TextProperty, binding);
Ankush Madankar
  • 3,689
  • 4
  • 40
  • 74