0

I have a form that is going to pull data from an sql server, give the user the ability to select an action to apply, then send the data back to the server for processing. I have the DataGridView setup, however when testing the form, it throws this error when i click on the DataGridViewComboBoxCell to edit the action value.

enter image description here

However i Do have [STAThread] set on the main program in Program.cs. I even tried setting the ApartmentState manually to bypass the compiler hint, but it threw the error: "Cannot Set the ApartmentState. State Type is MTA".

Here is the code for main:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HLedger
{
    internal static class Program
    {
        //for the application handler
        public static ApplicationHandler applicationHandler = new ApplicationHandler(new TokenHandler(new List<TokenHandler.TokenJsonListHandler>()));
         
        
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static async Task Main()
        {
            //Thread.CurrentThread.SetApartmentState(ApartmentState.STA); //Attempted to set here but it failed.
            ApplicationHandler.hlAttemptGenerateWallets();
            
            applicationHandler.initializeTokenHandler();
            applicationHandler.getApplicationTokenHandler().setWalletAddressesByChainType();
            Application.EnableVisualStyles();
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new hlMainFrmWalletInfoPanel());
        }
    }
}

Here is the code for the panel/form:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HLedger
{

    internal class ConsensusAction
    {
#nullable enable
        private static readonly string[] hlConsensusResultValues =
        {
            "Consensus Passed",
            "Consensus Failed",
            "Consensus Failed With Veto",
            "Consensus Sent For Revision"
        };

        private long actionID { get; set; }
        private string action { get; set; }

        private string[]? descrription { get; set; }
        private double weightToPass { get; set; }
        private double lastWeightRecorded { get; set; }
        private DateTime actionDeadline { get; set; }
        private string actionUserResponse { get; set; }
        private string actionReasoning { get; set; }
        private string actionVoteResult { get; set; }

        public ConsensusAction(long actionID, string action, string[]? description, double weightToPass, double lastWeightRecorded, DateTime actionDeadline, string actionUserResponse, string actionReasoning, string actionVoteResult)
        {
            this.actionID=actionID;
            this.action=action;
            this.descrription=description;
            this.weightToPass=weightToPass;
            this.lastWeightRecorded=lastWeightRecorded;
            this.actionDeadline=actionDeadline;
            this.actionUserResponse=actionUserResponse;
            this.actionReasoning=actionReasoning;
            this.actionVoteResult=actionVoteResult;
        }

        public long GetActionID() => actionID;
        public string GetAction() => action;
        public string[]? GetDescription() => descrription ?? new string[]{"",""};
        public double GetWeightToPass() => weightToPass;
        public double GetLastWeightRecorded() => lastWeightRecorded;
        public DateTime GetActionDeadline() => actionDeadline;  
        public string GetActionUserResponse() => actionUserResponse;

        public string GetUSerActionReasoning() => actionReasoning;
        public string GetActionVoteResult() => actionVoteResult;
#nullable disable
    }

    internal class HXConsensus
    {
        private Panel cPanel { get; set; }

        private ConsensusAction[] consensusActions { get; set; }

        public static readonly string[] hlConsensusValues =
        {
            "Deny",
            "Deny With Veto",
            "Object",
            "Abstain",
            "Approve"
        };

        public HXConsensus()
        {
            cPanel = new Panel();
            GeneratePanel();
        }

        //capture the cell value changed event to push data to server. 
        private async void dgv_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            DataGridViewCellEventArgs dgvcearg = e as DataGridViewCellEventArgs;
            DataGridViewColumn dgvc = ((DataGridView)cPanel.Controls["ConesnsusGrid"]).Columns[((DataGridView)cPanel.Controls["ConesnsusGrid"]).CurrentCell.ColumnIndex];
            if (dgvc is DataGridViewComboBoxColumn)
            {
                ConsensusAction ca = (ConsensusAction)consensusActions.Where(x => x.GetActionID() ==
                long.Parse(((DataGridView)cPanel.Controls["ConsensusGrid"]).Rows[dgvcearg.RowIndex].Cells[0].Value.ToString()));

                await Task.Run(() => Networking.hlAttemptUpdateConsensusQuestionWithAnswerAsyncDebug(ca));
            }
        }

        //debug function to create temp actions. remove if good
        private void hlSetTempQuestionsDebug()
        {
            consensusActions = new ConsensusAction[]
            { new ConsensusAction(1, "Test Action 1", new string[]{"","" }, 100, 0, DateTime.Now.AddDays(1),"", "","Open"),
              new ConsensusAction(2, "Test Action 2", new string[]{"","" }, 100, 0, DateTime.Now.AddDays(1),"", "","Open"),
              new ConsensusAction(3, "Test Action 3", new string[]{"","" }, 100, 0, DateTime.Now.AddDays(1),"", "","Open")};
        }

        private bool GeneratePanel()
        {
            try
            {
                //create data grid view for ConsensusWorks
                cPanel = new Panel();
                cPanel.Name = "Consensus";
                cPanel.Size = new Size(754, 833);
                cPanel.Location = new Point(253, 6);
                DataGridView dgv = new DataGridView();
                dgv.Size = new Size(850, 690);
                dgv.RowHeadersVisible = false;
                dgv.AllowUserToAddRows = false;
                dgv.AllowUserToDeleteRows = false;
                dgv.RowTemplate.MinimumHeight = 50;
                dgv.CellBorderStyle = DataGridViewCellBorderStyle.None;
                dgv.AllowUserToResizeRows = false;
                dgv.Name = "ConsensusGrid";

                dgv.CurrentCellDirtyStateChanged += dgv_CurrentCellDirtyStateChanged;

                //ConsensusID
                DataGridViewTextBoxColumn dgvccid = new DataGridViewTextBoxColumn();
                dgvccid.Name="ConsensusID";
                dgvccid.ReadOnly= true;
                dgvccid.AutoSizeMode=DataGridViewAutoSizeColumnMode.AllCells;
                dgvccid.HeaderText="Consensus ID";

                //ConsensusDeadline
                DataGridViewTextBoxColumn dgvccdl = new DataGridViewTextBoxColumn();
                dgvccdl.Name="ConsensusDeadline";
                dgvccdl.ReadOnly= true;
                dgvccdl.AutoSizeMode=DataGridViewAutoSizeColumnMode.AllCells;
                dgvccdl.HeaderText="Deadline";

                //consensus Action
                DataGridViewTextBoxColumn dgvcca = new DataGridViewTextBoxColumn();
                dgvcca.Name="ConsensusAction";
                dgvcca.ReadOnly= true;
                dgvcca.AutoSizeMode=DataGridViewAutoSizeColumnMode.AllCells;
                dgvcca.HeaderText="Consensus Action";

                //consensus vote object
                DataGridViewComboBoxColumn dgvccvo = new DataGridViewComboBoxColumn();
                dgvccvo.Name = "ConsensusVoteObject";
                dgvccvo.DataSource=hlConsensusValues;
                dgvccvo.HeaderText="Taken Action";
                dgvcca.AutoSizeMode=DataGridViewAutoSizeColumnMode.AllCells;

                //consensus reasoning object
                DataGridViewTextBoxColumn dgvccro= new DataGridViewTextBoxColumn();
                dgvccro.Name = "ConsensusReasoningObject";
                dgvccro.HeaderText = "Reasoning";
                dgvcca.AutoSizeMode=DataGridViewAutoSizeColumnMode.AllCells;

                //consensus results object
                DataGridViewTextBoxColumn dgvccrs= new DataGridViewTextBoxColumn();
                dgvccrs.Name="ConsensusResultsObject";
                dgvccrs.ReadOnly= true;
                dgvccrs.HeaderText="Result";
                dgvccrs.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                //add to view
                dgv.Columns.Add(dgvccid);
                dgv.Columns.Add(dgvccdl);
                dgv.Columns.Add(dgvcca);
                dgv.Columns.Add(dgvccvo);
                dgv.Columns.Add(dgvccro);
                dgv.Columns.Add(dgvccrs);

                //run if good
                if(SystemController.hlVerifyComputerThreadingSpecs())
                {
                    hlSetTempQuestionsDebug();
                    //Running as a task failed, so attempted using base function. still failed.
                    //Task.Run(() => Networking.hlattemptUpdateConsensusQuestionsAsyncDebug(dgv, consensusActions));
                    Networking.hlattemptUpdateConsensusQuestionsAsyncDebug(dgv, consensusActions);
                }
                cPanel.Controls.Add(dgv);
            }
            catch(Exception ex)
            {
                Environment.Exit(-1);
            }
            return false;
        }

        public Panel GetConsensusPanel() => cPanel;
    }
}

Relevant Functions From Networking.cs:

//debug function. update to correect function later

        public static async void hlAttemptUpdateConsensusQuestionWithAnswerAsyncDebug(ConsensusAction ca)
        {
            try
            {
                if (SystemController.hlVerifyComputerThreadingSpecs())
                {
                    //implement later
                }
                else throw new Exception();
            }catch(Exception ex)
            {
                Environment.Exit(-1);
            }
            
        }

        //debug function. update to correct functrion later
        public static async void hlattemptUpdateConsensusQuestionsAsyncDebug(DataGridView dgv, ConsensusAction[] actions)
        {
            try
            {
                foreach (ConsensusAction action in actions)
                {
                    DataGridViewRow dgvr = new DataGridViewRow();

                    DataGridViewTextBoxCell dgvrcid = new DataGridViewTextBoxCell();
                    dgvrcid.Value = action.GetActionID();
                    DataGridViewTextBoxCell dgvrcdl = new DataGridViewTextBoxCell();
                    dgvrcdl.Value = action.GetActionDeadline().ToShortDateString();
                    DataGridViewTextBoxCell dgvrca = new DataGridViewTextBoxCell();
                    dgvrca.Value = action.GetAction();
                    DataGridViewComboBoxCell dgvrcvo = new DataGridViewComboBoxCell();
                    dgvrcvo.DataSource = HXConsensus.hlConsensusValues;
                    DataGridViewTextBoxCell dgvrcro = new DataGridViewTextBoxCell();
                    DataGridViewTextBoxCell dgvrcrs = new DataGridViewTextBoxCell();
                    dgvrcrs.Value = action.GetActionVoteResult();
                    dgvr.Cells.Add(dgvrcid);
                    dgvr.Cells.Add(dgvrcdl);
                    dgvr.Cells.Add(dgvrca);
                    dgvr.Cells.Add(dgvrcvo);
                    dgvr.Cells.Add(dgvrcro);
                    dgvr.Cells.Add(dgvrcrs);
                    if(!action.GetActionVoteResult().Contains("Open"))
                    {
                        dgvrcvo.Value = action.GetActionUserResponse();
                        dgvrcro.Value = action.GetUSerActionReasoning();
                        dgvrcvo.ReadOnly= true;
                        dgvrcro.ReadOnly=true;
                    }
                    dgv.Rows.Add(dgvr);
                }
            }
            catch(Exception ex) {

                Environment.Exit(-1);
            }
        }

Function From SystemController (encase needed):

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace HLedger
{
    internal class SystemController
    {
        private static readonly int HL_MAX_KNOWN_NEEDED_THREADS = 6;
        
        public static bool hlVerifyComputerThreadingSpecs()
        {
            if (((Environment.ProcessorCount*2)+2) < HL_MAX_KNOWN_NEEDED_THREADS)
            {
                MessageBox.Show("Your system does not have enough processor threads avaialable to run all HyperLedger Services. Some functions may not work.",
                    "HyperLedger Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return (System.Diagnostics.Process.GetCurrentProcess().Threads.Count +1) >= Environment.ProcessorCount*2;

            }
            else return true;
        }
    }
}

I tried doing some research, but cannot seem to figure out how to fix this error. Any ide as to what is going on? thank you.

  • 1
    You're using `Task.Run()` to execute `hlattemptUpdateConsensusQuestionsAsyncDebug()`, then try and set properties of a Control from this Task, which, BTW, is marked as async but has nothing async in it and nothing is awaited there. A Task cannot be STA, but you shouldn't access Controls from a STA Thread other than the UI Thread either – Jimi Jan 14 '23 at 13:31
  • There will be code in that function to call to an SQL server to get information, it is just not written yet, as i wanted to see if the form itself generated and is working properly. This is why there is default questions and such to test the form. the function will need to be Asynchronous for that purpose. – Axyl Xaleuthe Jan 14 '23 at 13:38
  • When that method will be actually asynchronous and you'll be awaiting that method in the `CurrentCellDirtyStateChanged` handler, then you'll always resume in the UI Thread, so the problem won't come up again (like this, at least, unless you still try to `Task.Run()` it). You're still going to have a design problem, depending on how much time the code currently in that method takes to execute: you're in an async event handler... – Jimi Jan 14 '23 at 13:45
  • To access a Winforms object (Control, Form, etc.) from a thread that is not the UI thread, use Control.BeginInvoke https://stackoverflow.com/questions/4936459/dispatcher-begininvoke-cannot-convert-lambda-to-system-delegate – Simon Mourier Jan 14 '23 at 14:22
  • Please post the minimal amount of code that reproduces the problem. – Stephen Cleary Jan 14 '23 at 18:31

0 Answers0