0

So i have created an event, whenever the property ActualVoltage changed, it will update in Form1 but it doesnt. Property ActualVoltage change, when i send a set-voltage command to the machine, then it will send back a number and i assign that number to AcutalVoltage. pls help me, pls show me where is my mistake and explain it for me like i am a 5 years old kid.Here is my event code:

        public delegate void ValueChange();
        public event ValueChange Change;
        public double ActualVoltage
        {
            get { return actualVoltage; }
            set
            {
                if (actualVoltage == value) return;
                else
                {
                    actualVoltage = value;
                    OnValueChange();
                }

            }

        }

        private void OnValueChange()
        {
            Change?.Invoke();
        }

in Form1:

        private void Form1_Load(object sender, EventArgs e)
        {

            ps.Change += ps_change;


        }
          private void ps_change()
        {
            lblValueActualVoltage.Text = ps.ActualVoltage.ToString();
            lblValueActualCurrent.Text = ps.ActualCurrent.ToString();
            lblHexCur.Text = ps.HexActualCurrent1;
            lblHexVol.Text = ps.HexActualVoltage1;
        }

updated: in class PS2000B

        public void GetDeviceStatusInformation(byte[] rawData)
    {
        remoteMode = ((byte)(rawData[0] & 0b1)).ToString();
        outputMode = ((byte)(rawData[1] & 0b1)).ToString();
        List<byte> temp = new List<byte>();
        foreach (var v in rawData)
            temp.Add(v);
        byte[] vontageBytes = temp.GetRange(2, 2).ToArray();
        HexActualVoltage = BitConverter.ToString(vontageBytes);
        Array.Reverse(vontageBytes);
        byte[] currentBytes = temp.GetRange(4, 2).ToArray();
        HexActualCurrent = BitConverter.ToString(currentBytes);
        Array.Reverse(currentBytes);
        var a = (BitConverter.ToInt16(vontageBytes, 0));
        ActualVoltage =Math.Round( BitConverter.ToInt16(vontageBytes, 0) * nominalVoltage / 25600.0,2);
        ActualCurrent = BitConverter.ToInt16(currentBytes, 0) * nominalCurrent / 25600.0;

    }

 public void RunTest(string safeFileName,string save)
    {
        Stopwatch st = new Stopwatch();
        List<string> timeMeasure = new List<string>();
        List<string> CurrentResults = new List<string>();
        List<int> Time = new List<int>();
        List<string> Voltage = new List<string>();

        FileStream file = new FileStream(safeFileName, FileMode.Open, FileAccess.Read);
        StreamReader reader = new StreamReader(file);
        string strRead = reader.ReadLine();
        while (strRead != null)
        {
            string[] temp = strRead.Split(';');
            Voltage.Add(temp[0]);
            Time.Add(int.Parse(temp[1]));

            strRead = reader.ReadLine();
        }
        reader.Close();
        file.Close();
        int n = 0;
        st.Start();
        for (int i = 0; i < Voltage.Count(); i++)
        {
            SetVoltage(Voltage[i]);
            for (int j = 0; j < Time[i]/300; j++)
            {
                UpdateStatusInfomationAndActualValue();
                CurrentResults.Add(Voltage[i]+";"+0.3*n+";"+ActualCurrent.ToString()+";"+ HexActualCurrent);
                n++;
            }
        }
        st.Stop();
        FileStream wfile = new FileStream(save +"\\results.txt", FileMode.Create, FileAccess.Write);
        StreamWriter writer = new StreamWriter(wfile);
        writer.WriteLine("VOlTAGE;TIME;CURRENT");
        foreach (var v in CurrentResults)
            writer.WriteLine(v);
        writer.WriteLine("TOTAL TIME: "+st.Elapsed);
        writer.Close();
        wfile.Close();

    }

  public void SetVoltage(string vol)
    {
        vol = vol.Replace('.', ',');
        ToPowerSupply ToPowerSupply = new ToPowerSupply();
        var b = Convert.ToInt16(Single.Parse(vol) * 25600 / nominalVoltage);
        var input = BitConverter.GetBytes(b);
        Array.Reverse(input);
        var temp = ToPowerSupply.SendCommand(0b11, ObjectList.SET_U, input, 2);
        ComPort.Write(temp, 0, temp.Count());
        Thread.Sleep(150);
        int bytes = ComPort.BytesToRead;
        byte[] rawData = new byte[bytes];
        ComPort.Read(rawData, 0, bytes);



    }
Tùng Bill
  • 129
  • 1
  • 3
  • 12

3 Answers3

1

I finally figure out the reason. I use Thread.Sleep() on main thread. At the time ActualVoltage changes, the main thread is sleeping, thats why the GUI does not update. To handle this, i use BackGroudWorker and everything is fine now.

Tùng Bill
  • 129
  • 1
  • 3
  • 12
0

Following Form1.cs handles events properly:

public delegate void ValueChange();
public event ValueChange Change;
private double actualVoltage;
public double ActualVoltage
{
    get { return actualVoltage; }
    set
    {
        if (actualVoltage == value) return;
        else
        {
            actualVoltage = value;
            OnValueChange();
        }

    }

}

private void ps_change()
{
    //UI updates here
}

private void OnValueChange()
{
    Change?.Invoke();
}

private void Form1_Load(object sender, EventArgs e)
{
    Change += ps_change;
}

Please note: If your property setters are invoked in non-GUI-thread, then you should marshal GUI-updating code to UI thread as mentioned here

Risto M
  • 2,919
  • 1
  • 14
  • 27
0

If property setter is invoked on a non-GUI thread, the ps_change method should be as follows:

private void ps_change()
{
    if (InvokeRequired)
    {
        BeginInvoke(new Action(ps_change));
        return;
    }

    lblValueActualVoltage.Text = ps.ActualVoltage.ToString();
    lblValueActualCurrent.Text = ps.ActualCurrent.ToString();
    lblHexCur.Text = ps.HexActualCurrent1;
    lblHexVol.Text = ps.HexActualVoltage1;
}

Why?

i call the properties setter not on the GUI thread.

Controls on the form cannot be manipulated from a non-GUI thread. This is because Windows Forms controls are bound to Windows handles, and Windows handles are owned by threads. If a control (Form or Label in this case) is created on one thread (most likely the main application thread), operations on its handle (e.g. changing the text) cannot be performed on a different thread.

To resolve this, the Control class has methods InvokeRequired, Invoke, and BeginInvoke:

  • InvokeRequired indicates whether the control belongs to a different thread (true) or to the current thread (false).
  • When InvokeRequired is true, either Invoke or BeginInvoke must be used. Both these methods marshal another delegate to the GUI thread that owns the control. All control manipulations must be performed from that delegate.
  • The difference between the two methods is that Invoke blocks the calling thread until the delegate is executed on the GUI thread, whereas BeginInvoke just submits invocation to the queue and returns immediately. The marshaling is performed by a special Windows message sent to a window message queue.

More info here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx

The usual pattern of implementing this is check InvokeRequired inside an event handler, and if it's true, call either Invoke or BeginInvoke and supply delegate to this same event handler again. The handler is then re-invoked on the GUI thread, where InvokeRequired is false, and then the code safely manipulates form controls.

felix-b
  • 8,178
  • 1
  • 26
  • 36
  • i have tried your code, but it still does not work. when i send an voltage array{6,9,12,15,18}. i only get the answer at the last element. but every thing works good, only the number does not show in the GUI except the last element. – Tùng Bill Jan 26 '18 at 14:42