Binding and Asynchronous Operations

I created a window with a TextBlock inside. I bound the Text property and everything works fine. BUT when I change a restricted property inside a task, 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 displayed. If I dynamically change textblcok text (for example: m_oView.txtLoadingText.Text), it works fine (but this is not a solution)

EDIT This is a ViewModel base, each ViewModel implements this 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 a TextBlock to a 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> 
+6
source share
5 answers

A detailed answer on what you need to do to make sure that calls that occur on threads other than the UI call the UI methods correctly:

Ensuring everything runs in the user interface thread in WPF

+1
source

You need to implement INotifyPropertyChanged in your view model type and install the LoadingText installer for this event.

+1
source

@ Manolis Xountasis,

I do not know VB.net, but I am testing code in C #, this is normal. 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 you find this code useful.

+1
source

According to this page (my attention):

Now this may scare you away from the asynchronous functions of the C # language, because it makes them seem slow, but this is not an honest test. the reason it takes much longer is that we gave the program a lot more work. When a simple, synchronous version is launched in the thread user interface, WPF does very little immediate work for each LogUris collection item we add. Data binding will detect a change — weve associated the ListBox with this collection, so it will look for notification changes, but WPF will not fully process these changes until our code has finished working with the user interface thread. Data binding prevents it from working until the dispatcher thread works in a higher priority way.

This may be the reason for it to work if you update the property through the dispatcher. Are you trying to force update target information using GetBindingExpression(Property).UpdateTarget() 2 ?

+1
source

Now you can do it

In the constructor of 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); } 
0
source

Source: https://habr.com/ru/post/947987/


All Articles