-1

I had a standard foreach loop which I later turned into a Parallel.Foreach(). However in my loop I have areas where I access the UI elements and get and set the UI elements info.

So when I run it I get an error that I cannot access the element as another thread has access to it. There are multiple elements I need to access and the x:Name are stored in the list.

How do I get past this?

Parallel.ForEach(calculatedTestVariables, variable =>
        {
            string idName = "id_" + variable.id;
            var textBox = this.FindName(idName) as TextBox; //need the text from this TextBox

            //some calculations
            int x = 1 + 2 + 3 + 4

            textBox.Text = x.toString();

        });
Kristofer
  • 809
  • 9
  • 24
  • 2
    You should use data binding and probably data templates to avoid such nasty code. – BionicCode Sep 29 '21 at 22:40
  • Controls have thread affinity: you can only access them from the owning thread. For this reason it is pointless to do the UI work on a background thread. Only move the heavy calculations to the background thread and then return to the UI thread to update your UI with the result. – BionicCode Sep 30 '21 at 09:23

1 Answers1

0

To do this, you need data binding. You may follow such a way,

First create your view model and update your properties:

public class MainViewModel : BindableBase
{
    private string m_TextProgress;
    public string TextProgress
    {
        get { return m_TextProgress; }
        set { SetProperty(ref m_TextProgress, value); }
    }

    public void Run()
    {
        List<Variable> calculatedTestVariables = new List<Variable>();

        for (int i = 0; i < 5000; i++)
        {
            Variable item = new Variable();

            item.id = i;
            calculatedTestVariables.Add(item);
        }

        Parallel.ForEach(calculatedTestVariables, variable =>
        {
            string idName = "id_" + variable.id;

            //some calculations
            int x = 1 + 2 + 3 + 4;

            TextProgress = "" + variable.id + x;
        });
    }
}

Set your DataContext

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>

<Grid>

    <StackPanel>
        <TextBlock
            Width="120"
            Height="30"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Text="{Binding TextProgress, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />

        <Button
            Width="120"
            Height="30"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Click="Button_Click"
            Content="Calculate" />
    </StackPanel>
</Grid>

and call your Run method from GUI side

enter image description here

Ugur
  • 1,257
  • 2
  • 20
  • 30
  • I don't think you need data binding for the code in the example. – Andy Sep 30 '21 at 13:24
  • @Andy, how it would be then? – Ugur Oct 01 '21 at 07:39
  • A task or method can take a parameter and return a result. You can pass in anything you need from ui and return anything you need to set ui properties once the processing is complete, back on the ui thread. I would likely do this whilst working mvvm with viewmodels and I also recommend mvvm. But i don't think you absolutely must use binding. – Andy Oct 01 '21 at 10:16
  • Yes, binding is not a must in that case. He should be writing at least an async method. – Ugur Oct 01 '21 at 10:50