Good day. I have an application on WPF. On MainWindow there is a canvas, and a function that handles all controls in it. The function works in the main thread, otherwise you will not get objects from the canvas. I want to do any visualization of the process on this screen, for example changing the textbox. How to do it better?

public void SomeLongFunk() { foreach(var item in canvas.Children) { //dosomething } } 

    2 answers 2

    In order for a function running in a stream to "reach" UI objects, you need to use anonymous functions:

     this.Invoke((MethodInvoker) delegate () { textbox.Text = "Almost done!"; }); 
    • Please note: the function works in the main thread! - Pavel Mayorov

    If your function is long and works with UI objects, you are doing wrong. It is necessary to unload long-term work into other streams in order not to block the main stream. So that your code can still interact with the UI, you can use either async / await , or Dispatcher.InvokeAsync , or IProgress .

    Examples:

    async / await :

     async Task MoveObjects() { // собираем данные в UI-потоке var currentPositions = Arena.Children.Select(child => new Point(Canvas.GetX(child), Canvas.GetY(child))).ToList(); for (int i = 0; i < 100; i++) { // вычисляем новые данные в новом потоке var newPositions = await Task.Run(() => engine.ComputeNextPositions(currentPositions)); // присваиваем данные UI-объектам в UI-потоке for (int i = 0; i < Arena.Children.Count; i++) { var child = Arena.Children[i]; var coord = newPositions[i]; Canvas.SetX(child, coord.X); Canvas.SetY(child, coord.Y); } currentPositions = newPositions; await Task.Delay(25); } } 

    Dispatcher.InvokeAsync :

     Task MoveObjects() { var objects = Arena.Children.ToList(); return Task.Run(() => MoveObjectsInBackground(objects, Dispatcher)); } async Task MoveObjectsInBackground(List<UIElement> objects, Dispatcher dispatcher) { // собираем данные в UI-потоке var currentPositions = await dispatcher.InvokeAsync(() => objects.Select(child => new Point(Canvas.GetX(child), Canvas.GetY(child))).ToList()); for (int i = 0; i < 100; i++) { // вычисляем новые данные var newPositions = ComputeNewPositions(currentPositions); // отправляем их в UI-поток await dispatcher.InvokeAsync(() => { for (int i = 0; i < objects.Count; i++) { var child = objects[i]; var coord = newPositions[i]; Canvas.SetX(child, coord.X); Canvas.SetY(child, coord.Y); } }); await Task.Delay(25); } } 

    IProgress :

     Task MoveObjects() { // собираем данные в UI-потоке var positions = Arena.Children.Select(child => new Point(Canvas.GetX(child), Canvas.GetY(child))).ToList(); var progress = new Progress<List<Point>>(UpdatePositions); await Task.Run(() => MoveObjectsInBackground(positions, progress); } void UpdatePositions(List<Point> positions) { for (int i = 0; i < Arena.Children.Count; i++) { var child = Arena.Children[i]; var coord = newPositions[i]; Canvas.SetX(child, coord.X); Canvas.SetY(child, coord.Y); } } void MoveObjectsInBackground(List<Point> positions, IProgress<List<Point>> progress) { for (int i = 0; i < 100; i++) { var newPositions = ComputeNewPositions(currentPositions); progress.Report(newPositions); currentPositions = newPositions; Thread.Sleep(25); } } 
    • "The function works in the main thread, otherwise you will not get objects from the canvas " - what kind of day is it? - Pavel Mayorov
    • @PavelMayorov: The suggestion is to unload the function into the background thread. The long function has nothing to do in the UI thread. - VladD