【问题标题】:Updating TextBlock at start of code block in MainWindow asynchronously在 MainWindow 中的代码块开始处异步更新 TextBlock
【发布时间】:2015-08-14 10:13:50
【问题描述】:

我有一个问题困扰了我好几天,我已经尝试了所有选项,现在我将发布我自己的问题以寻求你们的一些具体帮助。

我需要在代码块的开头更新一个 TextBlock,它通过一个简单的按钮单击运行。

这是我的代码:

private void NewProject(bool blnCopy = false, string strFileName = null)
    {
      if (App.ApplicationBusy == false)
      {
        App.ApplicationBusy = true;

        try
        {
          Action action = delegate()
          {
            Cursor = Cursors.Wait;
            lblStatus.Text = "Opening Project...";
          };

          Dispatcher.Invoke(DispatcherPriority.Send, action);

          if (blnCopy == false) { Project = new GSProject((App.RecentProjectCount + 1)); }

          if (Project != null)
          {
            Projects.Add(Project);

            if (blnCopy == false)
            {
              if (strFileName == null)
              {
                Project.ProjectName = string.Format("GSProject{0}", Projects.Count.ToString());
                Project.ProjectDescription = string.Format("{0} - HW GS Project", Project.ProjectName);
                Project.LoadResource();
              }
              else
              {
                Project.Load(strFileName);
              }
            }
            else
            {
              Project = Project.Copy();
            }

            p_objNewPane = objDocker.AddDocument(Project.ProjectDisplayName, Project);

            if (p_objNewPane != null)
            {
              p_objNewPane.DataContext = Project;
              BindingOperations.SetBinding(p_objNewPane, ContentPane.HeaderProperty, new Binding("ProjectDisplayName") { Source = Project });
              p_objNewPane.Closing += new EventHandler<PaneClosingEventArgs>(ContentPane_Closing);
            }

            if (Project.CalculationExists == true)
            {
              InitializeCalculation(true);
            }
          }
          tabStartPage.Visibility = Visibility.Collapsed;
          objDocumentTabs.SelectedIndex = 0;
        }
        catch (Exception ex)
        {
          ModernDialog.ShowMessage(string.Format("An error has occurred:{0}{0}{1}", Environment.NewLine, ex.Message), "Error", MessageBoxButton.OK, Application.Current.MainWindow);
        }
        finally
        {
          App.ApplicationBusy = false;
          Cursor = Cursors.Arrow;
          AppStatus = "Ready";
          p_objNewPane = null;
        }
      }
    }

在 try 块开始时,我需要更新 TextBlock (lblStatus) 以说明发生了什么。 void 本身 NewProject 位于 MainWindow 上,通过单击按钮调用。

谁能告诉我我哪里出错了?我已经尝试了无数可能的解决方案,所以如果我回复你说我已经尝试过了,请不要生气。

问候,汤姆。

【问题讨论】:

  • 你没有说是什么问题,只是说你要更新一个TextBox..?
  • 抱歉,问题是 TextBlock 根本没有更新。它只是说“就绪”(在加载 MainWindow 时设置)。

标签: c# wpf multithreading xaml asynchronous


【解决方案1】:

经过几天的痛苦,我设法让这个工作正常进行。通过查看任务调度等,我完全找错了树。相反,所需要的只是一个 DependencyProperty。

XAML(主窗口):

    <TextBlock x:Name="lblStatus"
           Text="{Binding AppStatus, IsAsync=True}"
           Grid.Column="0"
           HorizontalAlignment="Left"
           VerticalAlignment="Center"
           FontFamily="Segoe UI"
           FontSize="12"
           Foreground="White"
           Margin="5, 0, 0, 0" />

C#(主窗口):

public string AppStatus
    {
      get { return (string)GetValue(AppStatusProperty); }
      set { SetValue(AppStatusProperty, value); }
    }
    public static readonly DependencyProperty AppStatusProperty =
        DependencyProperty.Register("AppStatus", typeof(string), typeof(MainWindow), new PropertyMetadata(null));

public void StatusBarUpdate(string strMainMessage)
    {
      Dispatcher.BeginInvoke((Action)(() => { AppStatus = strMainMessage; }));
    }

然后我可以随时调用StatusBarUpdate 方法,它会异步更新 UI。

【讨论】:

    【解决方案2】:

    你正在使用WPF,因此实施

    INotifyPropertyChanged 
    

    并使用适当的数据绑定。

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    public string StatusText
    {
        get {return this.m_statusText;}
    
        set
        {
            if (value != this.m_statusText)
            {
                this.m_statusText= value;
                NotifyPropertyChanged("StatusText");
            }
        }
    }
    

    在 XAML 中

    <TextBox Text="{Binding Path=StatusText}"/>
    

    【讨论】:

    • 感谢 Manuel - 这是我最初的设置,如果失败了,我将其更改为简单的 lblStatus.Text 以查看是否有任何不同。最初我有一个名为 AppStatus 的 INPC 字符串,TextBlock 就像您在上面所说的那样绑定到它,然后我简单地调用 AppStatus = "Opening Project";。不幸的是,结果和现在一样。
    • 好的,但是数据绑定是正确的方法。我会尝试让这种方法发挥作用。你设置了DataContext吗?您可以在 VisualStudio 中启用 WPF 绑定错误消息
    • 谢谢,我会告诉你我的进展情况!
    【解决方案3】:

    您还没有提到究竟是什么问题,但是查看代码我可以猜到文本框仅在您的代码完成后显示“正在打开项目...”,或者如果AppStatus 正在执行则显示“就绪”同样的事情。

    如果是,问题是你在 UI 线程上做所有事情。尽管您更改了文本框的文本,但它会在您的工作完成后呈现,因此存在问题。要解决此问题,您需要在工作线程上运行从 if (blnCopy == false)objDocumentTabs.SelectedIndex = 0; 的代码。这将解决您的问题。

    您可以为此使用 TPL,也可以使用 .ContinueWithTaskScheduler.FromCurrentSynchronizationContextTaskContinuationOptions.OnlyOnRanToCompletion 来执行 finally 块。

    编辑: 由于您没有使用 MVVM,因此您需要大量 Invoke 才能使您的代码正常工作。正如我所见,p_objNewPane 也是一个 UI 元素,就像 Project 是一个视图属性一样,很难将所有这些转换为 TPL。您可以保留 p_objNewPane = ... 的代码原样(即外部工作线程)。除了可以在另一个工作线程中运行的 InitializeCalculation 之外,这似乎不是很占用 CPU。

    更好的方法是使用 await/async 方法来等待所有繁重的方法。

    【讨论】:

    • 感谢 Yogesh - 是的,您是正确的,TextBlock 直到最后才更新。我的问题还在于“项目”是一个依赖属性,因此例如使用 BackgroundWorker 或 await Task.Run() 会导致异常,因为我无法在 UI 线程之外访问 propdp。
    • 不用直接赋值或修改依赖属性,只需新建一个Project实例,并将这个实例作为任务结果返回即可。在ContinueWith 方法中,只需将您的依赖属性分配给返回的Project 实例即可。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    相关资源
    最近更新 更多