using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using TradeIdeas.Extensions; using TradeIdeas.TIProGUI; namespace TIProDevExtension { /// /// A simple way to monitor the GUI thread and see how responsive it is. /// /// Just display this form and watch it. It will be obvious when the GUI is non-responsive. /// We also record the largest problems in a log. /// public partial class IsTheGuiAlive : Form { public IsTheGuiAlive() { _lastTime = DateTime.Now; InitializeComponent(); Charts.RegisterIsTheGuiAliveAction(guiCallback: (string text) => { AppendText(text); }); } /// /// When was the last time the timer woke us. So we know if there was a long delay. /// If there is a long delay, report it to the log. /// DateTime _lastTime; /// /// The timer is set to go off once every 50 milliseconds. /// /// 100 milliseconds is common for human reaction time. That corresponds to a rate of /// 10 frames per second. You can get away with this frame rate for a simple animation, /// but any worse and you'll notice the indivual frames, and the illustion of animation /// will disappear. 50 milliseconds per frame could do a good job for simple animations. /// Standard def TV had a frame rate of 33 milliseconds and High Def TV is 17 milliseconds. /// private static readonly TimeSpan REQUESTED = new TimeSpan(0, 0, 0, 0, 50); /// /// 150 milliseconds. 3x what we requested. /// private static readonly TimeSpan WORTH_REPORTING = new TimeSpan(0, 0, 0, 0, 150); private void timer1_Tick(object sender, EventArgs e) { DateTime now = DateTime.Now; TimeSpan elapsed = now - _lastTime; if (elapsed > WORTH_REPORTING) { AppendText(now.ToLongTimeString() + ": Waited " + (elapsed - REQUESTED) + " longer than requested." + Environment.NewLine); } _lastTime = now; // Try to update the animation at the REQUESTED frame rate. animatedPanel.Invalidate(); } public void AppendText(string text) { logTextBox.AppendText(text); } private static readonly Brush BRUSH1 = new SolidBrush(Color.Yellow); private static readonly Brush BRUSH2 = new SolidBrush(Color.DarkBlue); /// /// It will take 1 second for one color to be completely replaced by the other. /// The complete period is 2 seconds. /// private const double PERIOD_IN_SECONDS = 2; /// /// 10,000,000 ticks per second. /// private const long PERIOD_IN_TICKS = (long)(PERIOD_IN_SECONDS * 10000000.0 + 0.5); /// /// Draw something different at each moment in time. You should see two rectangles side by side. /// The rectangles will move constantly. /// /// /// private void animatedPanel_Paint(object sender, PaintEventArgs e) { // First fill the entire background with one color. e.Graphics.FillRectangle(BRUSH1, 0, 0, animatedPanel.Width, animatedPanel.Height); DateTime now = DateTime.Now; long position = now.Ticks % PERIOD_IN_TICKS; float left = (float)((position / (double)PERIOD_IN_TICKS * 2 - 1) * animatedPanel.Width); // Cover part of the control with the second color. This second rectangle is the same // size as the control, but we change the position so part of it will be cut off. The // user has no idea which is BRUSH1 and which is BRUSH2; the effect is totally symmetric. e.Graphics.FillRectangle(BRUSH2, left, 0, animatedPanel.Width, animatedPanel.Height); } } /// /// The standard way to add an extension to the menu. /// class IsTheGuiAliveExtension : IMainInit { /// /// Normal init for extensions. /// /// /// public static IList GetExtensions(Object mainProgram) { List result = new List(1); result.Add(new IsTheGuiAliveExtension((IMainProgram)mainProgram)); return result; } private readonly IMainProgram _mainProgram; private IsTheGuiAliveExtension(IMainProgram mainProgram) { _mainProgram = mainProgram; } public void MainInit() { _mainProgram.AddToToolsMenu("Is the GUI Alive?", delegate { new IsTheGuiAlive().Show(); }); } } }