从Backgroundworker更改TextBlock.Inlines

时间:2016-03-04 06:52:49

标签: c# wpf backgroundworker textblock inlines

有没有办法从BackgroundWorker更改内联?

我尝试了以下内容:

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并更改了进度,将静态语法类更改为普通类..但没有任何效果,它始终是相同的错误。

我也尝试从Backgroundworker调用它...这意味着调用

    List<Run> runList = Syntax.Highlight(kvp.Value);

this.Dispatcher.Invoke((Action)(() =>
    {
        runList.ForEach(x => publicRunList.Add(x));
    }));

有人知道这个问题吗?

2 个答案:

答案 0 :(得分:1)

使用

<iron-ajax
auto
url='https://us3.api.mailchimp.com/3.0/lists/{{listid}}'
handle-as='json'
method='get'
headers='{"Authorization": "Basic [[basicAuth(username, apiKey)]]"}'
debounce-duration='300'
last-response='{{json}}'></iron-ajax>
<script>
    Polymer({
        ...
        basicAuth(username, password) {
            return window.btoa(username + ':' + password)
        },
        ...
    });
</script>

而不是

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

Gui元素只能从Gui线程访问。使用tb.Text = ""; kvp.Value.ForEach(x => tb.Inlines.Add(x)); 可确保调用的操作在其上运行。

您还在Dispatcher.Invoke中创建Run个对象。你还必须在Gui线程上创建Gui元素。所以你也应该在调度程序调用中包装这个调用:

Syntax.Highlight

这应该有效:

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的接口以返回带有字符串和突出显示颜色的元组列表,然后在Gui线程上创建Syntax.Highlight个对象。

修改

正如Gopichandar所指出的那样,使用Run异步执行给定的Action,这样就可以解决应用程序的冻结问题。它仍然需要几秒钟才能将所有元素添加到Gui中。

答案 1 :(得分:0)

在WPF中,只有UI元素所属的线程(即UI线程)才能与之通信。 BackgroundWorker的DoWork部分在不同的线程中执行,因此无法执行与UI相关的任何操作。对于Timers而言,同样的事情也适用于BackgroundWorkers。

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

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