programing

WPF: 창을 닫은 후에는 재사용할 수 없습니다.

topblog 2023. 4. 18. 21:42
반응형

WPF: 창을 닫은 후에는 재사용할 수 없습니다.

예를 두려고 .Window할 때 전화하세요.ShowDialog이것은 winforms에서 찾을 수 있었습니다만, WPF에서는 다음의 예외를 인정받았습니다.

System.InvalidOperation예외: 가시성 또는 콜 표시, ShowDialog 또는 WindowInterop을 설정할 수 없습니다.Helper.InsureHandle은 창이 닫히면 자동으로 처리됩니다.

WPF에서 이와 같은 작업을 수행할 수 있는 방법이 있습니까?

MyWindow.Instance.ShowDialog();

public class MyWindow : Window
{
    private static MyWindow _instance;

    public static MyWindow Instance
    {
        if( _instance == null )
        {
            _instance = new Window();
        }
        return _instance();
    }
}

내 말이 맞다면 해당 창의 닫힘 이벤트를 취소하고 표시 상태를 숨김으로 설정할 수 있습니다.

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        e.Cancel = true;
        this.Visibility = Visibility.Hidden;
    } 

창문을 닫는 것보다 시야를 바꾸면 할 수 있을 것 같아요.Closing() 이벤트에서 이 작업을 수행한 후 닫기를 취소해야 합니다.닫힘을 허용하면 닫힌 창을 다시 열 수 없습니다. 여기서:

Closing 이벤트가 취소되지 않으면 다음과 같은 상황이 발생합니다.

...

창에서 생성한 관리되지 않는 리소스는 삭제됩니다.

그런 다음 창은 다시 유효하지 않습니다.

매번 새로운 창을 만드는 것은 그다지 큰 효과가 없다고 생각합니다.또, 버그나 메모리 누수를 디버깅하기 어려운 경우가 훨씬 적습니다(또, 애플리케이션을 셧다운 했을 때에, 애플리케이션을 닫고, 자원을 해방할 필요가 있습니다).


ShowDialog()를 사용하고 있다는 것만 읽어주세요.이렇게 하면 창이 모달 상태가 되어 숨기기만 해도 부모 창으로 제어가 반환되지 않습니다.나는 이것을 모달 창으로 하는 것이 전혀 가능하지 않다고 생각한다.

이것을 시험해 보세요.

protected override void OnClosing(CancelEventArgs e)
{
    this.Visibility = Visibility.Hidden;
    e.Cancel = true;
}

닫힌 창을 표시하려고 하면 다음과 같은 예외가 발생합니다.

"가시성 또는 콜 표시, ShowDialog 또는 WindowInterop을 설정할 수 없습니다.Helper.InsureHandle은 창이 닫히면 자동으로 처리됩니다.

따라서 이 문제를 해결하려면 창의 가시성 옵션을 사용하는 것이 좋습니다.창의 가시성을 직접 닫지 않고 숨김 또는 접힘으로 설정해야 합니다.

this.가시성 = 시스템.창문들.가시성접힘 또는 숨김

다시 표시하려면 가시성을 Visible로 설정하기만 하면 됩니다.

this.가시성 = 시스템.창문들.가시성표시,

닫기 이벤트를 취소하고 가시성을 = hidden으로 설정하면 이 문제를 재정의할 수 있습니다.

Private Sub ChildWindow_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
        e.Cancel = True
        Me.Visibility = Windows.Visibility.Hidden
End Sub
public class MyWindow : Window

public MyWindow ()
    {
        InitializeComponent();            
        Closed += new System.EventHandler(MyWindow_Closed);
    }

private static MyWindow _instance;

public static MyWindow Instance
{
    if( _instance == null )
    {
        _instance = new Window();
    }
    return _instance();
}
void MyWindow_Closed(object sender, System.EventArgs e)
    {
         _instance = null;
    }

처리방법은 다음과 같습니다.

public partial class MainWindow 
{
    bool IsAboutWindowOpen = false;

    private void Help_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (!IsAboutWindowOpen)
        {
            var aboutWindow = new About();
            aboutWindow.Closed += new EventHandler(aboutWindow_Closed);
            aboutWindow.Show();
            IsAboutWindowOpen = true;
        }
    }

    void aboutWindow_Closed(object sender, EventArgs e)
    {
        IsAboutWindowOpen = false;
    }
}

내가 이해할 수 없는 논리일 수도 있지만 창문을 닫으면 되돌릴 수 없다.

창을 "닫고" 버튼을 눌러서 다시 열기를 원하는 경우 다음과 같이 숨길 수 있습니다.

 private MyWindow myWindow;
 private void OpenMyWindow_OnClick(object sender, RoutedEventArgs e)
 {
     if (myWindow == null)
     {
         myWindow = new MyWindow();
     }

     if(!myWindow.IsVisible)
     {
         myWindow.Show();
     }
     else
     {
         myWindow.Hide();
     }
 }

창을 닫을 수 있다면 닫힘 이벤트로 처리하기를 제안합니다.(제가 사용한 솔루션은 다음과 같습니다.)

private MyWindow myWindow;
private void OpenMyWindow_OnClick(object sender, RoutedEventArgs e)
{
    if (myWindow == null)
    {
        myWindow = new MyWindow();
        myWindow.Closed += OnMyWindowClosed;
    }

    if(!myWindow.IsVisible)
    {
        myWindow.Show();
    }
    else
    {
        myWindow.Hide();
    }
}

private void OnMyWindowClosed(object obj, EventArgs e)
{
    myWindow = null;
}

내가 누군가를 도왔길 바란다

나도 비슷한 문제가 있었다.따라서 모달 대화상자이지만 이 대화상자에는 기본 양식으로 전환해야 하는 "Select(선택)" 버튼이 있습니다(가능하면 모달 대화상자를 닫지 않고). 여기서 일부 영역을 선택한 다음 선택 정보가 포함된 모달 대화상자로 돌아갑니다.나는 모델리스 대화/쇼/숨김을 가지고 조금 놀려고 했지만, 그 후 win32 네이티브 함수 호출을 사용하여 코드화된, 좋은 (코드하기 쉬운) 해결책을 찾을 수 없었다.테스트한 내용 - winforms 및 xaml에서도 사용할 수 있습니다.

이 문제 자체는 간단한 문제가 아닙니다.따라서 사용자가 "선택"을 누르면 사용자가 무언가를 선택했다는 것을 잊어버리고 다른 선택 대화상자의 동일한 대화상자로 돌아갈 수 있습니다.그러면 같은 대화상자가 두 개 이상 나타날 수 있습니다.

정적 변수(인스턴스/부모)를 사용하여 이 문제에 대처하려고 합니다.순수한 winforms 또는 순수 wpf 기술을 사용하면 인스턴스에서 부모를 얻을 수 있습니다.부모 또는 인스턴스주인.

public partial class MeasureModalDialog : Window
{
    //  Dialog has "Select area" button, need special launch mechanism. (showDialog / SwitchParentChildWindows)
    public static MeasureModalDialog instance = null;
    public static object parent = null;

    static public void showDialog(object _parent)
    {
        parent = _parent;
        if (instance == null)
        {
            instance = new MeasureModalDialog();

            // Parent is winforms, child is xaml, this is just glue to get correct window owner to child dialog.
            if (parent != null && parent is System.Windows.Forms.IWin32Window)
                new System.Windows.Interop.WindowInteropHelper(instance).Owner = (parent as System.Windows.Forms.IWin32Window).Handle;

            // Enable parent window if it was disabled.
            instance.Closed += (_sender, _e) => { instance.SwitchParentChildWindows(true); };
            instance.ShowDialog();

            instance = null;
            parent = null;
        }
        else
        {
            // Try to switch to child dialog.
            instance.SwitchParentChildWindows(false);
        }
    } //showDialog

    public void SwitchParentChildWindows( bool bParentActive )
    {
        View3d.SwitchParentChildWindows(bParentActive, parent, this);
    }


    public void AreaSelected( String selectedAreaInfo )
    {
        if( selectedAreaInfo != null )     // Not cancelled
            textAreaInfo.Text = selectedAreaInfo;

        SwitchParentChildWindows(false);
    }

    private void buttonAreaSelect_Click(object sender, RoutedEventArgs e)
    {
        SwitchParentChildWindows(true);
        View3d.SelectArea(AreaSelected);
    }

    ...

public static class View3d
{

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool EnableWindow(IntPtr hWnd, bool bEnable);

    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool BringWindowToTop(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowEnabled(IntPtr hWnd);

    /// <summary>
    /// Extracts window handle in technology independent wise.
    /// </summary>
    /// <param name="formOrWindow">form or window</param>
    /// <returns>window handle</returns>
    static public IntPtr getHandle( object formOrWindow )
    {
        System.Windows.Window window = formOrWindow as System.Windows.Window;
        if( window != null )
            return new System.Windows.Interop.WindowInteropHelper(window).Handle;

        System.Windows.Forms.IWin32Window form = formOrWindow as System.Windows.Forms.IWin32Window;
        if (form != null)
            return form.Handle;

        return IntPtr.Zero;
    }

    /// <summary>
    /// Switches between modal sub dialog and parent form, when sub dialog does not needs to be destroyed (e.g. selecting 
    /// something from parent form)
    /// </summary>
    /// <param name="bParentActive">true to set parent form active, false - child dialog.</param>
    /// <param name="parent">parent form or window</param>
    /// <param name="dlg">sub dialog form or window</param>
    static public void SwitchParentChildWindows(bool bParentActive, object parent, object dlg)
    {
        if( parent == null || dlg == null )
            return;

        IntPtr hParent = getHandle(parent);
        IntPtr hDlg = getHandle(dlg);

        if( !bParentActive )
        {
            //
            // Prevent recursive loops which can be triggered from UI. (Main form => sub dialog => select (sub dialog hidden) => sub dialog in again.
            // We try to end measuring here - if parent window becomes inactive - 
            // means that we returned to dialog from where we launched measuring. Meaning nothing special needs to be done.
            //
            bool bEnabled = IsWindowEnabled(hParent);
            View3d.EndMeasuring(true);   // Potentially can trigger SwitchParentChildWindows(false,...) call.
            bool bEnabled2 = IsWindowEnabled(hParent);

            if( bEnabled != bEnabled2 )
                return;
        }

        if( bParentActive )
        {
            EnableWindow(hDlg, false);      // Disable so won't eat parent keyboard presses.
            ShowWindow(hDlg, 0);  //SW_HIDE
        }

        EnableWindow(hParent, bParentActive);

        if( bParentActive )
        {
            SetForegroundWindow(hParent);
            BringWindowToTop(hParent);
        } else {
            ShowWindow(hDlg, 5 );  //SW_SHOW
            EnableWindow(hDlg, true);
            SetForegroundWindow(hDlg);
        }
    } //SwitchParentChildWindows

    ...

같은 패러다임에서는 각 선택함수 콜체인이 스택을 소비하여 최종적으로 스택오버플로우가 발생하거나 부모창 상태 관리(이네이블/디세이블)에 문제가 발생할 수 있기 때문에 모델리스 대화상자가 없을 수 있습니다.

그래서 저는 이것이 꽤 가벼운 문제 해결이라고 생각합니다. 다소 복잡해 보일지라도 말이죠.

언급URL : https://stackoverflow.com/questions/3568233/wpf-cannot-reuse-window-after-it-has-been-closed

반응형