Here is a rough sketch that (I think) does what you want. The magic is in the setter/getter method of the bound properties, which utilize the Update
method in INotifier
to update each other and the GUI and vice versa.
I used 2 text boxes (first changes the version number) because it simpler to make a quick GUI that way, but in principle I think this is what you are looking for:

.NET 6, Visual Studio WPF Application template:
MainWindow.xaml
:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:ViewModel}"
Title="MainWindow" Height="75" Width="100">
<StackPanel>
<TextBox Text="{Binding VersionNumber}"/>
<TextBox Text="{Binding VersionString}"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
(note, ViewModel
class should be its own file):
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp1
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged = null;
protected void Update<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChangedEventHandler? handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _versionString = "Ver1";
private string _versionNumber = "1";
private int _version = 1;
public string VersionString
{
get => _versionString;
set { Update(ref _versionString, value); }
}
public string VersionNumber
{
get => _versionNumber;
set
{
_versionNumber = value;
_version = int.Parse(_versionNumber);
VersionString = $"Ver{_version}";
}
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Final detail, I modified App.xaml
and App.xaml.cs
in order to wire the data context to the gui properly
xaml:
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<Application.Resources>
</Application.Resources>
</Application>
Code behind:
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var viewModel = new ViewModel();
var mainWindow = new MainWindow { DataContext = viewModel };
mainWindow.Show();
}
}
}