0

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

誰でも問題を知っていますか?

4

2 に答える 2

1

使用する

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すると、呼び出されたアクションが確実に実行されます。

Runでオブジェクトも作成していますSyntax.Highlight。また、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));
}));

これはおそらく、最初に a を使用したかった理由の目的を無効にしますBackgroundWorkerSyntax.Highlight代わりに文字列とハイライト色を含むタプルのリストを返すようにインターフェイスを変更Runし、Gui スレッドでオブジェクトを作成することをお勧めします。

編集

Gopichandar が指摘したように、 usingは指定さBeginInvokeれた Action を非同期的に実行するため、アプリケーションのフリーズは解決します。ただし、すべての要素が Gui に追加されるまで、まだ数秒かかります。

于 2016-03-04T06:58:25.647 に答える
0

WPF では、UI 要素が属するスレッド (つまり、UI スレッド) のみがそれと通信できます。BackgroundWorker の DoWork 部分は別のスレッドで実行されるため、UI 関連は何も実行できません。同じことは、BackgroundWorkers の代わりに Timers にも当てはまります。

ただし、BackgroundWorker を作成するとvar worker = new BackgroundWorker {WorkerReportsProgress = true};、のイベント ハンドラーを設定できますProgressChanged。内で、次の_DoWork()ように言うことができます。これにより、UI 要素を操作できる元のスレッドでイベント ハンドラー(sender as BackgroundWorker).ReportProgressが呼び出されます。ProgressChanged

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

于 2016-03-04T08:51:37.943 に答える