Search code examples
c#wpfdata-binding

Button's SourceUpdated handler not being triggered, and changes to bound source not reflected in the Button content


I am experimenting with the NotifyOnSourceUpdated property in the following minimal WPF app:

XAML file:

<Window x:Class="notify_on_source_updated.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:notify_on_source_updated"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Button x:Name="myButton" Click="myButton_Click" />
</Window>

Code-behind:

namespace notify_on_source_updated
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public string SourceText {get; set;}
        
        public MainWindow()
        {
            InitializeComponent();
            SourceText = "test";
            Binding binding = new Binding();

            binding.Source = SourceText;
            binding.NotifyOnSourceUpdated=true;
            
            myButton.SourceUpdated += myButton_SourceUpdated;
            myButton.SetBinding(ContentControl.ContentProperty, binding);
        }

        private void myButton_Click(object sender, RoutedEventArgs e)
        {
            SourceText = "changed";
        }

        private void myButton_SourceUpdated(object sender, DataTransferEventArgs e)
        {
            MessageBox.Show("Updated!");
        }
    }
}

There are two things I'm confused about here:

  1. When I click the button, it's Content is not updated with the string "changed"
  2. The handler I'm attaching to the myButton.SourceUpdated event is not being triggered and I never get the message box.

Why is it that the two above behaviors are not occurring? What am I missing here?


Solution

  • You need to specify a binding path:

    public MainWindow()
    {
        InitializeComponent();
        SourceText = "test";
        Binding binding = new Binding();
    
        binding.Path = new PropertyPath(nameof(SourceText));
        binding.Source = this;
        binding.NotifyOnSourceUpdated=true;
    
        // intList is the ListBox
        myButton.SourceUpdated += myButton_SourceUpdated;
        myButton.SetBinding(ContentControl.ContentProperty, binding);
    }
    

    Then for the Button to update, you need to implement the INotifyPropertyChanged interface and raise the PropertyChanged event for the data-bound source property:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _sourceText;
        public string SourceText
        {
            get { return _sourceText; }
            set 
            { 
                _sourceText = value; 
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SourceText))); 
            }
        }
    
        public event PropertyChangedEventHandler? PropertyChanged;
        ...
    }
    

    The other option would to be explicitly update the binding:

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        SourceText = "changed";
        myButton.GetBindingExpression(ContentControl.ContentProperty).UpdateTarget();
    }
    

    And the SourceUpdated event occurs when a value of an input control is transferred from the binding target to the binding source, for example when you type something into a data-bound TextBox. It's not raised in your example.