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)它工作正常(但这不是解决方案)

编辑 这是 ViewModel 基础,每个 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

以下是有关确保源自非 UI 线程的调用正确调用 UI 方法所需做的详细答案:

确保事物在 WPF 中的 UI 线程上运行

于 2013-06-24T12:10:16.723 回答
1

根据此页面(强调我的):

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

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

于 2013-07-05T17:26:19.757 回答
1

@马诺利斯Xoutasis,

我不知道 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 回答
1

您需要INotifyPropertyChanged在您的视图模型类型上实现,并让LoadingTextsetter 引发该事件。

于 2013-06-24T12:36:13.750 回答
0

现在你可以这样做

窗体/窗口的构造函数中

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);                
}
于 2017-11-08T07:12:22.810 回答