0

I have an application which has multiple controls on multiple pages. I'm using the Telerik Winforms controls. One of those pages has a RadGridView in a UserControl, which is on a RadPageViewPage in a RadPageView, which in turn is nested in another RadPageViewPage in another RadPageView. The following code is basically just to handle a Loading spinner that is housed in its own transparent Form. It is always called on its own thread, of course.

private static void RunWaiting(Control c, string text)
{
    wf = new WaitingForm();
    wf.drwbieSpinnerFrame.Text = text;
    wf.ShowInTaskbar = false;
    wf.Left = c.Left + (c.Width / 2); 
    wf.Top = c.Top + (c.Height / 2);
    wf.Width = c.Width;
    wf.Height = c.Height;               
    wf.FormBorderStyle = FormBorderStyle.None;
    wf.ControlBox = false;
    wf.TopMost = true;
    wf.StartPosition = FormStartPosition.Manual;

    Application.Run(wf);
}

Clearly, I want the spinner (WaitForm) to appear over the center of a control on-demand. It's fine if I pass it the main UserControl that houses the RadGridView, and I can also pass it the parent of that control and center on the RadPageViewPage. If I pass this method the RadGridView, the spinner doesn't appear at all, even though the code is called and the attributes of "wf" are still set. Can anyone tell me what I'm missing?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
CDove
  • 1,940
  • 10
  • 19
  • I'm not sure why you want to show a forms in another thread, but you can do it [this way](http://stackoverflow.com/a/41263382/3110834). – Reza Aghaei Jan 10 '17 at 20:05
  • That won't actually work. The spinner form has to be in a separate thread because it needs to run while a long-term operation is going on the main thread. I'm already calling this method from another thread, but it should be centered over the control that is loading data. That's where I'm having trouble. – CDove Jan 10 '17 at 20:11
  • In general you don't need to open a form in another thread, the time-consuming task should be in another thread but not your waiting form. Anyway, if you want to open the form from another thread, the link which I shared is the way of opening a form from another thread and surely it works. – Reza Aghaei Jan 10 '17 at 20:19
  • I'm already successfully opening the form in another thread. The issue is entirely the actual position of the form. The form itself is transparent and holds a "loading" spinner which needs to continually update while the UI loads data behind it. I tried an async load already and it froze Telerik's RadWaitingBar control. – CDove Jan 10 '17 at 20:24
  • I believe you don't need to open the form in another thread, take a look at [this post](http://stackoverflow.com/a/39142535/3110834) or [this one](http://stackoverflow.com/a/37473192/3110834). – Reza Aghaei Jan 10 '17 at 20:26
  • About the location: you set `wf.Left = c.Left + (c.Width / 2);`. `Location` of form is relative to desktop, but location of control is based on it's parent. – Reza Aghaei Jan 10 '17 at 20:27
  • Also based on your current solution, pay attention to probable memory leaks. – Reza Aghaei Jan 10 '17 at 20:36

1 Answers1

0

Unfortunately, the time-consuming task itself is updating the UI and must exist on the UI thread for other reasons, but I was able to put together a solution by using a recursive class that got me the appropriate (x,y) coordinates to display a form in the middle of the control.

private static void RunWaiting(Control c, string text)
        {
            wf = new WaitingForm();
            wf.drwbieSpinnerFrame.Text = text;
            wf.ShowInTaskbar = false;           
            int[] tl = GetTopLefts(c);
            wf.Top = (tl[0] + (c.Height / 2)) - (wf.Height / 2);
            wf.Left = (tl[1] + (c.Width / 2)) - (wf.Width / 2);            
            wf.FormBorderStyle = FormBorderStyle.None;
            wf.ControlBox = false;
            wf.TopMost = true;
            wf.StartPosition = FormStartPosition.Manual;
            IsHolding = true;
            Application.Run(wf);

        }

And the method it calls to get the position data:

private static int[] GetTopLefts(Control c)
        {
            int top, left;
            top = c.Top;
            left = c.Left;
            if (c.Parent != null)
            {
                int[] parentPoint = GetTopLefts(c.Parent);
                top += parentPoint[0];
                left += parentPoint[1];
            }
            return new int[] { top, left };
        }

You could probably get through this just fine by making the final output of GetTopLefts() a Point, but something about this way felt arbitrarily more reliable.

CDove
  • 1,940
  • 10
  • 19
  • 1
    All you need is `c.Parent.PointToScreen(c.Location);`. Why do you use a recursive method?! – Reza Aghaei Jan 10 '17 at 21:24
  • Also instead of an `int[]` you can use `Point`. – Reza Aghaei Jan 10 '17 at 21:27
  • `c.Parent.PointToScreen(c.Location)` was causing some problems on the terminal server environment. I don't know why it was, but you can't argue with the raw math in a recursive method, and that's working wonderfully. – CDove Jan 11 '17 at 12:43
  • Based on question title and description using `c.Parent.PointToScreen(c.Location);` is absolutely correct answer. I believe your posted answer may solve your problem but it is totally misleading because it shows the worst way of solving the problem and also what you said in comments about terminal server or something else is not reproducible and even not mentioned in question and so it will not be useful for future readers at all. – Reza Aghaei Jan 11 '17 at 16:11