Dynamiczne tworzenie elementów ToolStripMenuItems

0

Witam

Mam pomysł, i wydaje mi się możliwy do wykonania, ale w praktyce jeszcze nie udało mi się wpaść na to, jak to zrobić

Pobieram z bazy danych listę nazw do DataTable.
Chciałbym, aby ta lista nazw wyświetliła się jako kolejne elementy submenu typu ToolStripMenuItems dla wybranej opcji menu.
Niestety z założenia nie da się wcześniej zdefiniować jak taka lista będzie wyglądała, więc nie da się również sztywno zdefiniować elementów menu jak i metod im przypisanych.
Jakkolwiek łatwe jest samo stworzenie nowych elementów menu o nazwach pobranych z DataTable, i odpowiednio otagowanych choćby do Enumerable (czyli powiedzmy przeszukiwania elementów), to nie wiem jak dodać do każdego z tych elementów własnej dynamicznej metody z unikalną nazwą.
De-facto metoda ta za każdym razem ma robić dokładnie to samo:

  • pobierać nazwę wybranego submenu, lub pozycję z DataTable
  • ustawiać wartość zmiennej określającej który element menu wywołano nadrzędnie
  • załadować panel graficznego ui. - zawsze taki sam

Na razie mam coś takiego:

        private void ISM_Nowy_Grafik_Click(object sender, EventArgs e)
        {
            ISM_Nowy_Grafik = (ToolStripMenuItem)sender;

            PobierzDane loadData = new PobierzDane();
            DataTable listaDziałów = new DataTable();
            string polecenieSQL = string.Format(@"SELECT Dzial FROM [dbo].[Dzial]");

            try
            {
                loadData.Pobierz(polecenieSQL, ref listaDziałów);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Błąd pobierania listy działów : " + ex);
            }

            if (listaDziałów.Rows.Count == 0)
            {
                return;
            }
            else
            {
                string nazwaDziału = "";

                if (ISM_Nowy_Grafik.DropDownItems.Count == 0)
                {
                    for (int i = 0; i < listaDziałów.Rows.Count; i++)
                    {
                        nazwaDziału = Convert.ToString(listaDziałów.Rows[i][0]);

                        ToolStripMenuItem submenu;

                        submenu = new ToolStripMenuItem();
                        submenu.Text = nazwaDziału;
                        submenu.Name = i.ToString(); ;
                        submenu.Tag = i;
                        submenu.Checked = false;
                        submenu.CheckOnClick = true;
                        submenu.Click += new EventHandler(TSMI_NazwaDziału_Click);
                        ISM_Nowy_Grafik.DropDownItems.Add(submenu);
                    }
                }
            }
        }

TSMI_NazwaDziału_Click - może być wspólna dla wszystkich elementów wyświetlonych, bo wykona za każdym razem dokładnie taki sam kod.

Czy ktoś ma pomysł , lub wie i może podpowiedzieć jak takie coś osiągnąć?

0

A z czym masz problem? Bo masz wszystko napisane przecież. Jakość tego kodu to inna sprawa.

0

Szczerze powiedziawszy to nie wiem do końca o co chodzi, ale nazwę dodanego obiektu do menu możesz uzyskać w ten sposób. Z Twojego tłumaczenia wynika również, że chciałbyś mieć inną nazwę metody dla każdego dodawanego obiektu do tego menu, ale to nie miałoby żadnego sensu, wystarczy jedna metoda z tą samą nazwą, dzięki której zrobisz wszystko to co chcesz. Dodatkowo chciałbym zwrócić uwagę na nazwę zmiennych, polskie znaki aż rażą. No i ogólnie powinno się używać angielskich nazw.

        private void ISM_Nowy_Grafik_Click(object sender, EventArgs e)
        {
            string nazwaDzialu = textBox1.Text;

            ToolStripMenuItem submenu = new ToolStripMenuItem();
            submenu.Text = nazwaDzialu;
            submenu.Name = nazwaDzialu;
            submenu.Checked = false;
            submenu.CheckOnClick = true;
            submenu.Click += TSMI_NazwaDzialu;
            menuStrip1.Items.Add(submenu);
        }

        private void TSMI_NazwaDzialu(object sender, EventArgs e)
        {
            ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
            if (tsmi != null)
            {
                Console.WriteLine(tsmi.Name);
            }            
        }
0

Właśnie o osobne nazewnictwo mi chodziło, ale zrobiłem test, i jest ok.
Zaciąga sobie z bazy nazwy, obsługuje wszystkie jedną metodą _Click, i po uruchomieniu wystarczy, że wyczyszczę listę .Items.

            ISM_Nowy_Grafik.DropDownItems.Clear();

Przecież, za każdym razem gdy będzie potrzebne, ponownie stworzy się świeżutkie.

A co do jakości kodu - chętnie przyjmę każdą sugestię.

0
BPiotr napisał(a):

A co do jakości kodu - chętnie przyjmę każdą sugestię.

Przede wszystkim nazewnictwo. Nie nazywaj metod po Polsku a już tym bardziej z polskimi znakami :/
Twoja metoda na klik łamie z hukiem zasadę pojedynczej odpowiedzialności. Robi kilka rzeczy: łączy się z bazą, pobiera dane -> pobiera WSZYSTKIE działy... Powinieneś mieć tam jakiś warunek np. żeby nie pobierać działów, które zostały usunięte. A robienie DELETE w tabeli z działami nie jest dobrym rozwiązaniem.
Co tam dalej... no w końcu menu itemy i dodaje je do głównego menu itema.

Założę się o milion dolarów, że łamiesz w aplikacji drugą ważną zasadę: DRY - Don't Repeat Yourself. Co znaczy, że masz na pewno pełno powielonego kodu w całej aplikacji. Uwierz mi - ta aplikacja długo nie pożyje. Za jakiś czas utrzymywanie jej będzie jednym wielkim koszmarem.

Co tam jeszcze... Nazywasz klasy jak metody (PobierzDane). No i jeśli już musisz nadawać Name dla swoich menu itemów z jakiegoś powodu, to zrób to lepiej, a nie zwykłą liczbą, bo to też się może zemścić. Jeśli już musisz nadawać name, to zrób coś w tym stylu:

submenu.Name = $"deptMenuItem_{i}";

Jeśli nigdzie nie wyszukujesz tych menu itemów po nazwie, to nie musisz jej nadawać.

0

Chyba co do wszystkiego masz racje, pisze to już jakiś czas, i sam zauważam wielokrotne powtórzenia, co powoli staram się likwidować. Zajmuje to sporo czasu, ale upraszcza kod - zauważalnie.
Umieszczanie wszystkiego w metodzie - to właściwie pójście na łatwiznę, bo szybko chce sprawdzić cz pomysł jest ok, a potem już tak zostaje. Będę się starał stopniowo zrobić z tym porządek.
Co do nazewnictwa metod i klas - nie bardzo rozumiem - chodzi chyba o początek z dużej litery - to również postaram się stopniowo popoprawiać.

i śliczne dzięki za krytyczne spojrzenie na kod.

PB

0

z klasami i metodami nie o to chodzi o czym myślisz. Zarówno nazwy metod jak i klas powinny być pisane stosując notację PascalCase i to masz dobrze, ale chodzi o samą nazwę. Do nazewnictwa klas stosujesz rzeczowniki, a do metod czasowniki, a Ty masz nazwaną klasę "PobierzDane" więc taka nazwa nie pasuje bo opisuje czynność. So, Twoja klasa powinna się nazywać np. SqlDataAdapter i w niej definiujesz sobie metodę Fill służącą do wypełniania DateTable danymi. W sumie to podałem nazwy z gotowych klas, które służą do obsługi baz danych. Nie wiem czego Ty używasz ale warto korzystać z gotowych bibliotek. Dodatkowo rzucił mi się w oczy pewien błąd. Czy wiesz jaka jest różnica między typami wartościowymi, a referencyjnymi. Bo widzę, że przekazujesz do metody obiekt typu DateTable używając słowa kluczowego ref. Typ DateTable to klasa, a nie struktura więc nie używasz ref ani out przekazując obiekt tego typu do metody.

0

Ok - rozumiem o co chodzi w nazewnictwie - ale z DataTable - to nie wiedziałem.
Jakoś Profesor który miał z nami wykłady w tym zakresie nie raczył o takich sprawach wspomnieć.
Generalnie w ogóle niewiele mówił.... i właściwie staram się uczyć sam.

Najpierw ogarnę nazwy metod i klas, a jest kodu z 20k linijek, wiedz ciut się zejdzie.
Nowy kod już pisze rozbijając wszystkie czynności na poszczególne metody, i już widzę, że mogę dzięki temu uprościć sporo wcześniej pisanego kodu.

Naprawdę dzięki, bo pierwszy raz ktoś mi rzeczowo podpowiedział a-propo semantyki i przejrzystości kodu.

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