C# wpf clsoed window

Fórumok

Üdv,

Van egy MainWindow és egy másik window (Window2), amit a főablakból lehet megnyitni:

private Window2 window2 = new Window2();

// ...

public void Window2_Click()
            if (this.window2 == null)
            {
                this.window2 = new Window2();
                htis.window2.Show();
            }
            else
            {
                this.window2.Show();  # <--- closed window esetén runtime error
            }

A window2-t Hide()-al zárom be. Ha x-el zárja be a user, akkor nem lehet Show()-val megnyitni újból.

Mi az elegáns módja ennek?

Vagy ne tegyem be private-ba? Csak hívjam meg?

public void Window2_Click()
                Window2 window2 = new Window2();
                window2.Show();

Hozzászólások

Szerkesztve: 2024. 02. 28., sze – 12:03

Disclaimer: a WPF-et konkrétan nem ismerem, de egy rakás GUI technológiát igen és a legtöbb hasonló elveken működik, az alapján válaszolok.

Szerintem a második változatban ha Hide-dal eltünteted az ablakot, akkor a GUI része marad (a Scene vagy hasonló objektum tart rá egy referenciát, tehát RAM-ban marad az objektum), de a programod már nem tud róla, nem tudja újranyitni -> leak. Ha még listenerek is vannak rajta, akkor nem csak RAM-ot, de CPU-t is leakel.

Egyik megoldás, hogy a Hide-ot nem használod, hanem Dispose-olod helyette teljesen az ablakot, tehát ugyanazt csinálod mint az X. Persze akkor az állapota elveszik, az alkalmazás logikája dönti el, hogy ez jó-e. A bezárás és újracsinálás overheadje 99.9% hogy tökmindegy, gondolom nem cipel az ablak magábal egy atomreaktort.

Ha fenntartasz referenciát és Hide-ot használsz, akkor viszont nyomon kell követni, hogy a referált ojjektum disposed-e? Két lehetséges megoldás: 1. dispose listener - az adott keretrendszerben ki kell keresni, hogy hogy lehet elkapni a dispose eventet, és azzal együtt törölni a referenciát. 2. használat előtt lekérdezed az állapotát, és ha disposed, akkor nem használod. Erre a legtöbb GUI API-n van metódus vagy property:

if (this.window2 == null || this.window2.IsDisposed())

(A dispose listener amúgy is szokott kelleni és hasznos, ha van valami amit az ablakkal együtt törölni kell, különben leak lesz: tipikus példa a model listenerek (lásd: https://en.wikipedia.org/wiki/Lapsed_listener_problem ), azokat simán ott lehet felejteni, és abból jókora leak lesz. Ha ilyet kell csinálni, én azt a patternt szoktam követni, hogy a listener ráaggatással egy Disposable ojjektum keletkezik (erre a fejlesztőeszköz tud sírni, ha felhasználatlanul van hagyva), és ezt a disposablet azonnal hozzáadom az adott GUI elem dispose listenerjéhez. Ezzel a mintával a GUI listener alapú leak elkerülhető, ha becsuknak egy ablakot, akkor nem marad listener a belső modellre. Ehhez persze saját segéd osztályokat kellett írni, de összességében bevált ez a gyakorlat pár alkalmazásban, jól működik. Egy alternatív megközelítés volna a WeakReference típusú listener használata, de ilyet még nem csináltam.)

 

Szerk.: plusz egy alternatíva, hogy az X gomb hatását felüldefiniálod, hogy valójában csak hide-oljon, de Dispose-olja az ojektumot. Ez is lehetséges a legtöbb keretrendszerben.

MSDN szerint a Hide csak a Window Visibility-t állítja Visibility.Hiddenre. Vagyis az ablak bent marad a UI fában, csak láthatatlan lesz. Show esetén mi az a runtime error? Pontosan milyen exception-t milyen szöveggel kapsz?

Próbáld meg az else ágban a

this.window2.Visiblility = Visiblility.Visible;

értéket beállítani.

Ezt írja:

Exception Info: System.InvalidOperationException: Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.
 

Ezzel is ugyanaz: this.window2.Visibility = Visibility.Visible;

Ezek szerint valahol nem Hide() van az ablakra hívva, hanem Close(). Nézd meg az OnClosing event segítségével, hogy honnét jön a Close() hívás. Esetleg még X-szel zárod be az ablakot, és az OnClosing eventben nincs ez lekezelve, hogy mégse zárja be, csak a Hide() legyen meghívva rá.

Ha ragaszkodsz ahhoz, hogy az ablak megmaradjon, akkor egyszerűen ne engedd meg, hogy bezárják: https://stackoverflow.com/questions/2021681/hide-form-instead-of-closin…

Ha azt akarod, hogy néha eltűnjön, néha bezáródjon, akkor újranyitáskor nézd meg, hogy be lett-e zárva. Nem tudom fejből, de biztos le lehet kérdezni valahogy. Mondjuk ezt a fajta működést én furcsának tartanám.

De szerintem még standardabb megoldás, ha nem szórakozol az elrejtegetéssel, hanem mindig rendesen bezárod. Ne is ments el referenciát az ablakra, esetleg csak arra az időre, amíg feltétlenül muszáj pl. adatot cserélni.

Én nem raknám el a Window2-t, de biztos van valami oka, hogy így csinálod... ez esetben ha te amúgy csak hide-olod, akkor Window2 closingját fordítsd át hide-ba, hogy ha a user X-et nyom, akkor is csak hide legyen, valahogy imígyen:

protected override void OnClosing(CancelEventArgs e)
{ 
    e.Cancel = true; 

    Hide();

    base.OnClosing(e);
}

Nyilván szükség esetén a Window2 eltakarításáról azért gondoskodni kell...