2010-08-14 10 views
5

J'ai deux questions.Méthode de remplacement C# à l'exécution

1) J'ai trouvé un petit bijou de code pour how to make a control scroll smoothly.

Très bien. Mais il remplace la méthode WndProc, donc pour l'utiliser, j'ai dû supprimer le FlowLayoutPanel que j'avais déposé sur le formulaire au moment du design, sous-classe FlowLayoutPanel, puis enfin instancier ma nouvelle classe et créer toutes les propriétés manuellement et changer toutes les références au contrôle pour être this.Controls ["ControlName"]. (Ou je suppose que je pourrais faire une variable de niveau de classe qui est essentiellement ce que le contrôle était à l'origine, mais comment vous permettent-ils d'utiliser intellisense quand il n'est pas déclaré?)

Alors maintenant je me demande si il y avait en fait une façon d'exécuter pour le faire.

Puis-je faire quelque chose de simple comme cela, où MainPanel est le nom du contrôle:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel 

Il ne peut pas être facile, peut-il? Même ainsi, c'est ennuyeux parce que je dois encore avoir la sous-classe (ce qui peut être une bonne décision de conception mais j'aimerais la liberté de le faire une seule fois). Serait-il possible de mettre le code dans le parent de quelque chose comme FlowLayoutPanel ceci:

private Delegate void WndProcHandler(ref Message m); 
private WndProcHandler w; 

public void SomeCode() { 
    w = MainPanel.WndProc; // get reference to existing wndproc method 
    MainPanel.WndProc = WndProcSmoothScroll; //replace with new method 
} 

private void WndProcSmoothScroll(ref Message m) { // make smooth scrolling work 
    if (
     (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) 
     && (((int)m.WParam & 0xFFFF) == 5) 
    ) { 
     m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
    } 
    if (w != null) { w(); } 
    base.WndProc(ref m); 
    } 

Je sais que cela est probablement assez naïve. Je traite la méthode WndProc comme si c'était un événement, et ce n'est pas le cas.

2) Alors ma deuxième question est, si WndProc était un événement au lieu d'une méthode, comment pourrais-je faire la même chose magasin une copie de la liste originale des gestionnaires pour un événement, installer mon propre événement gestionnaire d'exécuter d'abord, puis appeler tous les gestionnaires d'événements d'origine?

BITS SAVOUREUSES

Dans le cas où quelqu'un est intéressé, j'ai remarqué une optimisation possible dans le code de défilement régulier:

//m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
m.WParam = (IntPtr)((int)m.WParam^1); 

Puisque nous voulons tourner les 16 derniers bits de 5 à 4, nous peut simplement retourner le dernier bit (XOR) plutôt que ET alors OU.

Répondre

8

Si je comprends bien votre question, tout ce que vous voulez faire est de surcharger WndProc lors de l'exécution. Si oui, tout ce dont vous avez besoin est un peu de magie Win32.

Chaque contrôle a un "handle" qui l'identifie pour que le système d'exploitation puisse lui envoyer des messages. Cette poignée est exposée à travers la propriété Handle sur chaque contrôle. Le système Win32 sous-jacent vous permet réellement d'écouter les WndProc de n'importe quel contrôle aussi longtemps que vous avez son handle. Cela signifie que vous n'avez pas besoin d'hériter d'un contrôle Winforms pour modifier son comportement Win32. System.Windows.Forms.NativeWindow dans .NET encapsule cette fonctionnalité sous-jacente.

Voici un exemple de la façon dont vous pourriez y arriver:

class SmoothScrollIntercept : System.Windows.Forms.NativeWindow 
{ 
    public SmoothScrollIntercept(IntPtr hWnd) 
    { 
     // assign the handle and listen to this control's WndProc 
     this.AssignHandle(hWnd); 
    } 

    protected override void WndProc(ref Message m) 
    { 
     // listen to WndProc here, do things 

     if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) 
      && (((int)m.WParam & 0xFFFF) == 5)) 
     { 
      m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4); 
     } 

     base.WndProc(ref m); 
    } 
} 

Puis dans le code derrière, attach l'interception au contrôle:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle); 

// myControl is now using smooth scrolling, without inheriting from the control 
+0

Merci Zach, mais j'ai déjà réussi à faire défiler mes fenêtres en douceur. Je détestais juste comment je devais arracher le contrôle du concepteur et tout faire dynamiquement. – ErikE

+0

@Emtucifor: Pas de problème, je voulais juste vous faire savoir que vous n'aviez pas besoin d'hériter de 'FlowLayoutPanel' pour remplacer son' WndProc'. –

+0

Oh, maintenant je comprends. Merci. J'avoue que je n'ai pas lu aussi soigneusement que je devrais l'avoir fait. C'est utile! En fait, vous avez raison à 100%, c'est la réponse que je cherchais. (sent le front) – ErikE

2

Non, ce que vous demandez est impossible. Vous devrez sous-classer comme vous l'avez fait avant.

Même s'il s'agissait d'un événement, vous ne seriez pas en mesure de faire ce que vous cherchez. L'interface publique d'un événement n'expose que add et remove; il n'y a aucun moyen d'obtenir ou d'assigner le (s) délégué (s) réel (s) attaché (s) à l'événement.

Cependant, en regardant le problème d'une autre point de vue, vous pourriez être en mesure d'utiliser l'interface IMessageFilter afin d'atteindre la fin résultat que vous recherchez.

Modifier

Après avoir regardé à nouveau votre code, IMessageFilter ne fonctionnera pas, comme vous ne pouvez pas modifier le message au sein PreFilterMessage; vous pouvez seulement l'examiner ou le supprimer.Votre meilleur pari à ce stade est de remplacer WndProc dans le parent Form et essayez d'y faire vos manipulations. Il ne semble pas qu'il y ait une solution générique à votre problème.

+0

Merci. Je sais un peu de javascript où vous pouvez faire cela avec des méthodes, donc j'étais juste curieux. Maintenant, il me vient à l'esprit, puis-je hériter à la fois d'un FlowLayoutPanel ET d'un UserControl pour que mon nouveau SmoothScrollingFlowLayoutPanel devienne un élément que je peux laisser tomber sur le formulaire au moment du design? – ErikE

+0

@Emtucifor: Non, aucun des langages .NET n'autorise l'héritage multiple. Vous pouvez cependant hériter de 'FlowLayoutPanel' et placer votre contrôle dans la boîte à outils. Si le contrôle est dans votre projet, il devrait apparaître automatiquement. Si c'est dans un autre projet, vous devrez cliquer avec le bouton droit sur la boîte à outils, cliquez sur "Choisir les éléments ...", puis naviguez jusqu'à ce fichier .dll et ajoutez le contrôle de cette façon. –

+0

C'était utile. Je savais que UserControls sous-classé montrerait dans la boîte à outils, mais en quelque sorte manqué que les contrôles réguliers sous-classés apparaissent aussi là. Merci. – ErikE

Questions connexes