Change TextBlock.Inlines from Backgroundworker

本文关键字:Backgroundworker from Inlines TextBlock Change | 更新日期: 2023-09-27 18:37:15

有没有办法从后台工作者更改内联?

我尝试了以下方法:

private void test()
    {
        var rows = GetDataGridRows(dgVarConfig);
        foreach (DataGridRow r in rows)
        {
            TextBlock tb = cMatchEx.GetCellContent(r) as TextBlock;
            if (!syntaxWorker.IsBusy)
                syntaxWorker.RunWorkerAsync(new KeyValuePair<TextBlock, String>(tb, tb.Text));
        }
    }
    private void syntaxWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        if (e.Argument == null)
            Thread.Sleep(100);
        else
        {
            KeyValuePair<TextBlock, String> kvp = (KeyValuePair<TextBlock, String>)e.Argument;
            e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
        }
    }

    private void syntaxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Result != null)
        {
            KeyValuePair<TextBlock, List<Run>> kvp = (KeyValuePair<TextBlock, List<Run>>)e.Result;
            TextBlock tb = kvp.Key;
            tb.Text = "";
            kvp.Value.ForEach(x => tb.Inlines.Add(x));
        }
    }

和语法类:

public static class Syntax
{
    static Regex subFormula = new Regex(@"'w+'(')");
    static Regex sapFormula = new Regex(@"'w+'(([^)]+)')");
    static Regex strings = new Regex(@"''[^']+''");
    static Regex numerals = new Regex(@"'b[0-9'.]+'b");
    static Regex characteristic = new Regex(@"(?:)?'w+(?:)?");
    static Regex andOr = new Regex(@"( and )|( AND )|( or )|( OR )");
    static Regex not = new Regex(@"(not )|(NOT )");
    private static Brush[] colorArray;
    public static List<Run> Highlight(String input)
    {

        colorArray = new Brush[input.Length];
        for (int i = 0; i < input.Length; i++)
            colorArray[i] = Brushes.Black;
        //Reihenfolge beibehalten!!
        assignColor(Brushes.Blue, characteristic.Matches(input));
        assignColor(Brushes.Black, andOr.Matches(input));
        assignColor(Brushes.Black, numerals.Matches(input));
        assignColor(Brushes.Orange, strings.Matches(input));
        assignColor(Brushes.DeepPink, subFormula.Matches(input));
        assignColor(Brushes.Green, sapFormula.Matches(input));
        assignColor(Brushes.Green, not.Matches(input));

        int index = 0;
        List<Run> runList = new List<Run>();
        foreach (Char character in input)
        {
            runList.Add(new Run(character.ToString()) { Foreground = colorArray[index] });
            index++;
        }

        colorArray = null;
        return runList;
    }
    public static void Check(TextBlock textBlock)
    {
    }

    private static void assignColor(Brush brush, MatchCollection matchCollection)
    {
        foreach (Match match in matchCollection)
        {
            int start = match.Index;
            int end = start + match.Length;
            for (int i = start; i < end; i++)
            {
                colorArray[i] = brush;
            }
        }
    }
}

我总是收到此错误:The calling thread cannot access this object because a different thread owns it.

我尝试了许多不同的方法:返回进度更改的 runList,将静态语法类更改为普通类......但没有任何效果,它总是相同的错误。

我还尝试从后台工作者调用它..这意味着调用

    List<Run> runList = Syntax.Highlight(kvp.Value);
this.Dispatcher.Invoke((Action)(() =>
    {
        runList.ForEach(x => publicRunList.Add(x));
    }));

有人知道问题吗?

Change TextBlock.Inlines from Backgroundworker

使用

tb.Dispatcher.Invoke(() => {
    tb.Text = "";
    kvp.Value.ForEach(x => tb.Inlines.Add(x));
});

而不是

tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));

Gui 元素只能从 Gui 线程访问。使用 Dispatcher.Invoke 可确保调用的操作在其上运行。

您还将在 Syntax.Highlight 中创建Run对象。您还必须在 Gui 线程上创建 Gui 元素。因此,您还应该将此调用包装在调度程序调用中:

e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));

这应该有效:

//this runs synchronously
kvp.Key.Dispatcher.Invoke(() => {
    e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
});
//this runs asynchronously
kvp.Key.Dispatcher.BeginInvoke((Action)(() => {
    e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
}));

这可能违背了您最初想要使用BackgroundWorker的目的。我建议更改 Syntax.Highlight 的接口以返回带有字符串和突出显示颜色的元组列表,然后在 Gui 线程上创建Run对象。

编辑

正如Gopichandar所指出的,使用BeginInvoke异步执行给定的操作,因此可以解决应用程序的冻结问题。不过,在将所有元素添加到 Gui 之前,仍然需要几秒钟。

在 WPF 中,只有 UI 元素所属的线程(即 UI 线程)可以与其通信。BackgroundWorker 的 DoWork 部分在不同的线程中执行,因此无法执行任何与 UI 相关的操作。同样的事情也适用于计时器而不是BackgroundWorkers。

但是,如果使用var worker = new BackgroundWorker {WorkerReportsProgress = true};创建后台工作者,则可以为ProgressChanged设置事件处理程序。在 _DoWork() 中,您可以说: (sender as BackgroundWorker).ReportProgress ,这将在原始线程中调用您的 ProgressChanged 事件处理程序,您可以在其中操作 UI 元素。

完整示例:http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/

相关文章:
  • 没有找到相关文章