2010-08-14 4 views
5

Ho due domande.Metodo di sovrascrittura C# in Runtime

1) Ho trovato un piccolo gioiello di codice per how to make a control scroll smoothly.

Grande. Ma sovrascrive il metodo WndProc, quindi per usarlo, dovevo strappare il FlowLayoutPanel che avevo lasciato sul modulo in fase di progettazione, sottoclassi FlowLayoutPanel, e infine creare un'istanza della mia nuova classe e creare manualmente tutte le proprietà e modificare tutti i riferimenti al controllo per essere questo.Controls ["ControlName"]. (O immagino di poter creare una variabile a livello di classe che è essenzialmente ciò che originariamente era il controllo, anche se come ti permettono di usare intellisense su di esso quando non è dichiarato da nessuna parte?)

Quindi ora mi chiedo se c'era in effetti un modo runtime per farlo.

Posso fare qualcosa di semplice come questo, in cui MainPanel è il nome del controllo:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel 

Non può essere così facile, vero? Anche così, è fastidioso perché devo ancora avere la sottoclasse (che potrebbe essere una buona decisione di progettazione, ma mi piacerebbe la libertà di renderla unica). Quindi sarebbe possibile mettere il codice nel genitore del qualcosa FlowLayoutPanel come questo:

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); 
    } 

Mi rendo conto che questo è probabilmente abbastanza ingenuo. Sto trattando il metodo WndProc come se fosse un evento, e non lo è.

2) Così poi la mia seconda domanda è, se WndProc era un evento invece di un metodo, come vorrei fare la stessa cosa punto vendita una copia della lista originale dei gestori per un evento, installare il mio proprio evento gestore di eseguire prima, quindi chiamare tutti i gestori di eventi originali?

BITS SAPORITI

Nel caso in cui qualcuno è interessato ho notato un'ottimizzazione possibile nel codice scorrimento continuo:

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

Dal momento che vogliamo trasformare gli ultimi 16 bit 5-4, abbiamo può semplicemente capovolgere l'ultimo bit (XOR) piuttosto che AND poi OR.

risposta

8

Se ho capito bene la tua domanda, tutto ciò che vuoi fare è sovrascrivere WndProc in fase di esecuzione. Se è così, tutto ciò che serve è una piccola magia Win32.

Ogni controllo ha un "handle" che lo identifica in modo che il sistema operativo possa inviarvi messaggi. Questo handle è esposto tramite la proprietà Handle su ogni controllo. Il sistema di Win32 sottostante in realtà consente di ascoltare il controllo di qualsiasi WndProc finché si ha il suo handle. Ciò significa che non è necessario ereditare da un controllo Winforms per modificare il comportamento di Win32. System.Windows.Forms.NativeWindow in .NET avvolge questa funzionalità sottostante.

Ecco un esempio di come si potrebbe raggiungere questo obiettivo:

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); 
    } 
} 

Poi nel codice dietro, collegare l'intercetta al controllo:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle); 

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

Grazie Zach, ma ho già compiuto facendo scorrere le finestre senza problemi. Ho solo odiato il modo in cui dovevo strappare il controllo al designer e fare tutto dinamicamente. – ErikE

+0

@Emtucifor: Nessun problema, volevo solo farti sapere che non dovevi ereditare da 'FlowLayoutPanel' per sovrascrivere il suo' WndProc'. –

+0

Oh, ora capisco. Grazie. Confesso che non ho letto con la stessa attenzione che avrei dovuto. Questo è utile! In effetti, hai ragione al 100%, è la risposta che stavo cercando. (sa di fronte) – ErikE

2

No, quello che stai chiedendo è impossibile. Dovrai creare una sottoclasse come hai fatto prima.

Anche se fosse un evento, non saresti in grado di fare quello che cerchi. L'interfaccia pubblica per un evento espone solo add e remove; non c'è modo di ottenere o assegnare i delegati effettivi associati all'evento.

Tuttavia, guardando il problema da un diverso punto di vista , si può essere in grado di fare uso dell'interfaccia IMessageFilter al fine di realizzare il risultato finale che stai cercando.

Modifica

Dopo aver guardato il codice di nuovo, IMessageFilter non funziona, in quanto non è possibile modificare il messaggio all'interno PreFilterMessage; puoi solo esaminarlo o sopprimerlo.La tua migliore scommessa a questo punto è di ignorare WndProc nel genitore Form e provare a fare le tue manipolazioni lì. Non sembra che ci sia una soluzione generica al tuo problema.

+0

Grazie. Conosco un po 'di javascript dove puoi farlo con i metodi, quindi ero solo curioso. Ora mi viene in mente, posso ereditare sia un FlowLayoutPanel sia un UserControl in modo che il mio nuovo SmoothScrollingFlowLayoutPanel diventi un elemento che posso rilasciare sul modulo in fase di progettazione? – ErikE

+0

@Emtucifor: No, nessuno dei linguaggi .NET consente l'ereditarietà multipla. Tuttavia, puoi ereditare da "FlowLayoutPanel" e mettere il tuo controllo nella casella degli strumenti. Se il controllo è nel tuo progetto, dovrebbe apparire lì automaticamente. Se si trova in un altro progetto, devi fare clic con il pulsante destro del mouse sulla casella degli strumenti, fare clic su "Scegli elementi ...", quindi cercare quella .dll e aggiungere il controllo in questo modo. –

+0

Questo è stato utile. Sapevo che gli UserControls sottoclassi sarebbero visualizzati nella casella degli strumenti, ma in qualche modo mancavano che anche i controlli regolari sottoclasse apparirebbero lì. Grazie. – ErikE