2009-12-15 4 views
4

J'essaie d'utiliser le modèle Model-View-Controller dans une petite application. Le modèle contient des données et une sélection comme ceciComment éviter les notifications circulaires dans MVC dans Delphi?

TModelSelection = record 
    CurrentItem : TItem; 
end; 

TModel = class 
public 
    property Items : TList <TItem>; 
    property Selection : TModelSelection; 
    property Subject : TSubject <TModel>; // Observer pattern 
end; 

Maintenant, j'ai une vue arborescente qui observe le modèle. Si l'utilisateur sélectionne un élément dans l'arborescence, la sélection du modèle doit changer.

Le problème est que je rencontre des problèmes avec les notifications de changement circulaire: Je change la sélection de modèle dans l'événement OnChange de l'arborescence. Cela provoque la mise à jour de la sélection de l'arborescence (puisque la sélection peut également être modifiée par d'autres parties de l'application), ce qui déclenche à nouveau l'événement OnChange et ainsi de suite.

Comment puis-je éviter ce problème?

Répondre

9

notifier uniquement les véritables modifications.

Ou utilisez un indicateur pour désactiver les mises à jour pendant les mises à jour.

procedure OnChange(...) 
begin 
    if FChanging = false then 
    begin 
    FChanging:=true; 
    ... do updates 
    FChanging:=false; 
    end; 
end; 

avec FChanging étant une variable membre de type booléen

+1

+1 merci! Utiliser le drapeau le fait pour moi. – jpfollenius

+0

Je suis content d'avoir pu aider. Une fois que vous avez vu ce modèle, c'est assez évident. C'est souvent utile. –

+0

Pas mal, mais ... 'sinon FChanging alors essayez FChanging: = TRUE; ... enfin FChanging: = FALSE; fin; 'me semble mieux :-) – JensG

8

Un problème classique avec MVC - quand un changement dans la vue affecte le modèle et quand il simplement reflète le modèle?

Ceci devrait être traité par le contrôleur - s'il n'est pas manipulé par le contrôleur alors vous n'avez pas vraiment une implémentation de MVC mais juste un MV avec la logique de contrôleur intégrée et distribuée pendant les interactions entre le modèle et la vue. Lorsque l'utilisateur interagit avec View the View (contrôle TreeView), il doit le notifier au contrôleur qui, à son tour, met le modèle à jour.

Lorsque le modèle est mis à jour, il doit informer le contrôleur.

Dans ce scénario, le contrôleur est déjà conscient de la mise à jour du modèle et peut donc ne pas tenir compte de certaines notifications qu'il transmettrait à la vue.

Le plus gros problème auquel vous êtes confronté est que les contrôles utilisés dans votre vue ne sont pas idéalement conçus pour être utilisés dans une implémentation MVC. Cliquer dans une vue d'arbre change la sélection dans le contrôle treeview (View) lui-même. Dans un MVC, idéalement, ce que vous voulez faire, c'est notifier le Modèle (via le Contrôleur) que la sélection doit être changée en l'élément cliqué. Par conséquent, l'état Sélection du modèle change et l'arborescence est notifiée. Aucune vue ne devrait jamais supposer qu'elle sait déjà ce qu'elle fait au modèle.

L'état de sélection de Treeview n'est alors toujours que le reflet de l'état du modèle. Mais sans travailler dans les commandes de l'interface utilisateur pour créer cette distance entre l'état du contrôle et l'état du modèle sous-jacent, vous allez avoir à faire face à des contournements.

+0

+1 merci pour l'explication. Je me rends compte que ce n'est pas une solution propre MVC et qu'une solution propre dans Delphi signifie probablement beaucoup de travail ... – jpfollenius

0

Stocke la sélection en cours dans le calque d'affichage (par exemple, noeud sélectionné actuel). Si la vue est demandée par le contrôleur pour passer au nouveau nœud, et que ce nœud est déjà sélectionné, ne rien faire.

Comportement standard des composants delphi lorsque la propriété est définie:

type TSomeClass = class 
    private 
    FSomeValue : TSomeType; 
    protected 
    procedure SetSomeProperety(const AValue: TSomeType); 
    public 
    property SomeProperty : TSomeType read FValue write SetSomeProperty; 
end; 

procedure TSomeClass.SetSomeProperety(const AValue: TSomeType); 
begin 
    if(AValue = FValue) then exit; 
    FValue := AValue; 
    // do other actions to reflect change 
end; 

Way avec « Changer Etat » par Tobias Langner pas bon - contrôleur peut décider de sélectionner un autre noeud, et au moins besoin

try 
    ... 
finally 
    FChanging := false; 
end; 

bloc.

Questions connexes