Search code examples
c#wpfdata-binding

WPF Title binding not getting updated


I'm trying to bind the window title to the value of a property of a custom class. The issue is that the window's title is not updated when the property is updated.

The custom class:

public class ObservableWindowTitle : INotifyPropertyChanged
{
    public string AppName { get; }

    private string _currentFileName = string.Empty;
    public string CurrentFileName
    {
        get => _currentFileName;
        set
        {
            if (value is null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (_currentFileName != value)
            {
                _currentFileName = value;
                PropertyChanged?.Invoke(this, new(nameof(CurrentFileName)));
            }
        }
    }

    private bool _isUnsaved = false;
    public bool IsUnsaved
    {
        get => _isUnsaved;
        set
        {
            if (_isUnsaved != value)
            {
                _isUnsaved = value;
                PropertyChanged?.Invoke(this, new(nameof(_isUnsaved)));
            }
        }
    }

    public string Title
    {
        get => string.Format("{0}{1} - {2}",
            (IsUnsaved ? "*" : string.Empty),
            (CurrentFileName.Length == 0 ? "Untitled" : CurrentFileName),
            AppName);
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public ObservableWindowTitle(string appName) => AppName = appName;
}

The Window Title XAML:

Title="{Binding Path=Title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

The Window code:

public partial class MainWindow : Window
{
    const string fileDialogFilter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
    readonly ILogger<MainWindow> _logger;
    ObservableWindowTitle observableTitle = new((Application.Current.FindResource("AppName") as string)!);

    public MainWindow(ILogger<MainWindow> logger)
    {
        _logger = logger;
        DataContext = observableTitle;

        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        observableTitle.CurrentFileName = "SomeFile";
    }
}

The title is correctly displayed when starting the app: "Untitled - SharpNote" (AppName is a static resource with the value "SharpNote"). However, when clicking the button, the title does not update (should be "SomeFile - SharpNote").


Solution

  • You need to inform the Binding mechanism that the calculated property Title may have changed and needs to be re-evaluated. Add

    PropertyChanged?.Invoke(this, new(nameof(Title)));
    

    to the setter of CurrentFileName and IsUnsaved.

    BTW: PropertyChanged?.Invoke(this, new(nameof(_isUnsaved))); in your code is wrong; it needs to be PropertyChanged?.Invoke(this, new(nameof(IsUnsaved)));