2013-06-24 100 views
6

我创建了一个带有TextBlock的窗口。我绑定了Text属性,一切正常。 但是 当我在一个任务内改变有界属性时,没有任何工作!绑定和异步操作

你知道为什么吗?

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 

文本2和3从不显示。如果我动态改变textblcok(例如:m_oView.txtLoadingText.Text)的文本,它工作正常(但它是mnot的解决方案)

编辑 这是视图模型基地,每个视图模型实现了类。

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 

StartProgress怎么叫:

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

编辑 绑定TextBlock的物业

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> 

回答

1

这里有你需要做的,以确保有什么详细的解答来自非UI线程的调用调用UI方法s请正确:

Ensuring that things run on the UI thread in WPF

+0

我没有任何错误,当我尝试设置文本属性的构造函数。此外,我使用Task,无论是Dispatcher还是BackgroundWorker,以关注调用UI。另外,如果我强制TextBlock更改文本,例如m_oView.txtLoadingText.Text =“something”,那么它会改变! –

1

您需要实现INotifyPropertyChanged您的视图模型类型,并有LoadingText二传手提高该事件。

+0

我已经这样做了。来自Task之外的代码,如LoadingText =“text 1”完美的作品。任务内的代码不起作用! –

+0

我怀疑'StartProgress'被称为初始化的一部分。在这种情况下,绑定会在你设置“LoadingText”之后生效。当你的'async'方法稍后恢复时,* update *失败。这完全表明了一个不正确的'INotifyPropertyChanged'实现。 –

+0

我编辑了我的帖子,以了解我如何实现INotifyPropertyChanged以及如何调用StartProgress。你能看看吗? –

1

@马诺利斯Xountasis,

我不知道VB.net,但我在C#中测试代码,它是确定。下面是我的代码:

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> 

希望这个代码是对您有用。

+0

您使任务调用调度程序,这意味着它不是“真正”异步(认为我的功能将需要大约1-2分钟才能完成)。当我们调用PropertyChange(“something”)时,也会通过文档检查是否需要调用控件。我不觉得它完成了我的要求。谢谢你的时间!! –

1

根据这个page(重点煤矿):

现在,因为这让他们看起来慢,但是这是一个不公平的测试,这可能吓跑你的C#异步语言功能, 。这需要很长时间的原因是我们已经给程序多了许多工作要做。当线程的UI 上运行简单的同步版本时,WPF为我们添加到LogUris集合的 的每个项目执行的任务很少。 数据绑定将检测到更改 - 我们将绑定到该集合的ListBox,因此它将寻找更改 通知事件 - 但WPF将不会完全处理这些更改,直到 我们的代码已完成与UI线程。数据绑定推迟其工作 ,直到调度程序线程没有更高优先级的工作要做。

这可能是它通过调度程序更新属性的原因。您是否尝试通过GetBindingExpression(Property).UpdateTarget()2强制更新目标?

+0

我没有..我会试一试,我会通知你!谢谢你的时间。 –

0

Nowdays你可以像这样

表/窗口

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