Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change button color from other thread

In my c# program I create a bunch of buttons in the main (UI) thread. Then I start a timer that runs a method Timeline(). Now I want the appearance of the buttons to change when the Timeline() method runs. But I'm not allowed to access the objects because they are owned by the main thread. How do I solve this?

Error: The calling thread cannot access this object because a different thread owns it.

I have found Dispatcher disp = Dispatcher.CurrentDispatcher; which has an Invoke() method but I'm not sure if that is the right way, and I couldn't get it to work.

Here is part of my code:

    private static SurfaceButton[,] buttons = new SurfaceButton[dimensionBeats, dimensionsNotes];

    public SurfaceWindow1()
    {
        try
        {
            InitializeComponent();
        }
        catch (Exception ex)
        {
        }


        LoadAudioContent();
        // Add handlers for Application activation events
        AddActivationHandlers();


        InitializeButtons();

        try
        {
            t = new Timer(Timeline, null, 1, dimensionBeats*500);
        }
        catch (Exception e)
        {

        } 
    }

    private void InitializeButtons()
    {
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 6; j++)
            {
                SurfaceButton btn = new SurfaceButton();
                //btn.Content = files[j].Substring(0,1);
                btn.Name = "a" +i + j;
                btn.Width = 120;
                btn.Height = 120;
                btn.ContactEnter+=StartSound;
                btn.ContactLeave+=StopSound;
                btn.Opacity = 0.01;
                btn.ClickMode = ClickMode.Release;

                buttons[i,j] = btn;

                // add the newly generated button to the correct column (left to right one panel per column)
                switch (i)
                {
                    case 0:
                        rowPanel0.Children.Add(btn);
                        break;
                    case 1:
                        rowPanel1.Children.Add(btn);
                        break;
                    case 2:
                        rowPanel2.Children.Add(btn);
                        break;
                    case 3:
                        rowPanel3.Children.Add(btn);
                        break;
                    case 4:
                        rowPanel4.Children.Add(btn);
                        break;
                    case 5:
                        rowPanel5.Children.Add(btn);
                        break;
                    case 6:
                        rowPanel6.Children.Add(btn);
                        break;
                    case 7:
                        rowPanel7.Children.Add(btn);
                        break;
                }
            }
        }
    }


    private void Timeline(Object state)
    {     

        for (int i = 0; i < pressed.GetLength(0); i++)
        {
            sendRequest(url, postMethod, tabelId, buttsToString(butts));

            for (int j = 0; j < pressed.GetLength(1); j++)
            {
                if (pressed[i,j])
                {
                    buttons[i, j].Background = Brushes.Red;
                }
            }
            Thread.Sleep(500);
            for (int j = 0; j < pressed.GetLength(1); j++)
            {
                buttons[i, j].Background = Brushes.White;
            }
        }
like image 888
Niels Avatar asked May 20 '26 13:05

Niels


1 Answers

You need to use Invoke/BeginInvoke to forward updates to the GUI thread:

int i1 = i; // local copies to avoid closure bugs
int j1 = j;
buttons[i1, j1].Dispatcher.BeginInvoke(
    ((Action)(() => buttons[i1, j1].Background = Brushes.Red)));
like image 192
Tudor Avatar answered May 23 '26 02:05

Tudor