如何刷新循环内设置的可视控件属性(TextBlock.text)?

Dev*_*Dan 12 wpf binding wpf-controls inotifypropertychanged

每次循环迭代,我都想直观地更新文本块的文本.我的问题是WPF窗口或控件在循环完成之前不会直观刷新.

for (int i = 0; i < 50; i++)
{
    System.Threading.Thread.Sleep(100);
    myTextBlock.Text = i.ToString();                
}
Run Code Online (Sandbox Code Playgroud)

在VB6中,我会打电话DoEvents()control.Refresh.目前我只是喜欢类似的快速和肮脏的解决方案DoEvents(),但我也想了解替代方案或"正确"的方法来做到这一点.我可以添加一个简单的绑定语句吗?语法是什么?提前致谢.

Gre*_*som 26

如果您真的想要快速而肮脏的实现并且不关心将来维护产品或关于用户体验,您只需添加对System.Windows.Forms的引用并调用System.Windows.Forms.Application.DoEvents():

for (int i = 0; i < 50; i++)
{
    System.Threading.Thread.Sleep(100);
    MyTextBlock.Text = i.ToString();
    System.Windows.Forms.Application.DoEvents();
}
Run Code Online (Sandbox Code Playgroud)

缺点是它真的很糟糕.您将在Thread.Sleep()期间锁定UI,这会使用户烦恼,并且您可能最终得到不可预测的结果,具体取决于程序的复杂性(我已经看到一个应用程序,其中有两个方法在运行UI线程,每个都重复调用DoEvents()...).

这是应该如何做到的:

  1. 每当您的应用程序必须等待某些事情发生时(即磁盘读取,Web服务调用或Sleep()),它应该在一个单独的线程上.
  2. 您不应手动设置TextBlock.Text - 将其绑定到属性并实现INotifyPropertyChanged.

这是一个显示您要求的功能的示例.它只需要几秒钟的时间来编写,并且它更容易使用 - 而且它不会锁定UI.

XAML:

<StackPanel>
    <TextBlock Name="MyTextBlock" Text="{Binding Path=MyValue}"></TextBlock>
    <Button Click="Button_Click">OK</Button>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

代码隐藏:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            for (int i = 0; i < 50; i++)
            {
                System.Threading.Thread.Sleep(100);
                MyValue = i.ToString();
            }
        });
    }

    private string myValue;
    public string MyValue
    {
        get { return myValue; }
        set
        {
            myValue = value;
            RaisePropertyChanged("MyValue");
        }
    }

    private void RaisePropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}
Run Code Online (Sandbox Code Playgroud)

代码可能看起来有点复杂,但它是WPF的基石,它与一些练习结合在一起 - 非常值得学习.

  • 优秀!其他答案很好,但我最了解这个答案,它包括我要求的快速而肮脏的解决方案。为了使 Greg 的解决方案起作用,我添加了: using System.ComponentModel; (和)使用 System.Threading.Tasks;。不要忘记引用 System.Windows.Forms 以使 DoEvents() 解决方案起作用。我学到了很多。谢谢! (2认同)

Jaz*_*azz 5

我尝试了这里公开的解决方案,直到我添加以下内容后它才对我有效:

创建扩展方法并确保从项目中引用其包含的程序集.

public static void Refresh(this UIElement uiElement){
    uiElement.Dispatcher.Invoke(DispatcherPriority.Background, new Action( () => { }));
}
Run Code Online (Sandbox Code Playgroud)

然后在之后立即调用RaisePropertyChanged:

RaisePropertyChanged("MyValue");
myTextBlock.Refresh();
Run Code Online (Sandbox Code Playgroud)

这将迫使UI线程暂时控制并在UI元素上分派任何挂起的更改.