Events not being handled from a user control and main form - wpf

I am attempting to handle an event in the application main form where said event is being raised from a user control populated on the main form. I've defined an Interface for the event which is being implemented in both the Main Form and the User Control. I'm using MvvmLight from GalaSoft for the MVVM support.
The main form code behind indicates that the event is attached, but when I do the check in the user control, it indicates that there is no handler attached to the event, so obviously, it won't ever get to the handler.
Any help would be appreciated.
The interface I've defined is pretty basic:
Public Interface IEventFiring
Event EventFiring(sender As Object)
End Interface
My Main Form Xaml looks like this:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EventFiringFromContolToMainForm"
xmlns:efc="clr-namespace:EventFiringControl;assembly=EventFiringControl"
Title="MainWindow">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding StatusText, Mode=OneWay}"
Height="25"
Margin="5"/>
<Separator Margin="5"/>
<efc:UserControl1/>
</StackPanel>
</Window>
The Code Behind is:
Imports EventFiringControl
Class MainWindow
Implements IEventFiring
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
AddHandler EventFiring, AddressOf EventFiringSub
If EventFiringEvent Is Nothing Then
CType(DataContext, MainWindowViewModel).StatusText = "Event did NOT Attach!!"
Else
CType(DataContext, MainWindowViewModel).StatusText = "Event Attached"
End If
End Sub
Public Event EventFiring(sender As Object) Implements IEventFiring.EventFiring
Private Sub EventFiringSub()
CType(DataContext, MainWindowViewModel).StatusText = "Event Fired"
End Sub
End Class
The view model for the Main Form is:
Imports GalaSoft.MvvmLight
Public Class MainWindowViewModel
Inherits ViewModelBase
Private _statusText As String
Public Property StatusText As String
Get
Return _statusText
End Get
Set(value As String)
_statusText = value
RaisePropertyChanged(Function() StatusText)
End Set
End Property
End Class
Now for the User Control.
Xaml file is:
<UserControl x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EventFiringControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:EventFiringViewModel/>
</UserControl.DataContext>
<Grid>
<Button Content="Cancel" Command="{Binding CancelCommand}" Height="50"/>
</Grid>
</UserControl>
The code behind is:
Public Class UserControl1
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
End Class
Finally, the View Model for the User Control is:
Imports GalaSoft.MvvmLight.Command
Imports GalaSoft.MvvmLight
Public Class EventFiringViewModel
Inherits ViewModelBase
Implements IEventFiring
Public ReadOnly Property CancelCommand As RelayCommand
Get
Return New RelayCommand(Sub() CancelSub())
End Get
End Property
Private Sub CancelSub()
If EventFiringEvent IsNot Nothing Then
RaiseEvent EventFiring(Me)
End If
End Sub
Public Event EventFiring(sender As Object) Implements IEventFiring.EventFiring
End Class

Related

UserControl Command to modify Property

In a User Control, I am trying to get a Command to modify a Property. I have an IncrementValueCommand and a Value property that I want to increment when a button is clicked. The button's Command is bound to the IncrementValueCommand and the Content is bound to the Value property.
I have tried two approaches to do this and in both cases the Button doesn't show the Value incrementing..
1st Approach: Dependency Property for Value
XAML:
<UserControl x:Class="UserControl1"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Content="{Binding Path=Value}"
Command="{Binding Path=IncrementValueCommand}" />
</UserControl>
Code behind:
Public Class UserControl1
Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(UserControl1), New PropertyMetadata(1))
Public Property IncrementValueCommand As ICommand
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)
End Sub
Public Property Value() As Integer
Get
Return GetValue(ValueProperty)
End Get
Set(value As Integer)
SetValue(ValueProperty, value)
End Set
End Property
Private Sub IncrementValue()
Value += 1
End Sub
End Class
2nd approach: INotifyPropertyChanged for Value
XAML:
<UserControl x:Class="UserControl2"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Content="{Binding Path=Value}"
Command="{Binding Path=IncrementValueCommand}" />
</UserControl>
Code behind:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Public Class UserControl2
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _value As Integer = 1
Public Property IncrementValueCommand As ICommand
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)
End Sub
Public Property Value() As Integer
Get
Return _value
End Get
Set(value As Integer)
If _value <> value Then
_value = value
NotifyPropertyChanged()
End If
End Set
End Property
' This method is called by the Set accessor of each property.
' The CallerMemberName attribute that is applied to the optional propertyName
' parameter causes the property name of the caller to be substituted as an argument.
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Private Sub IncrementValue()
Value += 1
End Sub
End Class
I left out the RelayCommand class which is a standard implementation for ICommand.
Any help will be greatly appreciated.
Working Code (thanks to Peter Duniho for his answer)
Adjust the code-behind constructor by creating the IncrementValueCommand first:
Public Sub New()
' Add any initialization after the InitializeComponent() call? Nah
IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)
' This call is required by the designer.
InitializeComponent()
End Sub
As I've explained in this comment, the problem in this particular variant of your attempts to use a command to update a value is that you are initializing the IncrementValueCommand property after the call to InitializeComponent() in the class constructor.
The InitializeComponent() call is where the binding to that property is set up, i.e. Command="{Binding Path=IncrementValueCommand}" in your XAML. When that call is made, the property still has its default value of null.
When you assign the property a value later, because the property is an auto-implemented property, there's nothing about that assignment that would cause a property-change notification to happen, so the binding is never updated to reflect the new value.
You can either implement property-change notification for that property, just as is already done for the Value property, or you can (as I had suggested earlier) move the assignment within the constructor so that it occurs before the call to InitializeComponent instead of after.

Can't declare the viewmodel in XAML for this code

I've reviewed as many 'how to declare the viewmodel in XAML' posts I can and still can't figure this out. I'm using the simple program below to learn the basics regarding binding and this code works. When I click to insert(add) items, the listbox automatically reflects that change, as well as when I clear the list.
See my question(s) after the code.
Model
Namespace MVVM3
Public Class MyListItem
Public Property MyListItemID() As Integer
Public Property Name() As String
End Class
End Namespace
ViewModel
Namespace MVVM3
Public Class ViewModel
Implements INotifyPropertyChanged
Public Property allIdeas() As New ObservableCollection(Of MyListItem)
Public Sub New()
For index = 0 To 9
Dim anItem As New MyListItem
anItem.Name = "Idea " & index
allIdeas.Add(anItem)
Next
End Sub
Public Sub InsertAnItem()
Dim anItem As New MyListItem
anItem.Name = "Item " & allIdeas.Count()
allIdeas.Add(anItem)
NotifyPropertyChanged("allIdeas")
End Sub
Public Sub ClearStoredList()
allIdeas.Clear()
NotifyPropertyChanged("allIdeas")
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
End Namespace
View
<Window x:Class="MVVM3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:mvvm3"
Title="MainWindow" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen"
>
<StackPanel Grid.Column="1">
<Button Margin="25" Content="Insert an item" Click="InsertAnItem_Click"/>
<Button Margin="25" Content="Clear stored list" Click="ClearStoredList_Click"/>
<ListBox Name="listBox3" ItemsSource="{Binding allIdeas}" DisplayMemberPath="Name" Height="100">
</ListBox>
</StackPanel>
</Window>
Code Behind
Namespace MVVM3
Partial Class MainWindow
Private vm = New ViewModel
Sub New()
InitializeComponent()
DataContext = vm
End Sub
Private Sub InsertAnItem_Click(sender As Object, e As RoutedEventArgs)
vm.InsertAnItem()
End Sub
Private Sub ClearStoredList_Click(sender As Object, e As RoutedEventArgs)
vm.ClearStoredList()
End Sub
End Class
End Namespace
I want to move the ViewModel declaration into XAML to eliminate code behind. If I comment out DataContext = vm, no matter what method I've followed from various posts, the binding no longer updates the listbox.
The following changes result in the listbox showing the initial assignment that takes place in ViewModel.New, but after that no changes are reflected:
<Window x:Class="MVVM3.MainWindow"
...
xmlns:local="clr-namespace:mvvm3"
xmlns:vm="clr-namespace:mvvm3.MVVM3"
...
>
<Window.DataContext>
<vm:ViewModel/>
</Window.DataContext>
What am I missing? Is it a namespace problem?
I'm hoping to move on to Commands and ViewModel locators, but I don't see how I can do that until I understand this.
When you declare an instance of the ViewModel class in XAML, you may access it in code behind by casting the value of the DataContext property to the ViewModel type:
Partial Class MainWindow
Sub New()
InitializeComponent()
End Sub
Private Sub InsertAnItem_Click(sender As Object, e As RoutedEventArgs)
CType(DataContext, ViewModel).InsertAnItem()
End Sub
Private Sub ClearStoredList_Click(sender As Object, e As RoutedEventArgs)
CType(DataContext, ViewModel).ClearStoredList()
End Sub
End Class

WPF binding with .NET object not communicating the data

I am following a tutorial on WPF data binding. I am trying to bind to a .NET object's property to a XAML control but the control does not display the expected data. These are what I believe to be the relevant sections of code:
In procedural code: (Note: removed ObservableCollection in PhotoGallery after original post)
Namespace PhotoGallery
Partial Public Class MainWindow
Inherits Window
Private photos As New Photos
...
End Class
Namespace PhotoGallery
Public Class Photos
Inherits Collection(Of Photo)
...
End Class
In XAML (Solution/Project name is Ch13-PhotoGallery):
<Window x:Class="PhotoGallery.MainWindow"
...
xmlns:local="clr-namespace:Ch13_PhotoGallery.PhotoGallery"
...>
<Window.Resources>
<local:Photos x:Key="Photos"/>
</Window.Resources>
And this is the control that is not displaying the data, which is the size of the Photos collection:
<Label x:Name="numItemsLabel" Background="AliceBlue" FontSize="8" Content="{Binding Source={StaticResource Photos}, Path=Count}"/>
When I typed in the < Label >, Intellisense popped up 'Count' for the Path property, so I think that tells me I have everything defined correctly.
If I add this line of procedural code behind to the refresh() method:
numItemsLabel.Content = photos.Count
Then the count is displayed correctly.
But I'm not getting the binding in XAML to display Photos.Count.
This creates a new instance of the Photos class:
<local:Photos x:Key="Photos"/>
If you want to bind to the Photos collection that you have created in your MainWindow.xaml.vb file you should expose it as a public property - you can only bind to properties but not fields - and set the DataContext of the window to an instance of the class where this property is defined, i.e. the window class itself in your case:
Class MainWindow
Public Property Photos As Photos
Public Sub New()
' This call is required by the designer.
InitializeComponent()
DataContext = Me
...
End Sub
End Class
You can the bind directly to the property:
<Label x:Name="numItemsLabel" Background="AliceBlue" FontSize="8" Content="{Binding Path=Photos.Count}"/>
Your ViewModel needs to implement the INotifyPropertyChanged interface, this will let your window listen to changes to your ViewModel
Here's an example of how to implement this interface in VB
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7de44362-8b88-4292-b4ee-0385c3b34d7d/im-just-looking-for-a-simple-vb-net-mvvm-sample-wpf?forum=wpf
ViewModel
Public Class ViewModel
Implements INotifyPropertyChanged
Public Sub New()
Me.myTextValue = "default value..."
End Sub
Private myTextValue As String = String.Empty
Public Property MyTextProperty() As String
Get
Return Me.myTextValue
End Get
Set(ByVal value As String)
Me.myTextValue = value
NotifyPropertyChanged("MyTextProperty")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding MyTextProperty}"/>
</Grid>
</Window>
XAML Code Behind
Class MainWindow
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = New ViewModel()
End Sub
End Class

WPF INotifyPropertyChanged not updating label

I'm currently learning some basics in WPF and I've been looking for the mistake for about 2 days. Hope you guys can help.
I'm trying to update my UI (in this case the content of a label) by using INotifyPropertyChanged and a binding in XAML. The thing is: it only takes the first value and puts it in the content. Furthermore nothing happens but the event (OnPropertyChanged) is fired.
This is what I have in XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:View x:Key="ViewModel"/>
</Window.Resources>
<Grid Margin="0,0,2,-4" DataContext="{Binding Source={StaticResource ViewModel}}">
....
<Label x:Name="lbl_money" Grid.ColumnSpan="2" Content="{Binding Path=PropMoney}" HorizontalAlignment="Left" Margin="403,42,0,0" VerticalAlignment="Top">
And this is the necessary part of my class View:
Public Class View
Inherits ViewModelBase
Private rest1 As New Restaurant
Private mainPlayer As New Player
Private mycurrentMoney As Double = 3
Private currentClickIncrease = mainPlayer.PropClickIncrease
Public Property PropMoney() As Double
Get
Return mycurrentMoney
End Get
Set(value As Double)
mycurrentMoney = value
OnPropertyChanged("mycurrentMoney")
End Set
End Property
Sub SelfClicked()
PropMoney() += 1
End Sub
Last but not least the MainWindow class, where i instantiate my view:
Class MainWindow
Private view As New View
Sub New()
InitializeComponent()
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
view.SelfClicked()
End Sub
End Class
So my mycurrentMoney is increasing each click and the event is fired but the label doesn't update.
Thank you in advance!
If you have Visual Studio 15 use NameOf operator instead of string literal like so:
NameOf(PropMoney);
If you later rename your property, it will still work opposed to string literal which will NOT. Alternatively modify your OnPropertyChange to make use of CallerMemberName
OnPropertyChange ([System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
{
}
The property name will be filled in, this works only in setter for current property however.
Also, set DataContext for whole window (Setting DataContext in XAML in WPF). DataContext={StaticResource ViewModel} and don't use Path in your Binding, just {Binding PropertyName}
Your OnPropertyChanged("mycurrentMoney") statement won't raise a property change on your property, because it's called PropMoney.
You have to set OnPropertyChanged("PropMoney") in your setter instead.
There are 2 problems with your code
First you raise PropertyChanged event for the backing field and should raise it for property name
OnPropertyChanged("PropMoney")
Second the property you change belong to different instance of View then the one set as DataContext. So in XAML remove DataContext changes, only leave property binding
<Window ...>
<Grid Margin="0,0,2,-4">
<!-- .... -->
<Label ... Content="{Binding Path=PropMoney}">
and then in code set DataContext of MainWindow to the instance that you create and modify
Class MainWindow
Private view As New View
Sub New()
InitializeComponent()
DataContext = view
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
view.SelfClicked()
End Sub
End Class

Binding and Async Operations

I have created a Window with a TextBlock inside. I have bound the Text property and everything works fine.
BUT
When I change the bounded property while inside a Task then nothing works!!
Do you know why?
Public Async Sub StartProgress()
Try
LoadingText = "text 1" 'Works perfect
Dim fResult As Boolean = Await LoadModules()
If Not fResult Then
MessageBox.Show(Me.Error)
End If
m_oView.Close()
Catch ex As Exception
Msg_Err(ex)
End Try
End Sub
Public Async Function LoadModules() As Task(Of Boolean)
Try
Await Task.Delay(3000)
LoadingText = "text 2" 'Nothing Happens
Await Task.Delay(5000)
LoadingText = "complete" 'Nothing Happens
Await Task.Delay(3000)
Return True
Catch ex As Exception
Me.Error = ex.Message
Return False
End Try
End Function
text 2 and 3 are never shown. If I change dynamically the Text of the textblcok(ex : m_oView.txtLoadingText.Text) It works fine(but it's mnot a solution)
EDIT
This is the ViewModel Base, every ViewModel implements that Class.
Public Class VM_Base
Implements IDisposable
Implements INotifyPropertyChanged
Private m_oDS As MxDataSet
Public Property [Error] As String
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Protected Sub New()
m_oDS = New MxDataSet
End Sub
Protected Overrides Sub Finalize()
Try
Me.Dispose(False)
Debug.Fail("Dispose not called on ViewModel class.")
Finally
MyBase.Finalize()
End Try
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Me.Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(disposing As Boolean)
End Sub
Protected Overridable Sub OnPropertyChanged(propertyName As String)
Me.EnsureProperty(propertyName)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
<Conditional("DEBUG")> _
Private Sub EnsureProperty(propertyName As String)
If TypeDescriptor.GetProperties(Me)(propertyName) Is Nothing Then
Throw New ArgumentException("Property does not exist.", "propertyName")
End If
End Sub
End Class
How StartProgress is Called:
<i:Interaction.Triggers>
<i:EventTrigger EventName="ContentRendered">
<i:InvokeCommandAction Command="{Binding DataContext.WindowsActivatedCommand,ElementName=fLoading}" />
</i:EventTrigger>
</i:Interaction.Triggers>
EDIT
Binding TextBlock to Property
Public Property LoadingText As String
Get
Return m_sLoadingText
End Get
Set(value As String)
m_sLoadingText = value
OnPropertyChanged("LoadingText")
End Set
End Property
<TextBlock x:Name="txtLoading" Width="450"
Grid.Row="1" VerticalAlignment="Center"
HorizontalAlignment="Left"
TextWrapping="Wrap" Text="{Binding LoadingText}">
</TextBlock>
Here's a detailed answer on what you need to do to make sure calls that originate on non-UI threads invoke UI methods properly:
Ensuring that things run on the UI thread in WPF
You need to implement INotifyPropertyChanged on your view model type, and have the LoadingText setter raise that event.
# Manolis Xountasis,
I do not know VB.net, but I test code in C#, it is ok. Below is my code:
public partial class MainWindow : Window
{
private TestViewModel _tvm = new TestViewModel();
public MainWindow()
{
InitializeComponent();
this.wndTest.DataContext = _tvm;
_tvm.TestData = "First Data";
this.btnAsync.Click += BtnAsyncOnClick;
}
private void BtnAsyncOnClick(object sender, RoutedEventArgs routedEventArgs)
{
var task = Task.Factory.StartNew(() => this.Dispatcher.Invoke(new Action(() => _tvm.TestData = "changed data")));
}
}
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="WpfApplication3.MainWindow"
x:Name="wndTest"
Title="MainWindow"
Height="350"
Width="525">
<!--<Window.Resources>
<local:TestViewModel x:Key="TestViewModelDataSource" d:IsDataSource="True"/>
</Window.Resources>
<Window.DataContext>
<Binding Mode="OneWay" Source="{StaticResource TestViewModelDataSource}"/>
</Window.DataContext>-->
<Grid>
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding TestData}" />
<Button x:Name="btnAsync" Content="Change Async" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
Hope this code is useful for you.
According to this page (emphasis mine):
Now this might scare you off the C# asynchronous language features,
because it makes them seem slow, but this is not a fair test. The
reason this takes so much longer is that we’ve given the program much
more work to do. When the simple, synchronous version runs on the UI
thread, WPF does very little immediate work for each item we add to
the LogUris collection. Data binding will detect the change—we’ve
bound a ListBox to that collection, so it’ll be looking for change
notification events—but WPF won’t fully process those changes until
our code has finished with the UI thread. Data binding defers its work
until the dispatcher thread has no higher priority work to do.
That may be the reason why it works if you update the property via the dispatcher. Have you try to force an update in the target via GetBindingExpression(Property).UpdateTarget()2 ?
Nowdays you can do like this
In constructor of the Form/Window
btnAddNewCard.Click += async (s, e) => await BtnAddNewCard_ClickAsync(s, e);
private async Task BtnAddNewCard_ClickAsync(object sender, RoutedEventArgs e)
{
// Any code like
await ShowOKMessageAsync(msg);
}

Resources