I cannot get a two-way binding in WPF to work as it should do.
What I got is a class called UserCache having a property called ‘Current’. An Object of this class is placed within a facading class, called ConnectedUserCache, which also has got a property, called ‘Current’.
MainWindow.xaml
[...]
<Window.DataContext>
<local:ConnectedUserCache/>
</Window.DataContext>
[...]
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Style="{StaticResource style1}" >Vorname:</Label>
<TextBox Text="{Binding Path=Current.FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<Label Style="{StaticResource style1}" Grid.Row="1">Nachname:</Label>
<TextBox Grid.Row="1" Text="{Binding Path=Current.LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<Label Style="{StaticResource style1}" Grid.Row="2">Telefon:</Label>
<TextBox Grid.Row="2" Text="{Binding Path=Current.PhoneNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</Grid>
[...]
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private ConnectedUserCache _cache = new ConnectedUserCache();
public MainWindow()
{
InitializeComponent();
DataContext = _cache;
}
private void Button_SeachUserInCache_Click(object sender, RoutedEventArgs e)
{
var cache = DataContext as ConnectedUserCache;
if (cache == null) return;
cache.SearchCurrentUser();
// Reload datasource of datagrid // Todo: Looks like a workaround. Can this be improved? .Items.Refresh() does not work.
UserDataGrid.ItemsSource = null;
UserDataGrid.ItemsSource = cache.UsersShown;
}
[...]
ConnectedUserCache.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using UserWpfClient.Http;
using UserWpfClient.LocalData;
namespace UserWpfClient
{
internal class ConnectedUserCache : INotifyPropertyChanged
{
private UserCache _userCache = new UserCache();
private HttpUserClient _userClient = new HttpUserClient();
//
// Constructors
//
public ConnectedUserCache() {}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[...]
//
// Events
//
//public delegate void PropertyChangedEventHandler();
public event PropertyChangedEventHandler? PropertyChanged;
//
// Properties
//
public UserModelView Current
{
get { return _userCache.Current; }
set {
_userCache.Current = value;
OnPropertyChanged(nameof(Current));
}
}
[...]
//
// Methods
//
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
[...]
UserCache.cs
public class UserCache : INotifyPropertyChanged
{
private Collection<UserModelView> _allUsers = new Collection<UserModelView>();
private UserModelView _current = new UserModelView();
//
// Events
//
public event PropertyChangedEventHandler? PropertyChanged;
//
// Properties
//
public ObservableCollection<UserModelView> UsersShown { get; set; } = [];
public UserModelView Current
{
get => _current;
set
{
_current = value;
OnPropertyChanged(nameof(Current));
}
}
//
// Methods
//
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[...]
UserViewModel.cs
using System.ComponentModel;
using UserWpfClient.Attributes;
namespace UserWpfClient.LocalData
{
public class UserModelView : INotifyPropertyChanged
{
private string _firstName = string.Empty;
private string _lastName = string.Empty;
private string _phoneNumber = string.Empty;
public event PropertyChangedEventHandler? PropertyChanged;
[ColumnName("Vorname")]
public string FirstName
{
get => _firstName ?? string.Empty;
set
{
_firstName = value;
OnPropertyChanged(nameof(FirstName));
}
}
[ColumnName("Nachname")]
public string LastName
{
get => _lastName ?? string.Empty;
set
{
_lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
[ColumnName("Telefon")]
public string PhoneNumber
{
get => _phoneNumber ?? string.Empty;
set
{
_phoneNumber = value;
OnPropertyChanged(nameof(PhoneNumber));
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I found the following: Two-way binding in WPF
and Wpf two way binding in listbox .
And as you can see, I changed both classes (UserCache and ConnectedUserCache) acordingly.
At next I will follow this: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty But as it reduces the amount of boilerplate, I think I still have to understand the root cause of my problem, which means understanding WPF and its Events much better.
MainWindow.xaml
There I made several attempts with Mode=TwoWay
and/or UpdateSourceTrigger=PropertyChanged
.
UserCache.cs
There I tried it with and without INotifyPropertyChanged.
Expectation and what really happens:
I expect that if those TextBoxes have got content, that related property ‘Current’ has also got such content, as also property ‘Current’ within – and all this the other way arround, too.
But what happens is, if I write something into one of those textboxes during runtime as a user, to e.g. start filtering users on a datagrid (bound to the cache behind), it works fine. Then, after showing all users again (which works fine) and input the textbox is still be shown, when I start filtering again, it should work like before – as it uses the same code.
But it doesn’t filter again. I have to rewrite into the textbox again. So it seems like the bounding needs an update-trigger or in other words boudning seems to be lost for this specific moment.
Qestions:
So, when I write down these lines, I wonder if I search in the wrong direction. Is this a binding-problem? Or does the problem lie somewhere else?
My next step:
If you give me a fiew moments, during the next days, I will my git-repositiories (WPF-Client and Backend-Sercice) make public. But, please don’t expect something operfect. It is just a study for learning.