Comment puis-je en toute sécurité remplir avec les données et de les Actualiser() un DataGridView dans une application multi-thread?
Mon application a un DataGridView objet et une Liste de type MousePos. MousePos est une classe personnalisée qui tient la souris coordonnées X,Y (de type "Point") et un nombre de cette position. J'ai un fil (le Système.Les minuteries.Timer) qui déclenche un événement, une fois par seconde, vérifie la position de la souris, ajoute et/ou mises à jour le comte de la position de la souris sur cette Liste.
Je voudrais avoir un semblable thread en cours d'exécution (encore une fois, je pense que le Système.Les minuteries.La minuterie est un bon choix), qui serait nouveau soulever un événement une fois de plus une seconde pour Actualiser automatiquement() le DataGridView, de sorte que l'utilisateur peut voir les données sur l'écran de mise à jour. (comme le gestionnaire de tâches.)
Malheureusement, l'appel de la DataGridView.Méthode Refresh() résultats dans VS2005 l'arrêt de l'exécution, et en notant que j'ai couru en croix-threading situation.
Si je suis bien comprendre, j'ai 3 fils maintenant:
- Primaire thread d'INTERFACE utilisateur
- MousePos fil de la Liste (Timer)
- DataGridView thread d'Actualisation (Timer)
Pour voir si je pouvais Refresh (), le DataGridView sur le thread principal, j'ai ajouté un bouton au formulaire qui appelle DataGridView.Refresh(), mais cette (étrangement) n'a rien fait. J'ai trouvé un sujet qui semble indiquer que si j'ai mis DataGridView.DataSource = null et de retour à ma Liste, que ce serait l'actualisation de la grille de données. Et en effet cela a fonctionné, mais seulement à travers le bouton (qui est traitée sur le thread principal.)
Ainsi, cette question s'est transformée en deux parties:
- Est le réglage de DataGridView.Source de données à null et de retour à ma Liste une façon acceptable d'actualisation de la grille de données? (Il semble inefficace pour moi...)
- Comment je peux faire cela dans un environnement multi-thread?
Voici le code que j'ai écrit jusqu'à présent (C#/.Net 2.0)
public partial class Form1 : Form
{
private static List<MousePos> mousePositionList = new List<MousePos>();
private static System.Timers.Timer mouseCheck = new System.Timers.Timer(1000);
private static System.Timers.Timer refreshWindow = new System.Timers.Timer(1000);
public Form1()
{
InitializeComponent();
mousePositionList.Add(new MousePos()); //ANSWER! Must have at least 1 entry before binding to DataSource
dataGridView1.DataSource = mousePositionList;
mouseCheck.Elapsed += new System.Timers.ElapsedEventHandler(mouseCheck_Elapsed);
mouseCheck.Start();
refreshWindow.Elapsed += new System.Timers.ElapsedEventHandler(refreshWindow_Elapsed);
refreshWindow.Start();
}
public void mouseCheck_Elapsed(object source, EventArgs e)
{
Point mPnt = Control.MousePosition;
MousePos mPos = mousePositionList.Find(ByPoint(mPnt));
if (mPos == null) { mousePositionList.Add(new MousePos(mPnt)); }
else { mPos.Count++; }
}
public void refreshWindow_Elapsed(object source, EventArgs e)
{
//dataGridView1.DataSource = null; //Old way
//dataGridView1.DataSource = mousePositionList; //Old way
dataGridView1.Invalidate(); //<= ANSWER!!
}
private static Predicate<MousePos> ByPoint(Point pnt)
{
return delegate(MousePos mPos) { return (mPos.Pnt == pnt); };
}
}
public class MousePos
{
private Point position = new Point();
private int count = 1;
public Point Pnt { get { return position; } }
public int X { get { return position.X; } set { position.X = value; } }
public int Y { get { return position.Y; } set { position.Y = value; } }
public int Count { get { return count; } set { count = value; } }
public MousePos() { }
public MousePos(Point mouse) { position = mouse; }
}
OriginalL'auteur Pretzel | 2008-11-03
Vous devez vous connecter pour publier un commentaire.
Vous devez mettre à jour la grille sur le main thread d'INTERFACE utilisateur, comme tous les autres contrôles. Voir contrôle.Invoquer ou de Contrôle.BeginInvoke.
OriginalL'auteur Grzenio
Mise à JOUR! -- Je partiellement trouvé la réponse à partie #1 dans le livre "les Pro .NET 2.0 Windows Forms et de la Clientèle de Contrôle en C#"
J'avais initialement pensé que Refresh() n'a rien fait et que je devais appeler le Invalidate() méthode, pour dire à Windows de repeindre mon contrôle à sa guise. (ce qui est généralement tout de suite, mais si vous avez besoin d'une garantie pour la repeindre maintenant, puis un suivi avec un appel immédiat à la méthode Update ().)
Mais, il s'avère que le Refresh() méthode est simplement un alias pour:
Le seul problème que j'ai trouvé avec ce que si il n'y a pas de données dans le dataGridView, aucun montant de la invalider serait actualiser le contrôle. J'ai dû réaffecter la source de données. Puis il a bien fonctionné par la suite. Mais seulement pour le nombre de lignes (ou des éléments dans ma liste) -- Si de nouveaux articles ont été ajoutés, le dataGridView serait pas au courant qu'il y avait plus de lignes à afficher.
Il semble donc que, lors de la liaison d'une source de données de type Liste (ou Tableau) de la source de données, le dataGridView compte les éléments (lignes) et puis définit cette en interne et ne vérifie jamais pour voir si il y a de nouvelles lignes/éléments ou de lignes/postes supprimés. C'est pourquoi re-liaison de la source de données à plusieurs reprises travaillait avant.
Maintenant de comprendre comment mettre à jour le nombre de lignes à afficher dans dataGridView sans avoir à re-lier à la source de données... fun, fun, fun! 🙂
Après avoir fait quelques recherches, je pense avoir ma réponse à partie #2 de ma question (aka. sécurité Multi-threading):
Plutôt que d'utiliser Système.Les minuteries.Minuterie, j'ai trouvé que je devrais être à l'aide de Système.De Windows.Les formulaires.Minuterie à la place.
L'événement se produit, telles que la méthode qui est utilisée dans la fonction de Rappel automatiquement arrive sur le thread principal. Pas de croix-threading questions!
La déclaration ressemble à ceci:
Et la méthode est comme ceci:
OriginalL'auteur Pretzel
Semble que vous avez votre réponse!
Juste en cawse vous êtes curieux de savoir comment le faire de la croix-thread appelle à l'interface utilisateur:
Tous les contrôles ont une Invoke (), méthode (ou BEginInvoke()- dans le cas où vous voulez faire les choses de manière asynchrone), ce est utilisé pour appeler une méthode sur le contrôle dans le contexte de la principale thread d'INTERFACE utilisateur.
Donc, si vous allez appeler votre datagridview à partir d'un autre thread, vous devrez effectuer les opérations suivantes:
OriginalL'auteur Fredrik Bonde