Multidesk.be » Forums » .Net » [VB.Net] public dim binnen subs mogelijk?

  • Pagina
  • 1 van 1
0 gasten lezen dit onderwerp.
^ Onderwerp geschreven door steven4ever op maandag 25 juli 2011 om 10:53:17.
steven4ever heeft nog geen avatar toegevoegd
Multiviteit: 206
Ik probeer een formulier te openen vanuit een backgroundworker thread (van zodra een bepaalde variabele op 'True' springt), maar dat schijnt dus niet zo eenvoudig te zijn. Momenteel heb ik een module met daarin de declaratie:
CODE
  1. Public frmTest As New Form1


'public' heb ik (denk ik) nodig om het formulier te kunnen manipuleren met code in andere klassen.

vervolgens gebruik ik "frmTest.Show()" in de "ProgressChanged" event van mijn backgroundworker. Het probleem: van zodra ik "frmTest.Close()" gebruik wordt heel de variabele leeggemaakt (disposed) en kan ik "frmTest.Show()" geen tweede maal meer gebruiken.

Wat ik zoal geprobeerd heb is ".Hide()" te gebruiken ipv ".Close()", maar dat is zelfs in mijn ogen prutswerk. De tweede manier was om "frmTest" binnen de Sub te declareren, maar dan vind ik geen manier om het formulier te benaderen vanuit andere klassen.
Ideaal zou dus zijn wanneer ik "Public frmTest As New Form1" binnen een sub zou kunnen plaatsen, maar dat gaat dan blijkbaar ook weer niet.

EDIT
------
volgens mij zit ik al 1 stap verder in de goeie richting. In mijn module staat nu:
CODE
  1. Public frmTest As Form


en binnen de BackgroundWorker1_ProgressChanged Sub het volgende:
CODE
  1. frmTest = New Form1
  2. frmTest.Show()


maar daar loopt het helaas vast op de eerste regel met volgende foutmelding:

Dixit

ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.


Alle hulp of info is welkom. Ik heb de indruk dat ik ergens met delegate subs moet werken maar daar geraak ik zelf niet uit.
^ Reactie #1 geschreven door thekid op maandag 25 juli 2011 om 12:31:20.
thekid's avatar
Multiviteit: 5273
Moderator
bon, volgens mij gade volledig de verkeerde kant uit :)

het is gemakkelijker een nieuwe klasse aan te maken die gebaseerd is op een backgroundworker, waar je uw eigen status requests zo wat mee update intern

kan je mij uitleggen waarom je op een processchanged uw form wilt tonen? en wanneer wil je deze dan weer hiden?

kan je wat meer uitleg geven over je bedoeling? Als het de bedoeling is om een label met text te voorzien in samenspraak met een progressbar die volloopt dan zijn er stukken eenvoudigere manier om dit te bewerkstelligen ;)

dus laat maar eens weten wat je net wenst te doen :D
"Human beings make life so interesting. Do you know, that in a universe so full of wonders, they have managed to invent boredom." - Death in Hogfather
^ Reactie #2 geschreven door steven4ever op maandag 25 juli 2011 om 13:56:47.
steven4ever heeft nog geen avatar toegevoegd
Multiviteit: 206
Mijn bedoeling is om de backgroundworker volledig zelfstandig te laten werken, tot op een bepaald moment dat er input van de gebruiker nodig is om door te kunnen gaan (in dit geval een captcha invullen).
Om die captcha weer te geven heb ik een nieuw formulier nodig, liefst 1 waarbij ik de waarden van labels en dergelijke kan aflezen of invullen. ProgressChanged leek mij de meest logische sub om de code te plaatsen aangezien heel het process wordt stilgezet tot de gebruiker het formulier heeft ingevuld.

Ik begrijp min of meer dat een applicatie een GUI thread heeft en dat backgroundworkers op een aparte thread opereren, maar hoe dat dus verder moet om vanuit een aparte thread iets op de GUI thread te doen is allemaal nog zeer wazig voor mij.

Snel samengevat: op het startformulier stelt men zijn voorkeuren in. Een startknop start dan een nieuw formulier (met progress bars) en de backgroundworker, en om de x aantal minuten heb ik dat derde formuliertje nodig dewelke ik probeer aan te roepen vanuit de achtergrond thread.

tis eigenlijk een wreed simpel probleem, maar wie de oplossing niet kent geraakt nergens (en aan de schaarse documentatie die er te vinden is heb ik ook niet veel).

Ondertussen ziet mijn code er ongeveer zo uit:
CODE
  1. Public Class Form1
  2.  
  3. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  4.         form2.show()
  5.         With BackgroundWorker1
  6.                 .WorkerReportsProgress = True
  7.                 .RunWorkerAsync()
  8.         End With
  9. End Sub
  10.  
  11. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
  12.         'langdurig process dat af en toe progress reports doet
  13. End Sub
  14.  
  15.  
  16. Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
  17.         If e.UserState = "captcha" then
  18.                 'hier probeer ik het captcha formulier (Form3) te openen
  19.                 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  20.                 Dim tNewform As System.Threading.Thread = New Threading.Thread(AddressOf OpenForm)
  21.                 tNewform.TrySetApartmentState(Threading.ApartmentState.STA)
  22.                 tNewform .Start()
  23.                 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  24.  
  25.         Else
  26.                 'update de progressbars op Form2
  27.         End If
  28. End Sub
  29.  
  30.  
  31. Private Sub OpenForm()
  32.         Application.Run(New Form3)
  33. End Sub
  34.  
  35. End Class


Hopelijk schept mijn textmuurtje meer duidelijkheid over wat ik probeer te bereiken. Eigenlijk, als ik nu nog wist hoe ik met bovenstaande code ook de controls op Form3 kon manipuleren was het opgelost, maar zo simpel is waarschijnlijk weer niet.
Deze tekst werd het laatst bewerkt voor 44.73 % door steven4ever op maandag 25 juli 2011 om 14:23:52.
^ Reactie #3 geschreven door steven4ever op maandag 25 juli 2011 om 15:08:15.
steven4ever heeft nog geen avatar toegevoegd
Multiviteit: 206
Nu ik erover nadenk, kan ik een formulier sluiten zonder dat Dispose() wordt aangeroepen? Daar ligt eigenlijk het probleem...
^ Reactie #4 geschreven door thekid op maandag 25 juli 2011 om 16:41:13.
thekid's avatar
Multiviteit: 5273
Moderator
dus

als ik het goed versta, want ik vind het nog wat wazig

- Je opent een formulier
- deze controleert per interval op een captcha input?
- je hebt deze captcha ook weer nodig in je uiteindelijke backgroundworker code?


in het algemeen, werk je met een backgroundworker zo -> (je kan natuurlijk ook gewoon je events toevoegen via += operator, maar de override werkt ook goed)

CODE
  1.  
  2.     public class MyWorker : BackgroundWorker
  3.     {
  4.         // de elementen die je wenst te wijzigen
  5.         public ProgressBar progressBar { get; set; }
  6.  
  7.         public MyWorker()
  8.         {
  9.              // kan hij gestopt worden?
  10.              WorkerSupportsCancellation = true;
  11.              // wens je de wijzigingen te rapporteren?
  12.              WorkerReportsProgress = true;
  13.         }
  14.  
  15.        protected override void OnDoWork(DoWorkEventArgs e)
  16.        {
  17.            // uw langdurige process
  18.            base.OnDoWork(e);
  19.        }
  20.  
  21.        protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
  22.        {
  23.            // worker is afgelopen
  24.            base.OnRunWorkerCompleted(e);
  25.        }
  26.  
  27.        protected override void OnProgressChanged(ProgressChangedEventArgs e)
  28.        {
  29.            // verandering
  30.            if (progressBar != null)
  31.            {
  32.                 progressBar.Value = e.ProgressPercentage;   // rekening houdend met 0-100 (min-max)
  33.            }
  34.            base.OnProgressChanged(e);
  35.        }
  36.     }
  37.  
  38.  


echter die versie zou niet echt goed staan met uw systeem :) (input opvragen via een form is te ingewikkeld)


voor wat je het nodig hebt, waarom werk je niet met een Thread op je form? Deze werkt nog steeds in background, en via Invoke en delegates ga je dan de wijzigingen opvragen. Het is niet extra ingewikkeld, het is gewoon iets voorzichtiger werken :)

een voorbeeld hiervan (opnieuw in C#)
CODE
  1.  
  2. using System.Linq;
  3. using System.Text;
  4. using System.Windows.Forms;
  5. using System.Threading;
  6.  
  7. namespace DelegateExample
  8. {
  9.     public partial class FormExample : Form
  10.     {
  11.         Thread t;
  12.         bool InvokeForm = false;
  13.         Form f = new Form();
  14.  
  15.         public delegate string GetFormValue(Form formToUse);
  16.  
  17.         public string DoGetFormValue(Form formToUse)
  18.         {
  19.             if (formToUse.InvokeRequired)
  20.                 return Invoke(new GetFormValue(DoGetFormValue), new object[] { formToUse }).ToString();
  21.             else
  22.             {
  23.                 if (formToUse.ShowDialog() == DialogResult.OK)
  24.                 {
  25.                     return "value on form to use to return";
  26.                 }
  27.                 else
  28.                 {
  29.                     return string.Empty;
  30.                 }
  31.             }
  32.         }
  33.  
  34.         public FormExample()
  35.         {
  36.             InitializeComponent();
  37.             t = new Thread(new ThreadStart(RunningThread));
  38.             t.Start();
  39.         }
  40.  
  41.         void RunningThread()
  42.         {
  43.             while (t.ThreadState != ThreadState.AbortRequested)
  44.             {
  45.                 if (InvokeForm)
  46.                 {
  47.                     InvokeForm = false;
  48.                     string s = DoGetFormValue(f);
  49.                 }
  50.                 // invoke hier uw procedure om de progressbars dan te wijzigen :)
  51.             }
  52.         }
  53.  
  54.         private void button1_Click(object sender, EventArgs e)
  55.         {
  56.             InvokeForm = true;
  57.         }
  58.     }
  59. }
  60.  



*edit*
ik wil je alle code in VB geven ook, maar dan moet je wachten tot vanavond, na mijn werk ;)

*edit 2*
ge kunt uw forms ook sluiten door gebruik te maken van this.DialogResult = DialogResult.OK (of Cancel) ... (in VB.NET is het Me.DialogResult = DialogResult.OK of My.DialogResult = DialogResult.OK 'k weet het niet direct vanbuiten :D)
Deze tekst werd het laatst bewerkt voor 3.34 % door thekid op maandag 25 juli 2011 om 16:45:32.
"Human beings make life so interesting. Do you know, that in a universe so full of wonders, they have managed to invent boredom." - Death in Hogfather
^ Reactie #5 geschreven door steven4ever op maandag 25 juli 2011 om 20:43:52.
steven4ever heeft nog geen avatar toegevoegd
Multiviteit: 206
Veel dank :) VB code hoeft niet, het zal zo ook wel gaan. Ik ga het morgen eens allemaal overlopen met nen iets frissere kop.
De bedoeling van het programma is een captcha die ik van een bepaalde webservice krijg (ter controle op webcrawlers of weet ik veel wat) door te voeren aan de gebruiker.
Misschien doe ik er goed aan om eerst eens grondig multithreading te gaan bestuderen, ik kan moeilijk geloven dat er geen gemakkelijkere manier bestaat om forms aan te roepen vanuit meerdere threads.
^ Reactie #6 geschreven door thekid op maandag 25 juli 2011 om 22:09:20.
thekid's avatar
Multiviteit: 5273
Moderator
kijk bij deze heb ik even over u idee gedacht en een vb projectje als voorbeeld gemaakt ;)

enige vragen, shoot


TestBackground (Vb.Net): 9e0d0060d1963d87bb14b85b0d213560.zip

da's dus uw gevraagde implementatie, met backgroundworker en te starten/stoppen via een knopke :D doet zelfs een rare versie van captcha controle om te tonen hoe je dan ook kan valideren

en ja, 't is in vb.net, maar doe uzelf een plezier als je in visual studio werkt, en ga naar C# want de editor is daar echt op gebouwd (qua autocomplete, shortcut keys, snelheid van werken)

't is wel .net framework 4 en Visual STudio 2010 (je kan de forms wel makkelijk importen in een lage versie, enkel letten op de property declaratie, die zou je eventueel wel volledig moeten integreren :D)
"Human beings make life so interesting. Do you know, that in a universe so full of wonders, they have managed to invent boredom." - Death in Hogfather
^ Reactie #7 geschreven door thekid op maandag 25 juli 2011 om 23:26:52.
thekid's avatar
Multiviteit: 5273
Moderator
bon, 'k had dat weer fantastisch getest

ge kunt de klasse CaptchaBackbround.vb file vervangen door deze :D

CODE
  1.  
  2. Imports System.ComponentModel
  3. Imports System.Threading
  4.  
  5. Public Class CaptchaBackground
  6.     Inherits BackgroundWorker
  7.  
  8.     Public Property VisualProgress As ProgressBar
  9.     Public Property VisualMessage As Label
  10.  
  11.     Public Sub New()
  12.         WorkerReportsProgress = True
  13.         WorkerSupportsCancellation = True
  14.     End Sub
  15.  
  16.     Private Sub SetProgress(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles MyBase.ProgressChanged
  17.         If VisualMessage IsNot Nothing Then
  18.             VisualMessage.Text = If(e.UserState IsNot Nothing, e.UserState.ToString(), String.Empty)
  19.         End If
  20.         If VisualProgress IsNot Nothing Then
  21.             VisualProgress.Value = If(e.ProgressPercentage < VisualProgress.Maximum, e.ProgressPercentage, VisualProgress.Maximum)
  22.         End If
  23.     End Sub
  24.  
  25.     Protected Sub DoBackgroundProcess(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles MyBase.DoWork
  26.         Dim r As New Random()
  27.         Dim cntCaptcha As Integer = 0
  28.         For count As Integer = 0 To 100
  29.             If CancellationPending Then
  30.                 Exit For
  31.             End If
  32.             If count Mod 25 = 0 Then
  33.                 Dim cc As String = (r.Next(8999) + 1000).ToString()
  34.                 Dim f As New CaptchaForm(cc)
  35.                 Do
  36.                     f.ShowDialog()
  37.                 Loop While Not CancellationPending And f.Captcha <> cc
  38.                 cntCaptcha += 1
  39.             End If
  40.             ReportProgress(count, String.Format("Current progress: {0}, Captcha's passed {1}", count, cntCaptcha))
  41.             Thread.Sleep(250)
  42.         Next
  43.     End Sub
  44.  
  45.     Protected Sub FinishedWorking(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles MyBase.RunWorkerCompleted
  46.         ' finito
  47.         If VisualMessage IsNot Nothing Then
  48.             VisualMessage.Text = If(e.Error IsNot Nothing, String.Format("Worker failed due to\r\n{0}\r\n{1}", e.Error.Message, e.Error.StackTrace), "Runworker completed succesfully")
  49.         End If
  50.         If VisualProgress IsNot Nothing Then
  51.             VisualProgress.Value = VisualProgress.Maximum
  52.         End If
  53.     End Sub
  54.  
  55. End Class
  56.  
"Human beings make life so interesting. Do you know, that in a universe so full of wonders, they have managed to invent boredom." - Death in Hogfather
^ Reactie #8 geschreven door steven4ever op dinsdag 26 juli 2011 om 10:16:29.
steven4ever heeft nog geen avatar toegevoegd
Multiviteit: 206
't Is toch weer een nieuw concept voor mij om een ingebouwde klasse te gaan gebruiken in een nieuwe klasse, maar ik denk dat ik de voordelen wel zie. Ik heb het VB projectje even getest en 't is exact wat ik probeer te bereiken, hopelijk lukt het bij mijn project ook op die manier :). Bij mij gebeurt het valideren van de Captcha wel op de webservice ipv binnen het project, dus daar moet ik ook nog iets op vinden (al heb ik wel al een paar ideeŽn klaarliggen).

Duizend maal dank voor al de tijd en moeite. Ik laat nog weten hoever ik ermee kom.
^ Reactie #9 geschreven door thekid op dinsdag 26 juli 2011 om 10:31:49.
thekid's avatar
Multiviteit: 5273
Moderator
graag gedaan

als je vragen hebt moet je het maar laten weten ook :)

inzake uw validatie: ik geef nu een string door met de captcha, dat kan evengoed een byte-array zijn met de image data van hen waarmee je de picture box dan opvult en je kan dan de f.Captcha <> cc in de Do Loop vervangen door een bool Succes en deze Succes bool of in de captcha form zelf te valideren (misschien het beste, en ze de form niet laten sluiten tot ze correct zijn) of voor het Loop while statement
"Human beings make life so interesting. Do you know, that in a universe so full of wonders, they have managed to invent boredom." - Death in Hogfather
^ Reactie #10 geschreven door rope op maandag 8 augustus 2011 om 18:29:16.
rope's avatar
Multiviteit: 15
Noobs. Je moet het gewoon buiten een sub alvast declarereren. Dus Public Form1 en daaronder declareren.
  • Pagina
  • 1 van 1

Snel-antwoordformulier
Toon uitgebreid antwoordformulier Bericht nalezen Bericht plaatsen