I am currently building a control library for WPF, WinUI3 and UWP. I use custom ContentControls for my controls with DependencyProperties. On some DependencyProperties (e.g. IsChecked) I have to use a PropertyChangedCallback to update some UI related stuff.
First I worked on WPF and evetyhing worked great since I am already used to WPF but I have some issues for UWP and WinUI3.
My Code
First of all Im going to share all my important code that might be related to the issue.
Both (in WPF and UWP) ViewModels and control code behinds are the exact same and Im binding both with a TwoWay binding. Please note that im using 'CommunityToolkit.Mvvm' which shouldnt make any difference through.
This is the DependencyProperty:
public bool IsChecked
{
get => (bool)GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(
"IsChecked", typeof(bool), typeof(ReCaptcha), new PropertyMetadata(false, OnIsCheckedChanged));
This is the PropertyChangedCallback:
private static void OnIsCheckedChanged(
DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
ReCaptcha owner = (ReCaptcha)sender;
UpdateUI(owner, (bool)e.NewValue ? "True" : "False");
owner.VerificationRequested?.Invoke(owner, new())
owner.VerifcationRequestedCommand?.Execute(owner.VerificationRemovedCommandParameter)
}
This is the ViewModel:
bool isChecked = false;
public bool IsChecked // IsChecked
{
get => isChecked;
set => SetProperty(ref isChecked, value);
}
[RelayCommand] // VerifyCommand
async Task VerifyAsync()
{
// Do stuff with 'IsChecked'
}
This is the XAML:
<ui:ReCaptcha
IsChecked="{x:Bind viewModel.IsChecked, Mode=TwoWay}"
VerificationRequestedCommand="{x:Bind viewModel.VerifyCommand}" />
My Issue
Okay. So the user presses something on the control and the DependencyProperty and the ViewModel property update (because of TwoWay binding).
BUT the PropertyChangedCallback (on the DependencyObject) gets called before the Binding (= the ViewModel property) is updated and before the setter of the ViewModel property is called. Since I am calling an event/command (which gets executed in the ViewModel) inside the PropertyChangedCallback the property (IsChecked) still has the old value at the time the event/command fires. This results in weird behaviours and breaks the application.
Calling order
I determinatedthe call order by setting break points wherever properties are updated and callbacks are called. This helped me to track down the problem:
Calling order in WPF (how I expect it to be)
- [TRIGGER] User presses something on control
- Setter of property 'IsChecked' on ViewModel gets called
- PropertyChangedCallback on DependencyObject gets called
- VerifyCommand gets invoked and executed ('IsChecked' has NEW value)
Calling order in UWP/WinUI3 (how I dont expect it to be)
- [TRIGGER] User presses something on control
- PropertyChangedCallback on DependencyObject gets called
- VerifyCommand gets invoked and executed ('IsChecked' has OLD value)
- Setter of property 'IsChecked' on ViewModel gets called
Furthermore information for UWP/WinUI3
When I hit the break point inside the PropertyChangedCallback on the DependencyObject and I take a look at the properties of the sender object (my custom control) I notice that the 'IsChecked' property has the new value but when I hit the break point inside the VerifyCommand (which comes after the PropertyChangedCallback break point) and take a look at my custom control in the XAML the 'IsChecked' property still has the old value. (Seen in the images bellow).
This leads me to think that something is broken with the binding, but since Im using the exact same binding in WPF (and its working), I don't know what to do.
Break point in PropertyChangedCallback on DependencyObject.
Break point in VerifyCommand on ViewModel/XAML.
Notes
I dont know if Im missing something completely, but Ive been stuck on this for many hours and this is my last option, so any help would be appreciated. Ive tried my best to make the issue as clear as possible, but feel free to ask for any additional information I could provide.
I also tried researching online but there is almost no documentation about PropertyChangedCallbacks on UWP and definitely not about the call order. I know this is a very specific use case, but I really want to keep the project structure as it is.
Im sorry if I described my problem unclearly and with incorrect grammar/spelling. Thanks in advance.
I expect the same behavior in UWP/WinUI3 just like in WPF so my ViewModel first gets updated and then the PropertyChangedCallback gets called.
This is important since I have to use the property inside an event/command (in the ViewModel) that gets invoked by the PropertyChangedCallback.
Sample projects
I have created two small sample projects and uploaded them here if its still not clear what the problem is: https://github.com/IcySnex/PropertyChangedCallbackTests. You can run both projects and take a look at the Debug Output. I have added a few Debug WriteLines so you can see when the callbacks are fired.