6

内部に TextBlock を含む Window を作成しました。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)のテキストを動的に変更すると、正常に動作します(ただし、解決策ではありません)。

EDIT これは ViewModel Base であり、すべての ViewModel がそのクラスを実装します。

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>
4

5 に答える 5

1

このページによると(強調鉱山):

C# の非同期言語機能が遅いように見えるので、これを怖がらせるかもしれませんが、これは公平なテストではありません。これほど時間がかかるのは、プログラムに多くの作業を任せたためです。単純な同期バージョンが UI スレッドで実行される場合、WPF は、LogUris コレクションに追加する各項目に対して即時の作業をほとんど行いません。データ バインディングは変更を検出します (ListBox をそのコレクションにバインドしたので、変更通知イベントを探します) が、コードが UI スレッドで終了するまで、WPF はこれらの変更を完全に処理しません。データ バインディングは、ディスパッチャー スレッドが優先度の高い作業を行う必要がなくなるまで、その作業を延期します。

これが、ディスパッチャーを介してプロパティを更新すると機能する理由かもしれません。GetBindingExpression(Property).UpdateTarget()2経由でターゲットを強制的に更新しようとしましたか?

于 2013-07-05T17:26:19.757 に答える
1

@ マノリス・シュンタシス

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>

このコードがお役に立てば幸いです。

于 2013-07-05T02:28:58.107 に答える