Comment faire défiler un élément de DataGridView à la fois à l'aide de la molette de la souris?
-
02-07-2019 - |
Question
Nous aimerions remplacer le comportement par défaut de DataGridView lors de l'utilisation d'une molette de la souris avec ce contrôle. Par défaut, DataGridView fait défiler un nombre de lignes égal au paramètre SystemInformation.MouseWheelScrollLines. Ce que nous aimerions faire, c'est faire défiler un élément à la fois.
(Nous affichons dans le DataGridView des images qui sont un peu volumineuses. En raison de ce défilement, trois lignes (un paramètre système typique) est trop, ce qui amène souvent l'utilisateur à faire défiler les éléments qu'il ne peut même pas voir.)
J'ai déjà essayé plusieurs choses et je n'ai pas eu beaucoup de succès jusqu'à présent. Voici quelques problèmes que j'ai rencontrés:
-
Vous pouvez vous abonner à des événements MouseWheel mais vous ne pouvez pas marquer l'événement comme étant géré et faire ce que je veux.
-
Vous pouvez remplacer OnMouseWheel mais cela ne semble jamais être appelé.
-
Vous pourrez peut-être corriger cela dans le code de défilement de base, mais cela semble être un travail fastidieux, car d'autres types de défilement (par exemple, au clavier) passent par le même pipeline.
Quelqu'un a-t-il une bonne suggestion?
Voici le code final, en utilisant la merveilleuse réponse donnée:
/// <summary>
/// Handle the mouse wheel manually due to the fact that we display
/// images, which don't work well when you scroll by more than one
/// item at a time.
/// </summary>
///
/// <param name="sender">
/// sender
/// </param>
/// <param name="e">
/// the mouse event
/// </param>
private void mImageDataGrid_MouseWheel(object sender, MouseEventArgs e)
{
// Hack alert! Through reflection, we know that the passed
// in event argument is actually a handled mouse event argument,
// allowing us to handle this event ourselves.
// See http://tinyurl.com/54o7lc for more info.
HandledMouseEventArgs handledE = (HandledMouseEventArgs) e;
handledE.Handled = true;
// Do the scrolling manually. Move just one row at a time.
int rowIndex = mImageDataGrid.FirstDisplayedScrollingRowIndex;
mImageDataGrid.FirstDisplayedScrollingRowIndex =
e.Delta < 0 ?
Math.Min(rowIndex + 1, mImageDataGrid.RowCount - 1):
Math.Max(rowIndex - 1, 0);
}
La solution
Je viens juste de fouiller un peu et d’essayer moi-même. J'ai utilisé Reflector pour enquêter et découvrir deux choses. L'événement MouseWheel
fournit un paramètre MouseEventArgs
, mais le remplacement de OnMouseWheel ()
dans DataGridView
le transfère vers . Géré
MouseEventArgs
. Cela fonctionne également lors de la gestion de l'événement MouseWheel
. OnMouseWheel ()
est effectivement appelé et c’est dans la substitution de DataGridView
qu’il utilise SystemInformation.MouseWheelScrollLines
.
Donc:
-
Vous pouvez en effet gérer l'événement
MouseWheel
, en convertissantMouseEventArgs
enHandledMouseEventArgs
et en définissantHandled = true
puis faites ce que vous voulez. -
Sous-classe
DataGridView
, remplacez vous-mêmeOnMouseWheel ()
et essayez de recréer tout le code que j'ai lu ici dans Reflector sauf pour remplacerSystemInformation.MouseWheelScrollLines
par1
.
Ce dernier serait très pénible car il utilise un certain nombre de variables privées (y compris des références aux ScrollBar
s) et vous devez en remplacer certaines par les vôtres et en obtenir / définir d’autres à l’aide de Reflection. .
Autres conseils
Je voudrais sous-classer DataGridView dans mon propre contrôle personnalisé (vous savez, ajouter un nouveau fichier Windows Forms - > Contrôle personnalisé et modifier la classe de base de Control à DataGridView).
public partial class MyDataGridView : DataGridView
Redéfinissez ensuite la méthode WndProc et remplacez-la de la manière suivante:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x20a)
{
int wheelDelta = ((int)m.WParam) >> 16;
// 120 = UP 1 tick
// -120 = DOWN 1 tick
this.FirstDisplayedScrollingRowIndex -= (wheelDelta / 120);
}
else
{
base.WndProc(ref m);
}
}
Bien sûr, vous aurez la possibilité de ne pas définir FirstDisplayedScrollingRowIndex sur un nombre en dehors de la plage de votre grille, etc. Mais cela fonctionne très bien!
Richard
Remplacer OnMouseWheel et ne pas appeler base.OnMouseWheel devrait fonctionner. Certaines souris à molette ont des réglages spéciaux que vous devrez peut-être définir vous-même pour que cela fonctionne correctement. Voir ce message http://forums.microsoft.com/MSDN/ShowPost .aspx? PostID = 126295 & SiteID = 1
UPDATE: Depuis que je sais maintenant que DataGridView
a un événement MouseWheel
, j'ai ajouté un deuxième remplacement plus simple.
Pour ce faire, vous pouvez sous-classer le DataGridView
et remplacer le WndProc
pour ajouter un traitement spécial du message WM_MOUSEWHEEL
.
Cet exemple intercepte le mouvement de la molette de la souris et le remplace par un appel à SendKeys.Send
.
(Cela diffère légèrement du défilement, car il sélectionne également la ligne suivante / précédente du DataGridView
. Mais cela fonctionne.)
public class MyDataGridView : DataGridView
{
private const uint WM_MOUSEWHEEL = 0x20a;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
var wheelDelta = ((int)m.WParam) >> 16;
if (wheelDelta < 0)
{
SendKeys.Send("{DOWN}");
}
if (wheelDelta > 0)
{
SendKeys.Send("{UP}");
}
return;
}
base.WndProc(ref m);
}
}
2e prise (avec les mêmes mises en garde que mentionnées ci-dessus):
public class MyDataGridView : DataGridView
{
protected override void OnMouseWheel(MouseEventArgs e)
{
if (e.Delta < 0)
SendKeys.Send("{DOWN}");
else
SendKeys.Send("{UP}");
}
}