Działanie w tle (WPF, C#)

Działanie w tle (WPF, C#)
shuz
  • Rejestracja:około 3 lata
  • Ostatnio:około 2 lata
  • Postów:1
0

Cześć, podjąłem się amatorskiego wykonania aplikacji do wypełniania protokołów, które trafiają do Word'a. Chciałem aby program na bieżąco przeładowywał zawartość utworzonego dokumentu tzn. pobierał tekst z TextBox'ów i wpisywał go w odpowiednie miejsca. Czy jest to w ogóle możliwe? Próbowałem poprzez stworzenie dodatkowego okienka, w którym w nieskończonej pętli byłyby podmieniane zawartości stringów ale po kompilacji nawet nie pojawia się okienko główne tylko od razu działa pętla w tym drugim.

Barstok
  • Rejestracja:ponad 4 lata
  • Ostatnio:29 dni
  • Postów:9
0

Jak nieskończona pętla to raczej przyda ci się oddzielny wątek do tego, czy tam Taski chyba są w c#. Nowe okienko ci nic nie da

shuz
Czytałem trochę o wątkach ale wydaje mi się, że byłby to trochę przerost formy nad treścią. Nie musi być to koniecznie nieskończona pętla tylko taki wpadł mi pomysł, może być np metoda, która się wywołuje cyklicznie co kilka sekund ale ona też chyba musiałaby być niezależna od okienka głównego
heyyou
  • Rejestracja:ponad 6 lat
  • Ostatnio:25 dni
  • Postów:183
0

jak chcesz, aby mozna bylo jednoczesnie korzystać z aplikacji no musisz to zrobić w innym wątku i tyle ;)
w uproszczeniu

Kopiuj
new System.Threading.Tasks.Task(() =>
{
    while (twój_warunek)
    {
        ZapiszKontrolkeDoWorda();
        System.Threading.Thread.Sleep(1000); // milisekund
    }
}).Start();

tylko tam sobie musisz doczytać o cancellationToken, itd - tj. musisz przy zamykaniu aplikacji (albo w jakimś innym wybranym przez ciebie momencie) ubić tego taskulca ;)

edytowany 3x, ostatnio: heyyou
Zobacz pozostałe 4 komentarze
JU
Chodzi o to, że powinien dać Task.Delay, zamiast Thread.Sleep. Wtedy będzie elegancko. Ten kod może wykonać na wątku głównym. Jeśli będzie await Task.Delay - zdecydowanie wykona się na innym.
SO
No z Task.Delay niewątpliwie byłoby lepiej, ale wciąż nie widzę co tutaj może się wykonać na głównym wątku.
JU
Cały Task może wykonać się na głównym wątku - razem ze Sleepem.
SO
No nie może, bo Taski wykonywane są na ThreadPoolu, a tam nie ma głównego wątku, tak jak pisał @obscurity.
JU
Sprawdziłem i masz rację. Może coś się zmieniło, bo jestem na 100% pewien, że kiedyś na stronach Microsoftu czytałem, że Task może, ale nie musi wykonać się na innym wątku.
JU
  • Rejestracja:około 22 lata
  • Ostatnio:2 miesiące
  • Postów:5042
0

Są jeszcze inne opcje. Np. BackgroundWorker. Czy tam BackgroundTask. Można też zrobić asynchronicznego cyklicznego Taska. Kod będzie podobny do tego pokazanego przez @heyyou tyle że zamiast Thread.Sleep, daj Task.Delay. I użyj async/await w tym miejscu:

Kopiuj
new Task(async () => 
{
  while(...)
  {
    DoJob();
    await Task.Delay(1000);
  }
}

Oczywiście, jeśli DoJob robi coś na kontrolkach, to musisz to odpowiednio synchronizować, bo nie możesz odwołać się do kontrolki z innego wątku niż główny.

CF
  • Rejestracja:ponad 4 lata
  • Ostatnio:ponad 2 lata
  • Postów:27
1

@shuz: jeżeli zrobisz to aktywną pętlą w osobnym wątku, to program będzie generował dokument nawet jeżeli nie ma żadnych zmian. To będzie upierdliwe jak ktoś będzie chciał otworzyć dokument, zmienić nazwę albo przenieść.

Możesz pokusić się o coś bardziej zwinnego np.:

Gdy textbox rzuca TextChanged, wtedy uruchamiasz 3 sekundowy licznik.
a) jeżeli pojawiło się nowe zdarzenie TextChanged (globalnie dla wszystkich TextBoxów, które powodują nadpisanie dokumentu) restartujesz licznik
b) jeżeli minęły 3 sekundy, ustawiasz wspólną flagę na true.

Pozwala to uniknąć przegenerowania pliku, jeżeli ktoś wciąż coś pisze.

W wątku obok cały czas monitorujesz flagę w pętli i jeżeli jest ona ustawiona na true to zmieniasz ją na false i generujesz dokument. Jeżeli flaga jest ustawiona na false robisz jakieś sleep na sekundę.

Aby robić to jeszcze bardziej zwinne możesz poczytać o wybudzaniu wątku gdy następuje zdarzenie, wtedy w ogóle pozbywasz się aktywnego czekania.

G1
  • Rejestracja:ponad 4 lata
  • Ostatnio:7 dni
  • Postów:506
0

Może trochę przerost formy nad treścią, pisane na szybko ale u mnie sprawdzało się coś na wzór tego (kolega wyżej to ładnie opisał)

Kopiuj
public class PdfGenerator<Args, Return>
    {
        public delegate Return GeneratePdfDelegate(Args args);

        public PdfGenerator(TimeSpan delay, GeneratePdfDelegate generatePdfMethod)
        {
            Delay = delay;
            this.generatePdfMethod = generatePdfMethod;

            timer = new Timer();
            timer.AutoReset = false;
            timer.Interval = Delay.TotalMilliseconds;
            timer.Elapsed += Timer_Elapsed;
        }

        public TimeSpan Delay { get; private set; }
        private GeneratePdfDelegate generatePdfMethod;

        private Timer timer;
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            RequestgeneratePdf?.Invoke(this, EventArgs.Empty);
        }
        public event EventHandler<EventArgs> RequestgeneratePdf;
        public event EventHandler<Return> PdfGenerated;

        public void TextChange()
        {
            timer.Stop();
            timer.Start();
        }

        public async void GeneratePdf(Args args)
        {
            await Task.Run(() =>
            {
                Return myPdf = generatePdfMethod(args);
                PdfGenerated?.Invoke(this, myPdf);
            });
        }
        
    }

I przykład użycia bardzo prosty:

Kopiuj
public class Main
    {
        public Main()
        {
            pdfGenerator = new PdfGenerator<string, string>(new TimeSpan(0, 0, 3), (string args) => {
                return $"My Pdf with text {args}";
            });
            pdfGenerator.RequestgeneratePdf += PdfGenerator_RequestgeneratePdf;
            pdfGenerator.PdfGenerated += PdfGenerator_PdfGenerated;
        }

        PdfGenerator<string, string> pdfGenerator;
        private void TextChange(object sender, EventArgs eventArgs)
        {
            pdfGenerator.TextChange();
        }
        private void PdfGenerator_RequestgeneratePdf(object sender, EventArgs e)
        {
            pdfGenerator.GeneratePdf(MyTextBox.text);
        }
        private void PdfGenerator_PdfGenerated(object sender, string e)
        {
            string MyPdf = e;
        }
    }

Pdf będzie się generował po upłynięciu delay czasu bezczynności.

edytowany 1x, ostatnio: gswidwa1
MA
  • Rejestracja:ponad 5 lat
  • Ostatnio:9 dni
  • Postów:20
0

Jeżeli nie musisz targetowac innych platform niż Windows, to DispatcherTimer jest prosty i miły w użyciu oraz idealnie pasuje do wymagań.

https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=windowsdesktop-6.0

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.