Laden...

ProgressBar für Datenbankupdate anzeigen

Erstellt von rainman vor 14 Jahren Letzter Beitrag vor 14 Jahren 4.994 Views
R
rainman Themenstarter:in
63 Beiträge seit 2008
vor 14 Jahren
ProgressBar für Datenbankupdate anzeigen

Hallo Leute!

Habe ein Programm geschrieben, dass die Datenbank eines vorhandenen Programmes aktualisiert. Das macht mein
Programm auch alles schön brav. Braucht dafür 15sec (vielleicht wird es wenn ich mit DataSet arbeite schneller?)

Jetzt habe ich die ganze Updatemethode in ein Thread gepackt. Während das Update läuft soll ein Infofenster
angezeigt werden. Der Fortschritt soll in einem ProgressBar dargestellt werden. Ich wollte das über einen Timer
regeln. Gibt es da eine bessere Lösung? Hier mal noch etwas Quellcode:



public frmDBUpdateAdvanceInfo()
        {
            InitializeComponent();

            System.Threading.Thread updateThread = new System.Threading.Thread(new System.Threading.ThreadStart(StartDBUpdate));
            updateThread.Start();            
        }

        private void StartDBUpdate()
        {
            this.Focus();
            this.Activate();
            System.Timers.Timer timer = new System.Timers.Timer(10000);
            timer.Interval = 100;
            timer.Enabled = true;
            timer.Start();           
            
            MessageBox.Show("Datenbank wird aktualisiert! Bitte auf Abschlussmeldung warten...");
            UpdateDataBase.UpdatePosTable();
            UpdateDataBase.UpdateAKTable();
            UpdateDataBase.UpdatePosAKTable();
            MessageBox.Show("Aktualisierung abgeschlossen! Sie können weiterarbeiten.");
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);

            timer.Stop();
            
        }

        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            this.progressBarDBUpdate.Increment(10);
            this.progressBarDBUpdate.PerformStep();            
        }


Die Anwendung aus der das Update gestartet wurden (also die Hauptanwendung) sollte gesperrt werden. Also irgendwie
den Focus auf der Update Info Form halten bis das Update dann fertig ist. Wie kann man das erreichen?

Beispielcode wäre super... Vielen Dank schonmal für eure Hilfe.

916 Beiträge seit 2008
vor 14 Jahren

Also irgendwie
den Focus auf der Update Info Form halten bis das Update dann fertig ist. Wie kann man das erreichen?

Einfach die InfoForm als Dialog Starten und dann manuell schließen lassen.
Die Idee mit dem Timer ist gnaz gut, aber du musst darauf achten das du die Controls immer per Delegates updates wenn du das aus einen (NICHT GUI) thread tun willst. Da sich ansonsten deine GUI aufhägt bzw. ein Threadübergreifender Zugriff statt findet.

Guckst du Warum blockiert meine GUI?

Again what learned...

R
rainman Themenstarter:in
63 Beiträge seit 2008
vor 14 Jahren

Geht das Form als Dialog starten per Einstellung FormBorderStyle oder liege ich da falsch. Das Problem bei meinem Code oben ist, dass das timer.Elapsed Event nicht ausgelöst wird.
Ansonsten ist die Idee den ProgressBar auf 15 sec einzustellen und dann enstprechende Steps zu machen, wobei es schicker wäre, jede Sekunde die der Thread braucht den Fortschritt anzuzeigen...

151 Beiträge seit 2003
vor 14 Jahren

Also die Form als Dialog starten machst du mit form.ShowDialog();
Beim Timer benötigst du wohl das Tick Event anstelle von Elapsed.

Ich würde das ganze an deiner Stelle aber anders gestalten. Der Vorgang wird doch bestimmt je nach Rechner unterschiedlich lange dauern.

Besser wäre es vielleicht eine kleine Animation laufen zu lassen das was getan wird und eine Progressbar nach jedem Tabellenupdate zu aktualisieren (bei deinen 3 Tabellen also 33%, 66%, 100%).

916 Beiträge seit 2008
vor 14 Jahren

Wenn die Zeit nicht fix ist, dann kannst du den Modus der Progressbar auch umstellen. Sodass der Balken immer hin und her wandert. Das mit dem Elapsed Event stimmt, du musst dich auf das Tick Event anmelden. Nichts desto trotz musst du delegate verwenden.

Und wie moson bereits erwähnt hat, musst du die InfoForm mit ShowDialog() starten. Dann hat die immer den Fokus solang sie offen ist.


ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
ProgressBar.MarqueeAnimationSpeed = 100;
...

Again what learned...

R
rainman Themenstarter:in
63 Beiträge seit 2008
vor 14 Jahren

Super vielen Dank für eure Hilfe. Die Marquee Funktion des ProgressBar habe ich auch schon entdeckt. Ein Timer.Tick Event wird mir von Intelli Prompt nicht angeboten. Habe jetzt die Lösung von moson verwendet, danke dafür!
Mit Delegates habe ich leider noch nie etwas gemacht. Wie würde das für mein Beispiel von oben denn aussehen?

Hier nochmal der Code mit dem dann folgende Fehlermeldung erscheint: "Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement frmDBUpdateAdvanceInfo erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."
Das mit den Threads und Gui habe ich wohl auch nicht richtig verstanden. Werde es nochmal genau durchlesen...



public partial class frmDBUpdateAdvanceInfo : Form
    {       
        public frmDBUpdateAdvanceInfo()
        {
            InitializeComponent();

            System.Threading.Thread updateThread = new System.Threading.Thread(new System.Threading.ThreadStart(StartDBUpdate));
            updateThread.Start();
                    }

        private void StartDBUpdate()
        {            
            this.lblInfo.Text = "Update der Tabelle positionen...";            
            UpdateDataBase.UpdatePosTable();
            this.progressBarDBUpdate.Increment(26);

            this.lblInfo.Text = "Update der Tabelle pos_arbeitskomplexe...";
            UpdateDataBase.UpdateAKTable();
            this.progressBarDBUpdate.Increment(26);

            this.lblInfo.Text = "Upadte der Tabelle pos_ak...";
            UpdateDataBase.UpdatePosAKTable();
            this.progressBarDBUpdate.Increment(35);
            
            this.lblInfo.Text = "Spalte passwort zu Tabelle firma hinzufügen...";
            //UpdateDataBase.AlterFirmaTable();
            this.progressBarDBUpdate.Increment(13);

            this.lblInfo.Text = "Vorgang abgeschlossen!";
            this.btnReady.Visible = true;    
        }


J
1.114 Beiträge seit 2007
vor 14 Jahren

Wie lange dauert denn dein gesamter DB Update. Wenn es auch nur einige Sekunden sind, so ist während dieser Zeit dein Fenster blockiert, und du kriegst z.B. unschöne Artefakte, wenn du ein anderes Fenster einer anderen Anwendung darüber bewegst.

Um das zu vermeiden, sollte man längere Operationen in eigene Threads auslagern. Und wenn dir das Thema Threading noch eventuell ein Dorn im Auge ist, so schau dir doch mal die BackgroundWorker Klasse an. In deinem konkreten Problemfall des DB Updatens würde ich sogar fast sagen wäre das die optimale Denkrichtung.

R
rainman Themenstarter:in
63 Beiträge seit 2008
vor 14 Jahren

geht wie oben schon geschrieben ca. 15 sec nur mit dem Code von meinem letzten Post gibt es eben diese Fehlermeldung:

"InvalidOperationException wurde nicht behandelt.
Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement frmDBUpdateAdvanceInfo erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."

Nehme an d.h. so viel wie aus meinem Update Thread darf nicht in den Thread, der das Control zeichnet zugegriffen werden. Wie bekommt man diese Problem in den Griff?

J
1.114 Beiträge seit 2007
vor 14 Jahren

Das heisst genau das, ja! Deshalb musst du invoken. Und weil Herbivore sowieso auch hierher verlinken würde, nehm ich's ihm mal vorweg: Warum blockiert mein GUI?
Wenn dir diese ganze Invoke Geschichte zu kompliziert erscheint, so kannst du immer noch die BackgroundWorker Klasse nehmen, und dort das ProgressChanged Event auswerten, und in der Methode dein Gui updaten.

916 Beiträge seit 2008
vor 14 Jahren

Du musst jeden Zugriff auf ein GUI Element an ein Delegate weiterleiten. Dazu musst du einfach ein Delegate definieren der die Selbe Schnittstelle hat wie die zu rufende Funktion. In deinem Fall so.


public delegate void BarHelper(int offset);

public void Increment(int offset)
{
    this.progressBarDBUpdate.Increment(offset);
}

Dann kannst du den aufruf einfach an den Delegaten weiter geben.


this.progressBarDBUpdate.Invoke(new BarHelper(Increment), new object[] { 13 });

Was passiert? Der Thread gibt den aufruf an den delegate weiter und der wiederrum führt die Increment Methode im GUI Thread aus. Damit blockiert deine GUI nicht mehr. Du kannst den delegaten auch in Form einer anonymen Methode definieren.

Again what learned...

R
rainman Themenstarter:in
63 Beiträge seit 2008
vor 14 Jahren

super funktioniert vielen dankeschön 😃

Sollte ich für die Anzeige des Labeltextes einen extra Delegaten schreiben bzw. auch wenn ich den Button nach dem Update auf visible setzen möchte?

Man lernt nie aus.

151 Beiträge seit 2003
vor 14 Jahren

Jupp, da brauchst du ein weiteres delegate (da du für den labeltext einen string parameter übergeben musst) + methode/anonyme methode.

btw.:

Als anonyme Methode:


public delegate void BarHelper(int offset);

//....

BarHelper helpMe = delegate(int offset)
{
    this.progressBarDBUpdate.Increment(offset);
};
this.progressBarDBUpdate.Invoke(helpMe, 13);

Lambda Ausdruck:


public delegate void BarHelper(int offset);

//....

BarHelper helpMe = (int offset) =>
{
    this.progressBarDBUpdate.Increment(offset);
};
this.progressBarDBUpdate.Invoke(helpMe, 13);

(Ja, hab grad langeweile : ) ){silver}

Gruß,
moson

916 Beiträge seit 2008
vor 14 Jahren

Grundsetzlich solltest du, wenn du aus einen Thread heraus in irgendeiner Form auf eine Control zugreifst, und sei es um eine Eigenschaft abzufragen, IMMER ein delegate verwenden. Noch sauberer ist es wenn du das Control vorher noch fragst ob es ein Invoke benötigt.


if (this.progressBarDBUpdate.InvokeRequired)
    this.progressBarDBUpdate.Invoke(new BarHelper(Increment), new object[] { 13 });
else 
    this.progressBarDBUpdate.Increment(13);

Again what learned...