Czekanie na metodę przy zamykaniu okna

0

Witam. Mam problem z zapisem danych, po kliknięciu "X" w programie. Mój kod, w skrócie wygląda tak:

 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Save();
}
private async void Save()
{
//działania na wątku okna
//Tutaj funkcje  await Task.Run(() => metoda()));
}

Niestety, wątek okna nie czeka na wykonanie się kodu z "await"... Nie mam pojęcia czemu. Jak ustawie:

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Close = true;
Save();
}

Wszystko działa jak należy. Poza tym, że oko się nie zamknie...

Wie ktoś może jak rozwiązać ten problem ?
Proszę o pomoc.
Pozdrawiam.

0
         protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            if (MessageBox.Show("Czy na pewno chcesz zakończyć ?",
              "Zamknac?", MessageBoxButton.YesNo, MessageBoxImage.Question)
                      == MessageBoxResult.No)
                e.Cancel = true;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
//****************************
            //tu zapisujsz
        }



0

Sorki, pomyłka ;)

0

Zrobiłem tak jak radziłeś:

   protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            if ((Password_listView.Items.Count > 0) && (IsSave == false))
            {
                if (MessageBox.Show("Czy chcesz zapisać zmiany ?", "Zapis", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                {
                    e.Cancel = true;
                }
            }
        }
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            Save();
        }

Dostaję takie błędy:
Ważność Kod Opis Projekt Plik Wiersz
Błąd No overload for 'OnClosing' matches delegate 'CancelEventHandler',
oraz
Ważność Kod Opis Projekt Plik Wiersz
Błąd CS0123 Żadne z przeciążeń dla elementu „OnClosing” nie pasuje do delegata „CancelEventHandler”.

PS: Piszę w WPF, może ten kod jest dla WinForms ?

0

Tak zrób:

        public MainWindow() {
            InitializeComponent();

            var task = new Task(new Action(() => {
                var upperLimit = 1000000000;
                for (int i = 0; i < upperLimit; ++i) { }
            }));
            task.Start();


            this.Window.Closing += (sender, e) => {
                var result = MessageBox.Show("Czy chcesz zamknąć okno?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                switch (result) {
                    case MessageBoxResult.Yes:
                        {
                            if (!task.IsCompleted) {
                                MessageBox.Show("Zadanie nie zostało zakończone.");
                                e.Cancel = true;
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            e.Cancel = true;
                            break;
                        }
                }
            };
        }

A jeśli chcesz wykonać jakieś zmiany w GUI użyj Dispatcher'a. Ustaw sobie duży upperLimit żebyś zdążył zobaczyć, że okno się nie zamknie dopóki trwa zadanie.

0

@grzesiek51114, czemu mam wywołać funkcję Save(), w konstruktorze ?
Ja potrzebuję ją wykonać dopiero przy zamykaniu okna.

private void Save()
{
//coś tam z oknem
await Task.Run(() => metoda()));
await Task.Run(() => metoda()));
//koniec
}
0

No przecież to co napisałem to jest tylko przykład, żebyś sobie po swojemu rozpisał. Zrób całego sejwa jako task'a i sprawdzaj przy zamknięciu czy zakończył działanie. Jak nie to po prostu nie zamykaj okna.

0

A dobra... wiem o co Ci chodzi. :D Nie zajarzyłem na początku. Żeby nie zapętlił Ci się event zamykania okna musisz sprawdzać status obiektu task.
Tak będzie dobrze:

        public MainWindow() {
            InitializeComponent();

            var task = new Task(new Action(() => {
                for (int i = 0; i < int.MaxValue; ++i) { }
            }));

            this.Window.Closing += (sender, e) => {
                if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();
                var result = MessageBox.Show("Czy chcesz zamknąć okno?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                switch (result) {
                    case MessageBoxResult.Yes:
                        {
                            if (!task.IsCompleted) {
                                MessageBox.Show("Zadanie nie zostało zakończone.");
                                e.Cancel = true;
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            e.Cancel = true;
                            break;
                        }
                }
            };
        }
0

Nie działa :/

Sam sprawdź, uruchom tym taką metodę:

  private void Save()
        {
            MessageBox.Show("a");
        }

Zapętla się...

1

Przecież działa i nic się nie zapętla, bo nie może!

        public MainWindow() {
            InitializeComponent();

            var task = new Task(this.Save);

            this.Window.Closing += (sender, e) => {

                // TO jest kluczowa linijka:
                if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();

                var result = MessageBox.Show("Czy chcesz zamknąć okno?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                switch (result) {
                    case MessageBoxResult.Yes:
                        {
                            if (!task.IsCompleted) {
                                MessageBox.Show("Zadanie nie zostało zakończone.");
                                e.Cancel = true;
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            e.Cancel = true;
                            break;
                        }
                }
            };
        }

        private void Save() {
            MessageBox.Show("a");
        }

EDIT: Póki nie zamkniesz messagebox'a z literą program nie pozwoli Ci zamknąć okna.

0

Em, po prostu dodaj sobie flagę IsTaskDone do jakiejś klasy - podczas uruchamia taska ustawiaj tę flagę na false i przy zamykaniu okna czekaj, aż zmieni się na true...

1

Ten kod działa:

this.Closing += (sender, e) => {
                if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();
                var result = MessageBox.Show("Czy chcesz zamknąć okno?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                switch (result)
                {
                    case MessageBoxResult.Yes:
                        {
                            if (!task.IsCompleted)
                            {
                                MessageBox.Show("Zadanie nie zostało zakończone.");
                                e.Cancel = true;
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            e.Cancel = true;
                            break;
                        }
                }
            };

Potrzebuję jeszcze ustawić wątek na STA... Jak to zrobić, bo raczej "thread.SetApartmentState(ApartmentState.STA);" nie zda tu egzaminu ;P

1

Potrzebuję jeszcze ustawić wątek na STA...

        private void Save() {
            // Tutaj działania na danych.
            App.Current.Dispatcher.BeginInvoke(new Action(() => {
                // Tutaj działania na GUI.
            }));
        }
0

Aha, i jeszcze jedno pytanie
Czym różni się Dispatcher.Invoke, od App.Current.Dispatcher.BeginInvoke, bo zawsze używałem tego pierwszego ?

0

Ehh, dalej nie do końca działa to tak jakbym chciał

var task = new Task(Save);
            
            this.Closing += (sender, e) => {
                if (IsSave == false)
                {
                    var result = MessageBox.Show("Czy chcesz zapisać dane?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                    if (result == MessageBoxResult.Yes)
                    {
                        if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();
                    }
                    if (!task.IsCompleted)
                    {
                        MessageBox.Show("Zadanie nie zostało zakończone.");
                        e.Cancel = true;
                    }
                    else
                    {
                        e.Cancel = false;
                    }
                }
            };

Muszę jakoś wywoływać zdarzenie Close(), żeby po zakończeniu zadania okno się zamykało.

0
 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Task.WaitAll(Save());
}
private async Task Save()
{
//działania na wątku okna
    await Task.Run(() => metoda()).ConfigureAwait(false);
}
0

Zrób własną wersję klasy Task, która będzie miała zdarzenie TaskFinished. Będziesz mógł podpiąć pod nie jakąkolwiek akcję chcesz zamiast dopinać zamykanie na sztywno.


// Nie zwracaj uwagi na usingi. Poustawiałem sobie uniwersalne :)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace _4programmersWPF {

    public class MyTask : Task {
        public delegate void TaskFinishedEventHandler();
        public TaskFinishedEventHandler TaskFinished;

        public MyTask(Action action) : base(action) { }
        public void FireTaskFinished() {
            if (this.TaskFinished != null) this.TaskFinished();             
        }
    }

    public partial class MainWindow : Window {
        private MyTask task;

        public MainWindow() {
            InitializeComponent();

            this.task = new MyTask(this.Save);
            this.task.TaskFinished += () => {
                App.Current.Dispatcher.BeginInvoke(new Action(() => {

                    //  Należy desubskrybować zdarzenie zamykania okna, bo inaczej
                    //  program nas zapyta czy chcemy je zamknąć.
                    this.Window.Closing -= this.Window_Closing;
                    this.Close();
                }));
            };

            this.Window.Closing += Window_Closing;
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();
            var result = MessageBox.Show("Czy chcesz zamknąć okno?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
            switch (result) {
                case MessageBoxResult.Yes:
                    {
                        if (!task.IsCompleted) {
                            MessageBox.Show("Zadanie nie zostało zakończone.");
                            e.Cancel = true;
                        }
                        break;
                    }
                case MessageBoxResult.No:
                    {
                        e.Cancel = true;
                        break;
                    }
            }
        }

        //  Tak, posłużę się tą pętlą, bo na messageboksie nie zauważysz dobrze efektu.
        //  W swoim kodzie zrobisz po swojemu.
        private void Save() {
            for (int i = 0; i < int.MaxValue; ++i) { }
            this.task.FireTaskFinished();
        }
    }
}
1
grzesiek51114 napisał(a):

Zrób własną wersję klasy Task, która będzie miała zdarzenie TaskFinished. Będziesz mógł podpiąć pod nie jakąkolwiek akcję chcesz zamiast dopinać zamykanie na sztywno.

przecież taski już mają "ContinueWith" które robi to samo

0

Zamraża mi cały wątek główny

bo nie powiedziałeś że nie ma zamrażać
ogólnie czemu po prostu nie zrobisz zapisu w tle synchronicznie po zamknięciu okna na zdarzeniu "Closed"?

0

Coś takiego w sumie wystarczyłoby mi:

public MainWindow()
        {
            InitializeComponent();
            var task = new Task(Save);
            this.Closing += (sender, e) =>
            {
                
                if (IsSave == false)
                {
                    var result = MessageBox.Show("Czy chcesz zapisać dane?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
                    if (result == MessageBoxResult.Yes)
                    {
                        if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();
                        IsSave = true;
                        e.Cancel = true;
                    }
                }
                else
                {
                    if (task.Status == TaskStatus.Running == true)
                    {
                        e.Cancel = true;
                    }
                    else
                    {
                        Environment.Exit(0);
                    }
                }
            };
         }

Ale nie mam pojęcia, dlaczego task.Status zawsze zwraca mi true....

0

Czy wiesz co ta linijka robi: if (task.Status != TaskStatus.Running && task.IsCompleted != true) task.Start();?
Ona dba o to żeby program nie próbował odpalić zadania drugi raz. Dba także o to, żebyś nie odpalił już zakończonego taska.

A to if (IsSave == false) jest Ci kompletnie niepotrzebne! Tylko komplikujesz sobie życie. Dlaczego nie możesz zrobić tego tak jak Ci powiedziałem albo tak jak napisał @mały Młot? Nasze rozwiązania działają i niepotrzebnie bawisz się w komplikowanie sobie życia :)

0

Zdenerwowałem się, że mi nie wychodzi i zrobiłem tak:
Save, uruchamiam z argumentem typu bool, dorzuciłem w tej metodzie dwie linijki:

if (Exit == true)
this.Close();

I po problemie :D

Dzięki za pomoc :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1