Updating Display Configuration error with Glide

I have a fairly large program going to control a series of fluidic valves and a syringe pump. Ever since I started using Glide to build the UI, I have been getting this error when debugging. Assemblies are deployed, the system starts up and shows the Glide loading screen, then crashes and gives the “Updating Display Configuration” error and then wants me to manually restart debugging. It will do this indefinitely if I restart the debugger.

I believe I have a handle on the issue, but its not reproducible yet, so I don’t know the exact cause. However, if I set break points in the program.generated of a gadgeteer applications as well as in the constructor for Glide (added source to the project), I see that the Glide constructor is trying to display the loading screen before the display module gets initialized in program.generated. I believe this is resulting in the issue somehow. If I get into the condition where this reboot loop is occurring, I comment out the display of the loading screen in the Glide constructor and everything goes back to normal. The kicker is, if I then uncomment it, the system will run fine for a while until the problem will start again at some later date.

I just wanted to throw this out there in case someone with more knowledge could use this information to tell me exactly what is going on, or in case someone else is running into this and needs a fix. I am going to work for a while with the loading screen commented out and see if it happens again. I’ll report back in a week or two if this permanently fixes the issue.

I use Glide a lot but I don’t use it with Gadgeteer, I use with pure NETMF. I create the Glide instance in main and then fire threads to handle all the IO etc. Main then has a loop at the bottom that handles all the graphics functions. I have an exception in here if the window is not yet active or bitmaps have been deleted when I create a new window. Because the buttons etc are all handled until events, it is possible that a window will be destroyed and a new one created as I move through them. For this reason, the drawing functions check if the window exists first and only then will it complete the drawing. If there is an exception when I check the window, I do nothing until the next time the update runs (about 2 times per second)

Can you show any code that updates any glide text boxes etc?

First, I don’t quite understand you saying [quote]I create the Glide instance in main [/quote] as Glide is a static class. Anyway, I don’t know if you quite understood my post, but I think I can post an example of what you are looking for. I have developed a UI based on glide in a manner similar to MelodyGlider, but modified a bit because I have too many windows to have all the bitmaps drawn at the same time. So, I create a static class that has several properties that correspond to text and whatnot on the window object. In the static class, I raise an event whenever a property is changed and then subscribe to that event in the Window. The event simply passes a string that I use to know which component of the display I need to update. This all can get pretty tricky with null references if you aren’t careful about the order in which things occur. Anyway, when one window is done, I null the Window object to allow GC to trash the bitmap, but I keep the window object. This saves a bunch of space.

Okay, for the example:
first is the static interop class:


    public static class RunWindowInterop
    {
        public delegate void SomethingChanged(string returnstring);
        public static event SomethingChanged Change = delegate { };
        private static int _SyringeValvePosition;
        private static string _SyringeValveText;
        private static int _InjectValvePosition;
        private static string _InjectValveText;
        private static string _SelectedSequence = "Default.seq";

        public static void OnChange(string returnstring)
        {
            Change(returnstring);
        }

        public static int SyringeValvePosition
        {
            get
            {
                return _SyringeValvePosition;
            }
            set
            {
                if (_SyringeValvePosition != value)
                {
                    _SyringeValvePosition = value;
                    OnChange("SyringeValvePosition");
                }
            }
        }

There are many more properties that all do something similar to these. You can see I set up the event delegate and raise events if the new value doesn’t match the old. I then poll the actual syringe pump and inject valve over RS232 to ask about their status. I update these properties in that loop. Now, I have windows that inherit from this class (structure stolen from MelodyGlider):


    class WindowFrame
    {
        protected Window ThisWindow;

        public void Initialize(string getString)
        {
            ThisWindow = GlideLoader.LoadWindow(getString);
            SophisticatedInit();
            InitButtons();
        }

        public void SetToMain()
        {
            PrepareToMain();
            Glide.MainWindow = ThisWindow;
        }

        public void SetToMainDown()
        {
            PrepareToMain();
            Tween.NumSteps.SlideWindow = 30;
            Tween.SlideWindow(Glide.MainWindow, ThisWindow, Direction.Down);
            InitText();
            InitOverlay();

        }

        public void SetToMainUp()
        {
            PrepareToMain();
            Tween.NumSteps.SlideWindow = 30;
            Tween.SlideWindow(Glide.MainWindow, ThisWindow, Direction.Up);
            InitText();
            InitOverlay();
        }

        protected virtual void SophisticatedInit()
        {
        }

        protected virtual void InitDataGrid()
        {
        }

        protected virtual void InitButtons()
        {
        }

        protected virtual void PrepareToMain()
        {
        }

        protected virtual void InitOverlay()
        {
        }

        protected virtual void InitText()
        {
        }
    }

This takes care of drawing the elements in the correct order when displaying the page. Finally, the page itself:


    class RunWindow : WindowFrame
    {
        private readonly GUI _gui;
        private ProgressBar Syringepump;
        private byte[] red = new byte[] { 255, 0, 0 };
        private byte[] black = new byte[] { 255, 255, 255 };
        private GT.Timer timer = new GT.Timer(1000);
        private ProgressBar syrProg;
        private Button stopBtn;
        private Button startBtn;
        private Button pauseBtn;
        private Button backBtn;
        private Button loadBtn;
        private Dropdown Sequence;
        private TextBlock injvalve;
        private TextBlock syrvalve;
        private TextBlock syringe;
        private static List list = null;
        private int valvePosition = 1;

        // feed in GUI on instantiation
        public RunWindow(GUI gui)
        {
            _gui = gui;
        }

        //Set up demo Progress bar for syringe display
        //ToDo: Use actual syringe position and speed to control this
        protected override void SophisticatedInit()
        {
            Syringepump = new ProgressBar("Syringe Pump", 255, 360, 136, 80, 30);
            Syringepump.Direction = Direction.Right;
            Syringepump.MaxValue = 100;
            Syringepump.Value = 80;
            timer.Tick += timer_Tick;
        }

        //Timer for Syringe Demo - Kill once syringe pump is available
        void timer_Tick(GT.Timer timer)
        {
            if (syrProg.Value == 0)
            {
                syrProg.Value = syrProg.MaxValue;
            }
            syrProg.Value -= 1;
            //redraw just the progress bar
            syrProg.Invalidate();
        }

        //Initialize XML generated elements and subscribe to their events
        protected override void InitButtons()
        {
            backBtn = (Button)ThisWindow.GetChildByName("Back");
            backBtn.TapEvent += backBtn_TapEvent;

            loadBtn = (Button)ThisWindow.GetChildByName("Load");
            loadBtn.TapEvent += loadBtn_TapEvent;

            startBtn = (Button)ThisWindow.GetChildByName("StartButton");
            startBtn.TapEvent += startBtn_TapEvent;

            pauseBtn = (Button)ThisWindow.GetChildByName("PauseButton");
            pauseBtn.TapEvent += pauseBtn_TapEvent;
            //Start with pause button disabled
            pauseBtn.Enabled = false;

            stopBtn = (Button)ThisWindow.GetChildByName("StopButton");
            stopBtn.TapEvent += stopBtn_TapEvent;
            //Start with stop button disabled
            stopBtn.Enabled = false;

            syringe = (TextBlock)ThisWindow.GetChildByName("SyringeText");
            syrvalve = (TextBlock)ThisWindow.GetChildByName("SyrValve");
            injvalve = (TextBlock)ThisWindow.GetChildByName("InjValve");

            syrProg = (ProgressBar)ThisWindow.GetChildByName("Syringe");

            Sequence = (Dropdown)ThisWindow.GetChildByName("SequenceSelect");
            Sequence.TapEvent += new OnTap(Sequence_TapEvent);
            Sequence.ValueChangedEvent += Sequence_ValueChangedEvent;

            //initialize list of Sequence files for selection dropdown
            string[] files = _gui.sdCard.StorageDevice.ListFiles(_gui.sdCard.StorageDevice.RootDirectory);
            foreach (string file in files)
            {
                if (file.Length > 4)
                {
                    if (string.Compare(file.Substring(file.Length - 4, 4), ".seq") == 0)
                    {
                        object[] a = new object[] { file, file };
                        Sequence.Options.Add(a);
                    }
                }
            }
            list = new List(Sequence.Options, 200);
            RunWindowInterop.Change += RunWindowInterop_Change;
        }

        protected override void InitOverlay()
        {
            DrawSyringe(red);
            DrawSyringeValve(black, RunWindowInterop.SyringeValvePosition);
            DrawInjectValve(red, RunWindowInterop.InjectValvePosition);
        }

        protected override void InitText()
        {
            if (syringe.Text == null || RunWindowInterop.SyringeText == null)
            {
                syringe.Text = "Drive Error";
            }
            else
            {
                syringe.Text = RunWindowInterop.SyringeText;
            }
            if (syrvalve.Text == null || RunWindowInterop.SyringeValveText == null)
            {
                syrvalve.Text = "Valve Error";
            }
            else
            {
                syrvalve.Text = RunWindowInterop.SyringeValveText;
            }
            if (injvalve.Text == null || RunWindowInterop.InjectValveText == null)
            {
                injvalve.Text = "Normal";
            }
            else
            {
                injvalve.Text = RunWindowInterop.InjectValveText;
            }
            ThisWindow.Invalidate();
        }

        protected void DrawSyringe(byte[] colorRGB)
        {
            var color = Microsoft.SPOT.Presentation.Media.ColorUtility.ColorFromRGB(colorRGB[0], colorRGB[1], colorRGB[2]);
            ThisWindow.Graphics.DrawRectangle(color, 2, 280, 130, 150, 50, 2, 2, color, 0, 0, color, 0, 0, 0);
            ThisWindow.Graphics.DrawRectangle(color, 2, 430, 152, 10, 5, 2, 2, color, 0, 0, color, 0, 0, 0);
            ThisWindow.Graphics.DrawRectangle(color, 2, 440, 130, 5, 50, 2, 2, color, 0, 0, color, 0, 0, 0);
            ThisWindow.Graphics.DrawLine(color, 2, 255, 155, 280, 155);
            ThisWindow.Graphics.Flush();
        }

        //Draws the syringe valve in specified color and in a particular position (1-8)
        protected void DrawSyringeValve(byte[] colorRGB, int position)
        {
            var color = Microsoft.SPOT.Presentation.Media.ColorUtility.ColorFromRGB(colorRGB[0], colorRGB[1], colorRGB[2]);
            ThisWindow.Graphics.DrawEllipse(Colors.Red, 3, 180, 155, 50, 50, Microsoft.SPOT.Presentation.Media.Color.Black, 0, 0, Microsoft.SPOT.Presentation.Media.Color.Black, 0, 0, 0);
            DrawSyringeValvePosition(position);
            ThisWindow.Graphics.Flush();
        }

        //Draws the flow path to the correct position on the valve
        protected void DrawSyringeValvePosition(int position)
        {
            ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 3, 180, 155, 230, 155);
            if (position == 1)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 225, 133, 180, 155);
            }
            else if (position == 2)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 204, 111, 180, 155);

            }
            else if (position == 3)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 174, 105, 180, 155);
            }
            else if (position == 4)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 134, 136, 180, 155);
            }
            else if (position == 5)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 131, 166, 180, 155);
            }
            else if (position == 6)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 147, 192, 180, 155);
            }
            else if (position == 7)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 196, 202, 180, 155);
            }
            else if (position == 8)
            {
                ThisWindow.Graphics.DrawLine(Colors.Teal, 2, 221, 184, 180, 155);
            }
        }

        //Draws the inject valve in specified color and in a particular position (1-2)
        protected void DrawInjectValve(byte[] colorRGB, int position)
        {
            var color = Microsoft.SPOT.Presentation.Media.ColorUtility.ColorFromRGB(colorRGB[0], colorRGB[1], colorRGB[2]);
            ThisWindow.Graphics.DrawEllipse(Microsoft.SPOT.Presentation.Media.Color.Black, 3, 60, 155, 50, 50, Microsoft.SPOT.Presentation.Media.Color.Black, 0, 0, Microsoft.SPOT.Presentation.Media.Color.Black, 0, 0, 0);
            DrawInjectValvePosition(position);
            ThisWindow.Graphics.Flush();
        }

        //Draws the flow path for the specified valve position
        protected void DrawInjectValvePosition(int position)
        {
            if (position == 1)
            {
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 45, 190, 75, 190);
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 20, 150, 35, 125);
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 84, 125, 100, 150);
            }
            if (position == 2)
            {
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 45, 120, 75, 120);
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 20, 163, 35, 190);
                ThisWindow.Graphics.DrawLine(Microsoft.SPOT.Presentation.Media.Color.Black, 2, 84, 190, 100, 163);
            }
        }

        //Update the selected sequence in the interop to allow execution of proper sequence
        void Sequence_ValueChangedEvent(object sender)
        {
            RunWindowInterop.SelectedSequence = (string)Sequence.Value;
            Glide.CloseList();
            InitOverlay();
        }

        //Open the list of possible sequences
        void Sequence_TapEvent(object sender)
        {
            Glide.OpenList(sender, list);
        }

        //This is the magic... Subscribe to event from the Interop to dynamically update display
        //String passed in indicates what particular features need to be updated
        //Read in changed values from the Interop
        void RunWindowInterop_Change(string returnstring)
        {
            if (returnstring == "SyringeValveText")
            {
                syrvalve.Text = RunWindowInterop.SyringeValveText;
            }
            if (returnstring == "InjectValveText")
            {
                injvalve.Text = RunWindowInterop.InjectValveText;
            }
            if (ThisWindow != null)
            {
                ThisWindow.Invalidate();
                InitOverlay();
            }
        }

        //Dummy to show valve position updating when no valve is connected
        //Will be removed in final version
        void loadBtn_TapEvent(object sender)
        {
            if (valvePosition > 8)
            {
                valvePosition = 1;
            }
            ThisWindow.Invalidate();
            InitOverlay();
        }

        //Stops the sequence, disables itself and enables the run button as well as the back button
        void stopBtn_TapEvent(object sender)
        {
            SequenceExecuter.SequenceThread.Abort();
            timer.Stop();
            pauseBtn.Enabled = false;
            pauseBtn.Invalidate();
            stopBtn.Enabled = false;
            stopBtn.Invalidate();
            startBtn.Enabled = true;
            startBtn.Invalidate();
            backBtn.Enabled = true;
            backBtn.Invalidate();
        }

        //Pauses the sequence, disables itself and enables the stop button
        void pauseBtn_TapEvent(object sender)
        {
            SequenceExecuter.SequenceThread.Suspend();
            timer.Stop();
            pauseBtn.Enabled = false;
            pauseBtn.Invalidate();
            startBtn.Enabled = true;
            startBtn.Invalidate();
            stopBtn.Enabled = true;
            stopBtn.Invalidate();
            backBtn.Enabled = false;
            backBtn.Invalidate();
        }

        //if a sequence is running, it resumes it... If there isnt one, it starts a new one
        //also disables back button to lock users into run window during execution and enables pause button
        void startBtn_TapEvent(object sender)
        {
            if (SequenceExecuter.SequenceThread != null)
            {
                if (SequenceExecuter.SequenceThread.IsAlive)
                {
                    SequenceExecuter.SequenceThread.Resume();
                }
            }
            else
            {
                SequenceExecuter.RunSequence();
            }
            timer.Start();
            startBtn.Enabled = false;
            startBtn.Invalidate();
            pauseBtn.Enabled = true;
            pauseBtn.Invalidate();
            stopBtn.Enabled = true;
            stopBtn.Invalidate();
            backBtn.Enabled = false;
            backBtn.Invalidate();
        }

        //returns to main menu, disposing of the bitmap in the process to free resources
        private void backBtn_TapEvent(object sender)
        {
            _gui.MainMenu.Initialize(Resources.GetString(Resources.StringResources.MainMenu));
            _gui.MainMenu.SetToMainDown();
            timer.Stop();
            syrProg.Dispose();
            ThisWindow = null;

        }
    }

I am not a professional programmer, rather a chemistry professor, so I am sure this could be a heck of a lot better, but it does get the job done. This is also my first implementation, so I will probably come back and clean a lot of this up. But, it does update text boxes as well as the graphics that overlay the XML generated window dynamically based on changes occurring elsewhere in the program. Does this help?

Best,
Phil