2016-03-04 7 views
0

Есть ли способ изменить встроенные строки с BackgroundWorker?Изменить TextBlock.Inlines from 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)); 
    })); 

Кто-нибудь знает эту проблему?

+0

Где именно вы получите эту ошибку? – Domysee

+0

RunWorkerCompleted, где я хочу добавить встроенные строки –

+0

Вы пробовали синхронизацию? – Karolis

ответ

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 элементы могут быть доступны только из потока Гуй. Использование Dispatcher.Invoke гарантирует, что на него будет запущено вызванное действие.

Вы также создаете Run объектов в Syntax.Highlight. Вы также должны создавать элементы Gui в потоке Gui. Таким образом, вы должны также обернуть этот вызов в диспетчерскую Invoke:

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, чтобы вернуть список кортежей со строкой и цветом выделения, а затем создать объекты Run в потоке Gui.

Edit:

Как отмечалось Gopichandar, используя BeginInvoke выполняет данное действие асинхронно, так что бы решить замораживание применения. Это все равно займет пару секунд, пока все элементы не будут добавлены в Gui.

+0

то же сообщение об ошибке –

+0

@ThomasKlammer, пожалуйста, проверьте последние изменения, это должно устранить проблему – Domysee

+0

, так что для чего нужен фоновой рабочий? Syntax.Highlight занимает некоторое время для 2000 записей в DataGrid. С помощью этого метода мой UI Freezes –

0

В WPF может взаимодействовать только поток, к которому принадлежит элемент пользовательского интерфейса (то есть поток пользовательского интерфейса). Часть DoWork BackgroundWorker выполняется в другом потоке и, следовательно, не может делать ничего связанного с UI. То же самое относится к таймерам вместо BackgroundWorkers.

Но если вы создаете BackgroundWorker с помощью var worker = new BackgroundWorker {WorkerReportsProgress = true};, то вы можете установить обработчик событий для ProgressChanged. Внутри вашего _DoWork() вы можете сказать: (sender as BackgroundWorker).ReportProgress, который вызывается в обработчике событий ProgressChanged в исходном потоке, где вы можете манипулировать элементами пользовательского интерфейса.

Полный пример: http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/