简体   繁体   English

从Backgroundworker更改TextBlock.Inlines

[英]Change TextBlock.Inlines from Backgroundworker

Is there any way to change the inlines from a BackgroundWorker? 有什么方法可以从BackgroundWorker更改内联吗?

I tried the following: 我尝试了以下方法:

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));
        }
    }

And the syntax class: 和语法类:

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;
            }
        }
    }
}

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

I tried many different things: return the runList with progress changed, changed the static syntax class to a normal class.. but nothing worked, its always the same error. 我尝试了许多不同的操作:返回具有更改进度的runList,将静态语法类更改为普通类..但没有任何效果,它始终是相同的错误。

I also tried to invoke it from the Backgroundworker.. that means call 我也尝试从Backgroundworker调用它。

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

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

Anybody knows the problem? 有人知道这个问题吗?

Use 采用

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

instead of 代替

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

Gui elements can only be accessed from the Gui thread. Gui元素只能从Gui线程访问。 Using Dispatcher.Invoke ensures that the invoked action runs on it. 使用Dispatcher.Invoke确保调用的操作在其上运行。

You are also creating Run objects in Syntax.Highlight . 您还在Syntax.Highlight创建Run对象。 You also have to create Gui elements on the Gui thread. 您还必须在Gui线程上创建Gui元素。 So you should also wrap this call in a dispatcher invoke: 因此,您还应该将此调用包装在调度程序调用中:

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

This should work: 这应该工作:

//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));
}));

This probably defeats the purpose of why you wanted to use a BackgroundWorker in the first place. 这可能违反了为什么您首先要使用BackgroundWorker的目的。 I'd suggest to change the interface of Syntax.Highlight to return a list of tuples with the string and the highlight color instead, and then create the Run objects on the Gui thread. 我建议更改Syntax.Highlight的接口,以返回包含字符串和突出显示颜色的元组列表,然后在Gui线程上创建Run对象。

Edit : 编辑

As Gopichandar noted, using BeginInvoke executes the given Action asynchronously, so that would solve the freezing of the application. 正如Gopichandar指出的那样,使用BeginInvoke异步执行给定的Action,这样可以解决应用程序的冻结问题。 It would still take a couple of seconds until all elements are added to the Gui though. 不过,将所有元素添加到Gui仍然需要几秒钟。

In WPF, only the thread that the UI element belongs to (ie the UI thread) can communicate with it. 在WPF中,只有UI元素所属的线程(即UI线程)才能与其通信。 The DoWork part of the BackgroundWorker is executed in a different thread and thus cannot do anything UI-related. BackgroundWorker的DoWork部分是在不同的线程中执行的,因此无法执行与UI相关的任何操作。 The same thing goes for Timers instead of BackgroundWorkers. 对于Timers而不是BackgroundWorkers同样适用。

But if you create the BackgroundWorker with var worker = new BackgroundWorker {WorkerReportsProgress = true}; 但是,如果您使用var worker = new BackgroundWorker {WorkerReportsProgress = true};创建了背景var worker = new BackgroundWorker {WorkerReportsProgress = true}; then you can set an event handler for ProgressChanged . 然后可以为ProgressChanged设置事件处理程序。 Inside your _DoWork() , you can then say: (sender as BackgroundWorker).ReportProgress , which will call your ProgressChanged event handler in the original thread, where you can manipulate the UI elements. _DoWork() ,您可以说:( (sender as BackgroundWorker).ReportProgress ,它将在原始线程中调用ProgressChanged事件处理程序,您可以在其中操纵UI元素。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM