Dobry sposób na blokadę całego UI (WinForms) wraz z skrótami klawiaturowymi

0

Mam pytanie dotyczące MenuStrip w .NET (C #). Zastanawiam się, jak ustawić wartość dla całego menu (również ze wszystkimi elementami drzewa menu)

Menu.Enabled = false

Chodzi o zablokowanie skrótów w menu. W specyfikacji piszą że trzeba to zrobić ręcznie dla każdego elementu.
Czy istnieje prostsze rozwiązanie niż użycie pętli foreach zagnieżdżonej w innej pętli foreach?

public void LockForm(bool enabled){
             foreach (Control c in f.Controls)
                {                    
                        if (c is MenuStrip)
                        {
                            foreach (ToolStripMenuItem z in ((MenuStrip)c).Items)
                            {
                                ReflectionEnableItem(z, enabled);
                            }
                        }                                           
                }       
}

private static void ChangeStateItem(ToolStripMenuItem z, bool enabled)
        {
            if (z.HasDropDown)
            {
                foreach (object z1 in z.DropDownItems)
                {
                    if (z1 is ToolStripMenuItem)
                    {
                        ToolStripMenuItem subMenu = (ToolStripMenuItem)z1;
                        ReflectionEnableItem(subMenu, enabled);
                    }
                }
            }
            else
            {
                z.Enabled = !enabled;                
            }
        }  

Chcę zablokować wszystkie okna i wszystkie kontrolki (w tym skróty klawiaturowe) na czas wykonywania operacji w tle.

0

Z tego co wyczytałem to jest jakiś bug na toolstripmenuitem, którego nie mają zamiaru poprawić od wersji .NET Framework 2.0 (nie wiem czy to prawda)
Możliwe obejścia:

class ToolStripItemEx : System.Windows.Forms.ToolStripMenuItem{
    public override bool CanSelect {
        get {
            return this.Enabled;
        }
    }
}

Możesz ewentualnie blokować subitemy na rozwinięciu parenta.
Ja to bym chyba zrobił jakieś okno dialogowe (borderless oczywiście, żeby nikt tego nie zamknął iksem), które nie dość, że nie pozwoli na klikanie okna "pod spodem" to będzie informować, że coś się dzieje.

1

Ja to bym chyba zrobił jakieś okno dialogowe (borderless oczywiście, żeby nikt tego nie zamknął iksem), które nie dość, że nie pozwoli na klikanie okna "pod spodem" to będzie informować, że coś się dzieje.

Takie okno może mieć oczywiście ustawioną pewną przezroczystość i masz wtedy działanie podobne do monitów Windowsa (windows robi tak naprawdę print screen, potem odpowiednio manipuluje obrazem, pokazuje Ci go i na nim wyświetla okno z monitem - tak w skrócie).

Poza tym to rekurencja. I nie masz żadnych zagnieżdżonych pętli.

1

Druga sprawa, jeśli już tak bardzo jesteś uparty na pętle to proponuję ją zawęzić

foreach(var ctrl in this.Controls.OfType<MenuStrip>())
{}

Może być tak, że zanim twoja pętelka doleci do

if (c is MenuStrip)

to przeleci przez wszystkie kontrolki w aplikacji.
Rekurencja zdecydowanie lepsza jak sugeruje kolega wyżej ;)

0

Ok. Ja u siebie interfejs próbuje blokować czymś na kształt GlassPanelu. Tworze przezroczysty panel na oknie i przenoszę go na górę. To usuwa możliwość klikania w interfejs.
AdamWox użyłem tego co proponujesz. Chciałbym to zrobić dobrze - zgodnie ze sztuka ;) Możecie powiedzieć czy takie rozwiązanie jest dobre?

private static void ChangeStateItem(ToolStripItemCollection i, bool enabled)
{
	foreach (var z in i.OfType<ToolStripMenuItem>())
        {
	        if (z.HasDropDown)
                {
        		ChangeStateItem(z.DropDownItems, enabled);                    
                }
                else
                {
                	z.Enabled = !enabled;
                }
	}
}

public void LockForm(bool enabled)
{
	foreach (Form f in Application.OpenForms)
        {
        	GlassPanel gp = f.Controls.Find("GLASPANEL", true).FirstOrDefault() as GlassPanel;

                if (gp == null)
                {
                	//del glaspanel
                	if (!enabled)
                    	{
                        	f.Controls.Remove(gp);
                        	Cursor.Current = Cursors.Default;
                    	}
                    	else
                    	{
                        	//busy cursor
                        	Cursor.Current = Cursors.WaitCursor;

	                        //add glaspanel
        	                gp = new GlassPanel(f);
                	        f.Controls.Add(gp);
                        	gp.Name = "GLASPANEL";
	                        gp.BringToFront();
        	                f.Invalidate();
                	        gp.Focus();
                    	}
                }

                foreach (var c in f.Controls.OfType<MenuStrip>())
                {
                   	 ChangeStateItem(c.Items, enabled: enabled);
                }
	}               
}

1

Czy zgodnie ze sztuką? Zdecydowanie nie.

Po 1. Zrobiłeś byka, jak stodoła:

 if (gp == null)
...
   f.Controls.Remove(gp);

Po 2. Metoda LockForm gwałci zasadę pojedynczej odpowiedzialności (SRP). Robi zdecydowanie za dużo. Powinna zająć się blokowaniem/odblokowaniem formy w taki sposób, żeby poszczególne elementy delegować do innych metod. Poza tym nie robi tego, czego się od niej oczekuje. Wywołując metodę LockForm oczekuję, że zablokuje mi ona jakąś formę. Najpewniej tą, którą przekażę w parametrze (ewentualnie formę, na rzecz której wywołuję tą metodę).

Natomiast u Ciebie metoda blokuje wszystkie formy i robi wszystkie zadania składowe sama.

Można to rozwiązać na kilka sposobów. Np:

  1. Wszystkie Twoje formy dziedziczą po jakiejś bazowej formie, która ma metodę LockForm, która to metoda blokuje tylko odpowiednią formę
  2. Tworzysz na poziomie Program.cs (albo w jakiejś klasie statycznej FormHelpers/UIHelpers) metodę LockOpenedForms i ta metoda:
  • przelatuje przez wszystkie otwarte formy, które dziedziczą po Twojej bazowej
  • wywołuje u nich metodę LockForm

Można to zrobić bez dziedziczenia, po prostu wtedy metoda LockForm powinna być jakąś metodą statyczną i przyjmować w parametrze formę do zablokowania.

W każdej wersji metoda LockForm powinna delegować do innych metod odpowiednie działania, np:

public static void LockForm(Form f, bool enabled)
{
  ShowGlassPanel(f, enabled); //to też metody statyczne
  LockMenu(f, enabled);    
}
0

No tak Juhas, mądrze prawisz ;) Dokształcić się muszę w tej kwestii ;) Dziękuję Ci za pomoc. Pozdr.

1 użytkowników online, w tym zalogowanych: 0, gości: 1