[ALG / C#] TreeView met MVC (of MGM) pattern?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Tijdens mijn zoektocht naar het Model-View-Controller pattern, stuite ik op dit artikel waarin uitgelegd wordt hoe je logica van je controls kan scheiden. Zelf zat ik al hard na te denken over hoe ik het MVC kon implementeren, en kwam toen op een ongeveer zelde constructie uit. Het kan zijn dat ik de elementen uit MVC op een verkeerd niveau plaats, graag hier dan reactie op.

De TreeView beschouw ik als de Controller, aangezien hier de input van de gebruiker op binnenkomt. Deze TreeView heeft een verwijzing naar de TreeNodes, wat de Views in het verhaal zijn. Zij zijn tenslotte verantwoordelijk voor het tonen aan de gebruiker. In de Tag van zo'n TreeNode zit een verwijzing naar een object in een lijst, verborgen in een onderliggende class. Ik zie nu dit object als Model. Dit object heeft dan ook een lijst met verwijzingen naar de TreeNodes die naar hem wijzen, zodat hij kan iedereen kan waarschuwen als ie zelf gewijzigd is. Hierdoor kunnen meerdere TreeNodes dezelfde waarde bevatten, en tegelijkertijd geupdate worden.

Ik ben er dus niet helemaal zeker van of ik niet de TreeView als View moet beschouwen, omdat de TreeNode misschien een te laag niveau is. Maar wat is dan de Controller? Of is de TreeView beiden?
En is het ok dat ik het object in een lijst (omdat het om een TreeView gaat) als Model beschouw? Of zou ik veel eerder de lijst zelf als Model moeten zien? Dat lijkt me wel aannemelijk wanneer de TreeView als View beschouwd wordt.

Voor de TreeView is bovenstaand verhaal aannemelijk. Maar wat als je een TextBox in het MVC pattern wilt gieten? Moet je dat dan alleen voor dat betreffende control doen, of geldt het pattern meer voor de complete GUI? Het nadeel hiervan is, net als met dat je de TreeView als View ziet, dat bij een wijziging er veel meer geupdate moet worden dan nodig is.

Op welke punten verschilt het MVC pattern nog (zoals ik hierboven beschreven heb) van het MGM pattern?

offtopic:
Ik heb thuis al wel het GoF boek liggen, maar moet er nog in beginnen...

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

Ik heb deze vraag voor jou al eens beantwoord : [rml][ C#] TreeView data & bussines logic[/rml]

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Klopt ja :)

Alleen heb ik daar met je antwoord nog een probleem dat het mij niet duidelijk is of je de data nou echt gescheiden hebt van de GUI elementen. Zoals ik het nu zie is het meer dat jij ipv de standaard TreeNode een eigen implementatie voorschrijft. Ik wil dit juist helemaal ontkoppelen met het MVC-pattern (en ook leren hoe dit in het algemeen moet).

Nu probeer ik de troep die ik toen gemaakt heb op te ruimen. Toentertijd had ik de objecten die ik gebruikte allemaal rechtstreeks afgeleid van TreeNode. Het probleem wat ik daarmee veroorzaakt heb, is dat (bijna) alle functionaliteit om wat met die data te doen in de GUI-class zitten.

Het model zoals ik boven heb beschreven (waarvan ik dus niet weet óf het MVC is, zoniet dan wil ik dat graag wel zo maken) scheidt de GUI-elementen (op TreeNode-niveau) van de data.

[Edit]
Heeft de Controller ook standaard functies? Deze hoeft toch nooit berichten te ontvangen? Is het dan wel een Interface, of is het meer een naampje voor een onderdeel van het pattern? Volgens deze site heeft de View ook een weakly-typed pointer naar de Controller, maar waarom staat er niet uitgelegd.

                 +------------+
                 |   Model    |
                 +------------+
                /\ .          /\
                / .            \
               / .              \
              / .                \
             / \/                 \
      +------------+ <------ +------------+
      |    View    |         | Controller |
      +------------+ [b]......>[/b] +------------+

[ Voor 48% gewijzigd door riezebosch op 26-04-2005 14:02 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Probeer het toch zo simpel mogelijk te houden. Model is verantwoordelijk van updates op de data van je model. Je View kijkt direct naar het model en wordt bij veranderingen op de hoogte gesteld. Views die veranderingen aanbrengen sturen hun events door naar je Controller. De controller stuurt de veranderingen naar het model en het cirkeltje is rond.

Dus V&C houden zich alleen bezig met het model en M roept je DAO componenten aan. Ergo: GUI elementen zijn gescheiden van je data.

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Ja, maar wat is dan de Model, de View en de Controller in het verhaal? Dat probeer ik nu juist uit te vinden :)

Mijn suggestie:
Controller: TreeView (verantwoordelijk voor de input van gebruiker)
View: TreeNode (verantwoordelijk voor het weergeven aan de gebruiker)
Model: eigengemaakte class (verantwoordelijk voor de opslag en onderlingen relatie tussen objecten)

edit:

En de Controller (TreeView) bestaat dus uiteindelijk uit een hele (geneste) lijst van Views met waarschijnlijk een 1-op-1 relatie met de Models (mijn classes).

[ Voor 74% gewijzigd door riezebosch op 26-04-2005 14:06 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Tegensuggestie ;) :

- View: TreeView, verantwoordelijk voor data verkregen vanuit Model om te zetten naar Nodes
- Controller: Aparte klasse, verantwoordelijk voor afhandeling van UI Events
- Model: Aparte klasse, puur voor in memory houden van Model informatie

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Maar impliceert dat niet dat een update vanuit een Model zorgt dat de complete TreeView geüpdate wordt?

En wat is de reden om de controller zo op te splitsen? Dan moet je vanuit de GUI óf vanuit de TreeView de acties van de gebruiker doorsturen naar de Controller.

Wanneer we de TreeView als Controller beschouwen, biedt .NET/C# al de mogelijkheid om de input van de gebruiker in de class af te vangen. En wanneer we de TreeNode als View beschouwen, weet deze meteen wanneer hij zelf geüpdate moet worden.

Maar misschien ruk ik zo het complete MVC pattern uit z'n verband?

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

Ok, een voorbeeldje hoe het dan wel moet zonder interface, volledig volgens het mvc model :

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Model
public class Materiaal {
  guid id;
  string naam;
  Materiaal Hoofdmateriaal;
  Categorie categorie;
}

// Controller
public class MateriaalTreeviewController {
   MateriaalTreeviewController(treeview tv,MateriaalList mats) {
      tv.Nodes.Clear();
      foreach (Materiaal m in mats.Items) {
         TreeNode tn = new TreeNode(m.Naam);
         tn.Tag = m;
         tv.Nodes.Add(tn);
      }
      tv.OnItemClicked+=new Eventhandler(OnItemClicked);
      tv.ContextMenu = mats.GetContextMenu();
      mats.OnChanged+=new EventHandler(OnChanged);
   }

    private void OnItemClicked(object sender,TreeviewEventArgs e) {
      //....
    }

    private void OnChanged(object sender,EventArgs e) {
      //....
    }
    //....
}


Het nadeel hiervan is dan wel dat je voor elke combinatie tssn model en view een nieuwe controller moet schrijven, maar dat is net de volledige benadering van mvc 8)
Vandaar dat mijn aanpak werkt volgens het principe : 1 controller voor treeview, en alle objecten moeten de ITreeNode interface ondersteunen.

[edit]

Subscriben bij je Model_OnChange event om wijzigingen door te geven aan je treeview via de controller.

[ Voor 11% gewijzigd door D4Skunk op 26-04-2005 14:28 ]


Acties:
  • 0 Henk 'm!

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
De view en de controller zijn nauw verweven, dus volgens mij kun je het gewoon doen zoals je al zei. Het is bij MVC ook een beetje eigen voorkeur wat je wel en niet in de V of in de C stopt.
Vanuit Java Swing (waarvandaan ik het ken) moet je aan al je events een eventhandler hangen en dat is dan je Controller. Bij .NET spullen weet ik niet of een dergelijke scheiding zo duidelijk te maken valt.
Zolang je View zich bezig houdt met het weergeven van veranderingen in je model en je Controller zich bezig houdt met het verwerken van veranderingen van de gebruiker zit je dus altijd in de goede richting.

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Mijn suggestie lijkt erg overeen te komen met het MGM-pattern:

Afbeeldingslocatie: http://www.atug.com/andypatterns/images/Image10.gif

@D4Skunk: Ik mis nog de 'View' in je overzicht, en de vraag waarom de Controller los staat van de TreeView. Gezien deze 1-op-1 is, zou je kunnen zeggen dat de TreeView net zo goed zélf als Controller kan fungeren?

Wat ik me dus ook afvraag is in hoeverre het MGM- niet hetzelfde is als het MVC-pattern... Is het wel een erkend pattern, of anders bekend onder de namen:
- Model-View-Graphic (MVG) [Jezequel, Train, Mingins 2000, "Design Patterns and Contracts"]
- Model-View-Presenter (MVP) [Joele Coutaz, Andy Bower (Dolphin Smalltalk) ]

[ Voor 31% gewijzigd door riezebosch op 26-04-2005 14:50 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

riezebosch schreef op dinsdag 26 april 2005 @ 14:28:@D4Skunk: Ik mis nog de 'View' in je overzicht, en de vraag waarom de Controller los staat van de TreeView. Gezien deze 1-op-1 is, zou je kunnen zeggen dat de TreeView net zo goed zélf als Controller kan fungeren?
De TreeView is je View... Een andere view zou bijvoorbeeld een datagrid kunnen zijn.

Deze relatie is net niet 1-op-1, dat is net de hele MVC-gedachte :
- Materiaal kan je weergeven in een datagrid, maar ook in een treeview. Hier heb je dus 2 verschillende controllers voor nodig.
- Een treeview en/of datagrid kan net zo goed materialen weergeven, als personen. Hier heb je dus ook weer verschillende controllers voor nodig.

Wat is dan het gevolg ?

Wanneer je wijzigingen aanbrengt in Materiaal(=model), hoef je nooit de code van je treeview aanpassen, maar misschien wel de code in de controller.
Als je dan beslist om bv. in je materiaal de property naam te laten wegvallen, kan je in de controller bepalen dat je het nummer moet weergeven ipv de naam.

Waar het uiteindelijk om gaat, is dat alle code tussen model en view volledig van elkaar gescheiden is dmv de controller.

Het materiaal (=model) mag dus nooit veranderen omdat je bv beslist om gegevens op een andere manier te gaan weergeven; daarvoor dient nl. je controller.

Een view is wel afhankelijk van de controller (niet van het model) omdat je bv om een datagrid weer te geven andere gegevens nodig hebt dan om een treeview weer te geven.

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Maar juist als je de TreeView als Controller neemt, hoef je die volgens mij niet aan te passen wanneer het Model veranderd. De Controller is ten slotte alleen maar verwantwoordelijk voor het ontvangen van gebruikersinvoer, wat prima vanuit de TreeView kan. Van hieruit stuur je of de View of het Model aan. Als hierdoor het Model gewijzigd wordt, roept deze zijn bijbehorende Views aan (één of meerdere TreeNodes of zelfs nog andere typen), welke op hun beurt zichzelf weer updaten.

Denk niet dat ik een plank voor m'n kop heb, ik ben alleen wat koppig ;)
[...]
Het materiaal (=model) mag dus nooit veranderen omdat je bv beslist om gegevens op een andere manier te gaan weergeven; daarvoor dient nl. je controller.
[...]
De Controller? Of de View...

[ Voor 17% gewijzigd door riezebosch op 26-04-2005 14:56 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

riezebosch schreef op dinsdag 26 april 2005 @ 14:55:
Maar juist als je de TreeView als Controller neemt, hoef je die volgens mij niet aan te passen wanneer het Model veranderd. De Controller is ten slotte alleen maar verwantwoordelijk voor het ontvangen van gebruikersinvoer, wat prima vanuit de TreeView kan. Van hieruit stuur je of de View of het Model aan. Als hierdoor het Model gewijzigd wordt, roept deze zijn bijbehorende Views aan (één of meerdere TreeNodes of zelfs nog andere typen), welke op hun beurt zichzelf weer updaten.
In welke class zou je deze code om gegevens te synchroniseren tussen de treeview en het model dan gaan typen ?
spoiler:
Je gaat die code dus niet gaan intypen in de form zelf, maar maakt gebruik van een controller =)
De Controller? Of de View...
De controller (en soms ook de view)

Het gaat uiteindelijk allemaal om abstractie en onderhoudbaarheid van de code. Ik denk dat je wel degelijk mvc begrijpt, maar niet goed inziet waarom het nodig is. Wanneer je een paar keer code zonder mvc (de code voor de synchronisiatie tssn gui en model zit dus gewoon in de form ingebakken, en dit voor alle objecten op het scherm) moet re-engineren, zal je imho het nut vlugger gaan inzien

Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Nou, het was dus wel mijn idee om een nieuwe class te maken (de Controller), maar deze dan direct af te leiden van TreeView. Zo kan je die class er zelf voor laten zorgen dat het Model aangeroepen wordt.

Schets:
De TreeView/Controller krijgt binnen dat het label van een TreeNode gewijzigd moet worden. Deze stuurt dat door naar het bijbehorende Model (adhv de Tag uit de betreffende TreeNode). Het Model past zichzelf aan, en gaat z'n bijbehorende Views af (waaronder dus iig de TreeNode) met het bericht dat ze zichzelf moeten updaten. Deze Views halen tot slot hun informatie uit het Model en passen zichzelf (evt) aan.

Hmmm... Ik zie nu alleen dan ook even niet hoe je meerdere Views aan een Model kan hangen. Of zijn daar per definitie dan toch ook al meerdere Controllers voor nodig?
Each view is associated with a unique controller and vice versa.
Nu ik het probeer toe te passen vraag ik me toch al af of ik het MVC nodig heb. Misschien kan ik net zo goed mijn objecten nog steeds van TreeNode afgeleid laten zijn, en dan deze doorgeven naar een onderliggende laag (naar functionaliteit die nu in de GUI zelf zit). Ok, het is niet netjes :X

[ Voor 32% gewijzigd door riezebosch op 26-04-2005 15:40 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Dit schema is ook van toepassing op een Form met eigenschappen van een bepaald object. Het Form is in dat geval de Controller. De TextBoxes op het scherm zijn de Views. En de bijbehorende data is het Model. Wanneer je het eigenschappenscherm opent, laat je vanuit de Controller alle Views updaten door de Models te triggeren (of vanuit de Controller rechtstreeks de Views, is misschien zelfs voordehandliggender). Bij het sluiten van het scherm door het drukken op de OK-knop of het drukken op de Toepassen-knop, geef je vanuit de Controller (het Form) een signaal richting de Models (welke evt de Views weer zouden kunnen triggeren).

Dwaal ik erg af? Kan iemand me dan een halt toeroepen :P

edit:
In dit verhaal gaat de quote van het vorige bericht niet meer op... Dan zouden hebben meerdere Views slechts 1 Controller hebben. Het past trouwens wel nog steeds in het MGM-pattern. Hoe langer ik daarna kijk, hoe vager ik 'm vind. Met name de naamgeving dan. Want wát is de rol van de GUI in het verhaal? Wat ís en wat dóet de Mediator? Visueel ziet het er wél goed uit.

[ Voor 24% gewijzigd door riezebosch op 26-04-2005 16:12 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

riezebosch schreef op dinsdag 26 april 2005 @ 15:30:
[...]

Nu ik het probeer toe te passen vraag ik me toch al af of ik het MVC nodig heb. Misschien kan ik net zo goed mijn objecten nog steeds van TreeNode afgeleid laten zijn, en dan deze doorgeven naar een onderliggende laag (naar functionaliteit die nu in de GUI zelf zit). Ok, het is niet netjes :X
Veronderstel dat ik een form heb met daarop een treeview: treeView1.
Dan kan ik nu zeggen : mtvc =new MateriaalTreeviewController(treeView1,Materialen);
Wanneer een gebruiker daarna kiest om personen weer te geven kan ik diezelfde treeview hergebruiken : ptvc = new PersoonTreeviewController(treeView1, Personen);
Dus : de view blijft dezelfde (boomstructuur) , maar de gegevens en events erachter veranderen.
Mocht je in plaats van een controller een afgeleide klasse van Treeview gebruiken, dan is dit onmogelijk.
Dit schema is ook van toepassing op een Form met eigenschappen van een bepaald object.
Veronderstel dat je een form hebt dat een treeview weergeeft met alle materialen links, en rechts een detailscherm van het geselecteerde materiaal.
Later beslis je dat je in je programma nog een andere form wenst te maken, met links een keuzelijst met materialen, en rechts terug een treeview met alle onderliggende materialen van diegene die links geselecteerd is. De aanpak 'code in de form' zorgt er voor dat je dan al die code voor de treeview moet dupliceren voor beide forms.

Het gebruik van een controller is dus de oplossing voor beide hierboven genoemde problemen.

pfft, nooit gedacht dat het zo moeilijk was om het waarom van mvc uit te leggen :)

[ Voor 3% gewijzigd door D4Skunk op 26-04-2005 16:23 ]


Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Bovengenoemde is dus met een afgeleide class van TreeView ook mogelijk ;)

treeView1 = new MijnControllerWelkeEenAfgeleideTreeViewIs();

Sowieso hoor je dus voor elke View een aparte Controller te maken. Omdat die alleen weet wat en hoe het aangestuurd moet worden...

[ Voor 43% gewijzigd door riezebosch op 26-04-2005 16:49 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 26-07 16:22

D4Skunk

Kind of Blue

riezebosch schreef op dinsdag 26 april 2005 @ 16:33:
Bovengenoemde is dus met een afgeleide class van TreeView ook mogelijk ;)

treeView1 = new MijnAfgeleideTreeViewMetIngebouwdeController();

Sowieso hoor je dus voor elke View een aparte Controller te maken. Omdat die alleen weet wat en hoe het aangestuurd moet worden...
Inderdaad. Jouw code
C#:
1
2
3
4
5
6
// roep constructor treeview +constructor afgeleideklasse aan
treeView1 = new MijnAfgeleideTreeViewMetIngebouwdeController();

// roep destructor treeview +destructor afgeleideklasse aan
// roep constructor treeview +constructor afgeleideklasse2 aan
treeView1 = new MijnAfgeleideTreeViewMetIngebouwdeController2();


Mijn code :
C#:
1
2
3
treeView1 = new TreeView()
tvc=new TreeViewController1(treeView1,model1);
tvc=new TreeViewController2(treeView1,model2);


Je code is dus minder efficiënt. (treeview wordt gedestruct en terug geconstruct, en dan wordt er nog geen rekening gehouden met PerformLayout etc...).

Dat is dus net hetgene aan patterns wat voor sommigen iets moeilijker te vatten is : er is altijd wel een andere manier om het te doen, maar het pattern is de beste manier.

Een pattern zou je kunnen vergelijken met een wiskundige methode om een probleem op te lossen : wie de naam kent, weet meestal waarvoor de methode gebruikt wordt en hoe men ze moet uitvoeren, zonder het waarom ervan te weten.
Je moet nl. het wiskundig bewijs niet kennen om de methode te kunnen toepassen.

Een voorbeeldje van wat een pattern zou kunnen zijn :

Geef mij de som van alle getallen van 17 tot 123, dus
17+18+19+...+120+121+122+123 = ?

Een pattern hiervoor zou zijn :
spoiler:
(123+17)*(123-17+1)/2


De logica achter het pattern is als volgt :
spoiler:
=som(17+123 = 140,18+122=140, 19+121 = 140,... ) => in totaal (123-17+1)/2 keer


Je kan het dus perfect oplossen zonder het pattern, allen is het pattern beter om het probleem op te lossen

[ Voor 14% gewijzigd door D4Skunk op 26-04-2005 17:04 ]

Pagina: 1