2010-02-14 5 views
1

Je cours une importation de données, en utilisant un formulaire Windows pour lancer l'importation et montrer la progression. J'ai tout cela si gentil et convivial, avec des barres de progression majeures et mineures et tout ... mais juste un problème ... le rafraîchissement de la forme continue d'aller AWOL.Pourquoi Form.Refresh() ne fonctionne pas?

J'ai un appel à Form.Refresh() chaque fois que je mets à jour mes étiquettes/barres de progression, et cela commence habituellement à fonctionner. Mais si jamais j'ai besoin de passer en mode débogage, juste pour maintenir l'importation, l'appel Refresh() arrête de fonctionner, et parfois même si je cours sans mode Débogage, à un moment imprévisible, la même chose arrive: les étiquettes et les barres de progression ne sont pas mises à jour, et si vous masquez le formulaire et le rouvrez, le formulaire ne sera pas du tout repeint - il apparaîtra tout à fait blanc. Pourquoi, oh pourquoi, Form.Refresh() ne fonctionne plus, et comment puis-je résoudre ce problème?

+0

Que voulez-vous dire par "ne fonctionne pas"? –

+0

Je viens de mettre à jour la question pour plus de clarté. –

Répondre

9

Il semble que l'importation fonctionne sur le thread d'interface utilisateur, ce qui signifie que ce fil est bloqué, ce qui empêche la forme de se repeindre. Une meilleure approche consiste à utiliser un composant BackgroundWorker, à effectuer l'importation dans le gestionnaire d'événements DoWork et à utiliser le ProgressChanged pour mettre à jour l'interface utilisateur.

Exemple:

private void StartImport() 
{ 
    backgroundWorker.WorkerReportsProgress = true; 
    backgroundWorker.RunWorkerAsync(); 
} 

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    // do some work simulating a lenghy process which occasionally 
    // reports progress with data back to the caller 
    for (int i = 0; i < 100; i++) 
    { 
     Thread.Sleep(200); 
     backgroundWorker.ReportProgress(i, "Item No " + i.ToString()); 
    } 
} 

private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    listBox.Items.Add(e.UserState.ToString()); 
} 

En utilisant cette approche, vous aurez généralement pas besoin d'appeler Refresh pour forcer un repeindre de la forme.

+0

+1 + répondre crédit - spot on - merci! –

0

Vous pouvez modifier votre code en utilisant BeginUpdate et EndUpdate, comme ceci:

Control.BeginUpdate(); 
// Do something to the control, e.g. add items or whatnot 
Control.EndUpdate(); 

De cette façon Refresh ne devrait pas être nécessaire.

AFAIK appelle constamment Refresh est vraiment un hack et devrait être évité, car il insiste un peu sur le CPU (il doit tout rafraîchir au lieu de simplement les choses qui sont changées). Editer: Si le formulaire commence à être blanc, il semble que le code de dessin n'a pas été appelé du tout, ce qui indique qu'il ne répond pas du tout.

Je vérifierais le code pour tout ce qui peut bloquer ou autrement suspendre.

+0

Aucune méthode telle que BeginUpdate ou EndUpdate sur un contrôle C# ...? –

+0

Je pense que c'est 'myControl.SuspendLayout();' et 'myControl.ResumeLayout();' – herzmeister

+0

Il n'est pas disponible sur tous les contrôles non, mais il est généralement pour les contrôles qui peuvent avoir des enfants. Par exemple. Listview. Pourriez-vous poster le code où vous appelez Actualiser? – Steffen

0

Vous pouvez utiliser observateur pattern..in court si quelque chose change dans le profil d'observateur modèle fera en sorte que le changement est visible sur la forme ..

google pour quelques exemples ..

+0

S'il vous plaît clarifier ce que vous voulez dire. Quel modèle change? Pourquoi pensez-vous qu'un modèle d'observateur est indiqué ici? C'est juste un indicateur de progression montrant jusqu'où va l'importation ... –

+0

Je pense avoir compris ce que vous faites: Vous avez du code en boucle, et vous incrémentez la barre de progression dans la boucle. Ensuite, vous appelez Refresh pour refléter le changement de la barre de progression à l'utilisateur, non? La solution consiste à exécuter la boucle dans un thread et à supprimer simplement Refresh. De cette façon, votre thread principal (qui est responsable du dessin) sera redessiné automatiquement. N'oubliez pas d'utiliser Invoke lorsque vous modifiez la barre de progression de votre thread. – Steffen

-1

vous pourriez avoir besoin de donne le temps à la fenêtre de se redessiner. Je comprends que vous faites l'importation dans une boucle et que la boucle tourne sur le thread principal de l'interface utilisateur? Essayez d'ajouter cette ligne à la boucle:

Application.DoEvents(); 
+2

OMG c'est encore plus hackish qu'appeler Refresh tout le temps, sérieusement Sleep n'est presque jamais une solution. – Steffen

+1

D'accord avec Steffen: aussi, si l'importation est en cours d'exécution sur le thread de l'interface utilisateur (ce que j'ai l'impression que c'est), cela bloquera toujours ce thread, empêchant le redraw de se produire de toute façon. –

+0

Le commentaire de Steffen est correct. C'est horriblement hack - et ça ne marche pas non plus. Je sais, j'ai essayé ... (rougit) ... :) –

0

En fonction de ce cadre .NET que vous utilisez, vous pouvez utiliser l'approche Task.Run:

private void btnShowProgress_Click(object sender, EventArgs e) 
{ 
    progressBar1.Value = 0; 

    Task.Run(() => 
    { 
     for (int i = 0; i <= 100; i++) 
     { 
      Thread.Sleep(100); 
      progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = i; }));      
     } 
    }); 
} 

Task.Run info

Using invoke with controls

Questions connexes