Wygląda na to, że wpadłeś w typowe pułapki, ale nie martw się, można je naprawić :)
Najpierw musisz spojrzeć na swoją aplikację nieco inaczej i zacząć dzielić ją na części. Możemy podzielić kawałki na dwa kierunki. Najpierw możemy oddzielić logikę sterującą (reguły biznesowe, kod dostępu do danych, kod praw użytkownika itp.) Od kodu interfejsu użytkownika. Po drugie możemy rozbić kod interfejsu użytkownika na części.
Więc najpierw zajmiemy się drugą częścią, dzieląc interfejs na części. Najłatwiejszym sposobem na to jest utworzenie pojedynczego formularza hosta, na którym tworzysz interfejs użytkownika za pomocą kontrolek użytkownika. Każda kontrola użytkownika będzie odpowiedzialna za region formularza. Wyobraź sobie, że Twoja aplikacja ma listę użytkowników, a kiedy klikniesz użytkownika, pole tekstowe poniżej wypełnia się szczegółami. Możesz mieć jedną kontrolę użytkownika zarządzającą wyświetlaniem listy użytkowników, a drugą kontrolę wyświetlania danych użytkownika.
Prawdziwa sztuczka polega na tym, jak zarządzasz komunikacją między kontrolkami. Nie chcesz, aby 30 kontrolek użytkownika w formularzu losowo zawierało odniesienia do siebie i wywoływało na nich metody.
Tworzysz interfejs dla każdej kontrolki. Interfejs zawiera operacje, które kontrolka zaakceptuje oraz wszelkie zdarzenia, które wywoła. Gdy myślisz o tej aplikacji, nie przejmujesz się zmianami wyboru listy, interesuje Cię fakt, że zmienił się nowy użytkownik.
Korzystając z naszej przykładowej aplikacji, pierwszy interfejs kontrolki obsługującej listę użytkowników zawierałby zdarzenie o nazwie UserChanged, które przekazuje obiekt użytkownika.
Jest to świetne, ponieważ teraz, gdy znudzi Ci się pole listy i chcesz sterować magicznym okiem 3D, po prostu koduj go do tego samego interfejsu i podłącz go :)
Ok, więc część druga, oddzielając logikę interfejsu użytkownika od logiki domeny. Cóż, jest to dobrze zużyta ścieżka i polecam przyjrzeć się wzorowi MVP tutaj. To jest naprawdę proste.
Każda kontrolka nazywa się teraz Widok (V w MVP) i już omówiliśmy większość tego, co jest potrzebne powyżej. W tym przypadku kontrola i interfejs dla niego.
Dodajemy tylko model i prezentera.
Model zawiera logikę, która zarządza stanem aplikacji. Znasz rzeczy, to trafiłoby do bazy danych, aby uzyskać użytkowników, napisać do bazy danych po dodaniu użytkownika i tak dalej. Chodzi o to, że możesz to wszystko przetestować w całkowitej izolacji od wszystkiego innego.
Prezenter jest nieco trudniejszy do wyjaśnienia. Jest to klasa, która znajduje się między modelem a widokiem. Jest tworzony przez widok, a widok przechodzi do prezentera za pomocą interfejsu, który omówiliśmy wcześniej.
Prezenter nie musi mieć własnego interfejsu, ale i tak lubię go tworzyć. Sprawia, że to, co chcesz, aby prezenter zrobił wyraźnie.
Tak więc prezenter ujawniłby metody takie jak ListOfAllUsers, których widok użyłby do uzyskania swojej listy użytkowników, alternatywnie można umieścić metodę AddUser w widoku i wywołać ją z prezentera. Wolę ten drugi. W ten sposób prezenter może dodać użytkownika do pola listy, kiedy tylko chce.
Prezenter miałby również właściwości takie jak CanEditUser, które zwrócą wartość true, jeśli wybranego użytkownika można edytować. Widok wyświetli zapytanie, które za każdym razem musi wiedzieć. Możesz chcieć edytować te w kolorze czarnym i tylko do odczytu w kolorze szarym. Technicznie jest to decyzja dotycząca widoku, ponieważ jest on skoncentrowany na interfejsie użytkownika, czy to, czy użytkownik jest edytowalny, zależy przede wszystkim od Prezentera. Prezenter wie, ponieważ mówi do Modelu.
Podsumowując, użyj MVP. Microsoft udostępnia coś o nazwie SCSF (Smart Client Software Factory), które wykorzystuje MVP w sposób, który opisałem. Robi też wiele innych rzeczy. Jest dość złożony i nie podoba mi się sposób, w jaki wszystko robią, ale może to pomóc.