2013-06-24 16 views
6

Ich habe ein Fenster mit einem TextBlock innerhalb erstellt. Ich habe die Text-Eigenschaft gebunden und alles funktioniert einwandfrei. ABER Wenn ich die beschränkte Eigenschaft innerhalb einer Aufgabe ändern, dann funktioniert nichts !!Binding und Async-Operationen

Wissen Sie warum?

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 und 3 werden nie angezeigt. Wenn ich dynamisch den Text des textblcok (ex: m_oView.txtLoadingText.Text) ändern fein Es funktioniert (aber es ist mnot eine Lösung)

EDIT Dies ist die Ansichtsmodell Base, implementiert jede Ansichtsmodell dieser Klasse.

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 

Wie StartProgress heißt:

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="ContentRendered"> 
     <i:InvokeCommandAction Command="{Binding DataContext.WindowsActivatedCommand,ElementName=fLoading}" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

EDIT Binding TextBlock- zum Objekt

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> 

Antwort

1

Hier ist eine detaillierte Antwort auf das, was Sie dafür tun müssen, machen Aufrufe, die von Nicht-UI-Threads stammen, rufen die UI-Methode auf richtig s:

Ensuring that things run on the UI thread in WPF

+0

Ich habe keine Fehler, wenn ich versuche, die Text-Eigenschaft festlegen. Außerdem verwende ich Task, weder Dispatcher noch BackgroundWorker, um auf das Aufrufen der Benutzeroberfläche zu achten. Auch wenn ich TextBlock erzwinge, den Text zum Beispiel m_oView.txtLoadingText.Text = "etwas" zu ändern, dann ändert es sich !! –

1

Sie benötigen INotifyPropertyChanged nach Ihrer Ansicht Modelltyp zu implementieren und haben die LoadingText Setter dieses Ereignis erhöhen.

+0

Das habe ich schon gemacht. Code außerhalb von Aufgabe wie LoadingText = "Text 1" Funktioniert perfekt. Code innerhalb der Aufgabe funktioniert nicht !! –

+0

Ich vermute, 'StartProgress' wird als Teil der Initialisierung aufgerufen. In diesem Fall werden die Bindungen erst wirksam, nachdem Sie 'LoadingText' gesetzt haben. Wenn Ihre Async-Methode später fortgesetzt wird, schlägt das * Update * fehl. Dies ist ein perfektes Anzeichen für eine falsche 'INotifyPropertyChanged' -Implementierung. –

+0

Ich habe meinen Beitrag bearbeitet, um zu sehen, wie ich INotifyPropertyChanged implementiere und wie ich StartProgress aufruft. Kannst du dir das mal ansehen?? –

1

@ Manolis Xountasis,

Ich weiß VB.net nicht, aber ich testen Code in C#, ist es ok. Unten ist mein 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> 

Hoffnung dieser Code für Sie nützlich ist.

+0

Sie machen die Aufgabe, den Dispatcher aufzurufen, was bedeutet, dass es nicht "wirklich" async ist (denke, dass meine Funktion etwa 1-2 Minuten benötigt, um fertig zu werden). Wenn wir PropertyChange ("etwas") aufrufen, überprüft es auch, ob das Steuerelement aufgerufen werden muss oder nicht. Ich fühle nicht, dass es meine Anforderungen erfüllt. Vielen Dank für Ihre Zeit!! –

1

Nach dieser page (Hervorhebung von mir):

das Sie jetzt vielleicht erschrecken die C# asynchronen Sprachfunktionen aus, , weil es ihnen langsam erscheinen lässt, aber das ist kein fairer Test. Die Grund dauert so viel länger ist, dass wir dem Programm viel mehr Arbeit zu tun gegeben haben. Wenn die einfache, synchrone Version auf dem UI-Thread ausgeführt wird, arbeitet WPF sehr wenig sofort für jedes Element, das wir der LogUris-Auflistung hinzufügen. Datenbindung erkennt die Änderung - wir haben gebunden eine ListBox an diese Auflistung, so wird es nach Änderungen suchen Benachrichtigungsereignisse - aber WPF wird diese Änderungen nicht vollständig verarbeiten, bis unser Code mit dem UI-Thread fertig ist . Die Datenbindung verzögert ihre Arbeit , bis der Dispatcher-Thread keine höhere Priorität hat.

Das kann der Grund sein, warum es funktioniert, wenn Sie die Eigenschaft über den Dispatcher aktualisieren.Haben Sie versucht, ein Update im Ziel über GetBindingExpression(Property).UpdateTarget()2 zu erzwingen?

+0

Ich habe nicht .. Ich werde es versuchen und ich werde Sie informieren !! vielen Dank für Ihre Zeit. –

0

Nowdays Sie dies tun kann

Im Konstruktor des Form/Fensters

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);     
}