Search code examples
c#winformsthread-safetytrackbar

Threadsafe 'get' from form slider (TrackBar)


I have an application with an initial form that has various configurations (checkboxes, text boxes, sliders/trackbars). When I initiate the action that uses those configurations, there's a worker process that's called:

private void btnx_Click(object sender, EventArgs e)
{
  bgWorkerX.RunWorkerAsync();
}

private void bgWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
    Dictionary<string, object> dictCfgOut = GenerateConfigDict();
    XGen.CreateX.aReadFile(inputPathXRAW.Text,  outputPathX.Text, dictCfgOut);
}

The GenerateConfigDict function creates a Dictionary<string, object> of config data. This is because the data needed varies (a few bools, some ints, an array of strings). The code where this is created is below:

    private Dictionary<string, object> GenerateConfigDict()
    {
        Dictionary<string, object> ConfigDict = new Dictionary<string, object>();

        if (cbXAddOnus.Checked)
        {
            if (!MiscTools.ValidateX(txtInstX.Text, true))
                return null;
        }

        ConfigDict.Add("Available Images", GenerateConfigImageArray());
        ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
        ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
        ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
        ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
        ConfigDict.Add("X Somedata", (string)txtX.Text);
        ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
        ConfigDict.Add("X % Y", (int)sliderX.Value);

        return ConfigDict;
    }

The sliderX.Value part was just added today. Prior to this everything worked great. However, with only that bit, I get:

Cross-thread operation not valid: Control 'sliderX' accessed from a thread other than the thread it was created on.

First, why is reading that value not threadsafe, but reading checkboxes and text is? Second, I've poked around regarding making that threadsafe, but it all seems to require another function to call that value asynchronously. I've already taken measures to ensure that the slider is disabled while the worker is running, so changes cannot be made from the form.

Thanks!


Solution

  • To access any control thread-safely you should call Invoke() method of that control (MSDN tutorial).

    So, you piece of code will look like this:

    delegate int GetSliderValueCallback();
    
    private int GetValue()
    {
        int sliderValue;
        if (sliderX.InvokeRequired)
        {
            GetSliderValueCallback cb = new GetSliderValueCallback(GetValue);
            return (int)sliderX.Invoke(cb);
        }
        else
        {
            return (int)sliderX.Value;
        }
    }
    

    And call it when reading values:

    ConfigDict.Add("X % Y", GetValue());
    

    I also would recommend you to use async methods.

    private async void btnx_Click(object sender, EventArgs e)
    {
      await bgWorkerX_DoWorkAsync();
    }
    
    private async void bgWorkerX_DoWorkAsync()
    {
        Dictionary<string, object> dictCfgOut = await GenerateConfigDictAsync();
        XGen.CreateX.aReadFile(inputPathXRAW.Text,  outputPathX.Text, dictCfgOut);
    }
    
    private Task<Dictionary<string, object>> GenerateConfigDictAsync()
        {
            return Task.Run(() => {
            Dictionary<string, object> ConfigDict = new Dictionary<string, object>();
    
            if (cbXAddOnus.Checked)
            {
                if (!MiscTools.ValidateX(txtInstX.Text, true))
                    return null;
            }
    
            ConfigDict.Add("Available Images", GenerateConfigImageArray());
            ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
            ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
            ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
            ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
            ConfigDict.Add("X Somedata", (string)txtX.Text);
            ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
            ConfigDict.Add("X % Y", (int)sliderX.Value);
    
            return ConfigDict;
        });
    }
    

    WARNING! Unfortunately I do not have chance to build that code (I've wrote it just here), so, please use it just as example.