辅助方法检查窗口是否在另一个线程上打开

时间:2015-09-27 22:27:37

标签: c# wpf multithreading

我找到了这个解决方案,用于检查窗口是否打开:

How do I know if a WPF window is opened

因为我的wpf窗口在另一个线程上,所以它向我抛出一个错误。有没有办法继续使用它?

解决方案:

**data dump**
defaultdict(<class 'list'>, {<BadgeType: Talent>: [<BadgeAssertion: Blender Blue Belt>], <BadgeType: Achievement>: [<BadgeAssertion: MyBadge#1>, <BadgeAssertion: MyBadge#1>, <BadgeAssertion: MyBadge#2>], <BadgeType: Award>: [<BadgeAssertion: Copy of Copy of Blenbade>]})

**dataitems**
Talent: [<BadgeAssertion: Blender Blue Belt>]
Achievement: [<BadgeAssertion: MyBadge#1>, <BadgeAssertion: MyBadge#1>, <BadgeAssertion: MyBadge#2>]
Award: [<BadgeAssertion: Copy of Copy of Blenbade>]

**data.items**

**Not found test**
DEBUG WARNING: undefined template variable [i_dont_exist] not found 

3 个答案:

答案 0 :(得分:2)

我已经创建了一个示例应用程序,可以在花了一整天后解决您的问题。

Solution can be downloaded here

它的作用: 单击按钮在新线程上创建窗口。在新线程上为您创建一个新窗口。创建此新窗口时,主窗口中的此按钮将被禁用。当您关闭新窗口时,主窗口中的创建按钮将再次启用。

如果它不符合您的需求,请告诉您的要求,我会改进它。使用纯Win32函数也可以完成相同的操作,而无需使用我们的事件桥类。我正在做。我很快就会发布win32版本。

  1. 我在一个单独的线程上创建NewWindow。如果你关闭MainWindow,NewWindow仍然会在新线程上运行。
  2. 我将它完全分开,因为MainWindow中没有使用任何实例来指向NewWindow。为了解决这个问题,我使用的是Win32句柄。
  3. 为了让NewWindow向MainWindow发送通知,我使用静态类WindowNotifier和静态事件。这个类充当了两者之间的桥梁。在NewWindow中,Closing / Closed / Loaded事件用于触发事件。
  4. MainWindow处理此静态类的各种事件以保持有关NewWindow的更新。
  5. 使用的Win32函数:

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    
    [DllImport("user32.dll")]
    public static extern bool IsWindowVisible(IntPtr hWnd);
    
  6. ThreadCreator.cs

    public static class ThreadCreator
        {
            private static NewWindow W;
    
            public static void CreateWindow()
            {
                Thread t = new Thread(ThreadProc);
                t.SetApartmentState(ApartmentState.STA);
                t.Start();
            }
    
            private static void ThreadProc(object obj)
            {
                W = new NewWindow();
                W.ShowDialog();            
            }
        }
    

    WindowNotifier.cs

    public static class WindowNotifier
        {
            public static event CreatedDelegateCallback IamCreatedEvent;
            public delegate void CreatedDelegateCallback(IntPtr handle);
    
            public static event ClosingDelegateCallback IamClosingEvent;
            public delegate void ClosingDelegateCallback (IntPtr handle);
    
            public static event ClosedDelegateCallback IamClosedEvent;
            public delegate void ClosedDelegateCallback(IntPtr handle);
    
            public static void OnIamCreated(IntPtr handle)
            {
                if (IamCreatedEvent != null)
                    IamCreatedEvent(handle);
            }        
    
            public static void OnIamClosing(IntPtr handle)
            {
                if (IamClosingEvent != null)
                    IamClosingEvent(handle);
            }
    
            public static void OnIamClosed(IntPtr handle)
            {
                if (IamClosedEvent != null)
                    IamClosedEvent(handle);
            }
        }
    

    MainWindow.xaml.cs

    ...
        void WindowNotifier_IamCreatedEvent(IntPtr handle)
                {
                    HandleOfWindowOnNewThread = handle;
    
                    Debug.WriteLine(string.Format("I am created : {0}", handle));
    
                    btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = false);
                }
    
                void WindowNotifier_IamClosedEvent(IntPtr handle)
                {
                    if (HandleOfWindowOnNewThread == handle)
                        HandleOfWindowOnNewThread = IntPtr.Zero;
    
                    Debug.WriteLine(string.Format("I am closed : {0}", handle));
    
                    btnCreateNewWindow.Dispatcher.Invoke(() => btnCreateNewWindow.IsEnabled = true);
                }
    ...
    

    NewWindow.xaml.cs

    ...
            private void Window_Closed(object sender, EventArgs e)
            {
                WindowNotifier.OnIamClosed(Handle);
            }
    
            private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                WindowNotifier.OnIamClosing(Handle);
            }
    
            // To get correct handle we need to ensure Window is fully created and active
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                _handle = GetForegroundWindow();
    
                WindowNotifier.OnIamCreated(Handle);
            }       
    ...
    

答案 1 :(得分:1)

Dispatcher在这里没有帮助,因为当在另一个线程上创建窗口时,它不包含在Application.Windows集合中,而是包含在由于某种原因未被暴露的集合中(称为{{3 }})。很快,没有正式的方法可以做到这一点。当然,您可以根据自己的风险使用反思。

但是如果你的窗口在UI线程上并且你只想从另一个线程调用该函数,那么你可以使用这样的东西

Application.Current.Dispatcher.Invoke(() => IsWindowOpen<...>(...))

或更好地将辅助方法更改为

public static bool IsWindowOpen<T>(string name = "") where T : Window
{
    return Application.Current.Dispatcher.Invoke(() => string.IsNullOrEmpty(name)
       ? Application.Current.Windows.OfType<T>().Any()
       : Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name)));
}

编辑以下是目前有效的内容,但未来可能会发生变化,如上所述,请自行承担风险

public static class WindowUtils
{
    public static bool IsWindowOpen<T>(string name = "") where T : Window
    {
        return FindWindow<T>(name) != null;
    }
    public static T FindWindow<T>(string name = "") where T : Window
    {
        return FindWindow<T>(WindowsInternal, name) ?? FindWindow<T>(NonAppWindowsInternal, name);
    }
    private static T FindWindow<T>(Func<Application, WindowCollection> windowListAccessor, string name = "") where T : Window
    {
        bool matchName = !string.IsNullOrEmpty(name);
        var windowList = windowListAccessor(Application.Current);
        for (int i = windowList.Count - 1; i >= 0; i--)
        {
            var window = windowList[i] as T;
            if (window != null && (!matchName || window.Name == name)) return window;
        }
        return null;
    }
    private static readonly Func<Application, WindowCollection> WindowsInternal = GetWindowCollectionAccessor("WindowsInternal");
    private static readonly Func<Application, WindowCollection> NonAppWindowsInternal = GetWindowCollectionAccessor("NonAppWindowsInternal");
    private static Func<Application, WindowCollection> GetWindowCollectionAccessor(string propertyName)
    {
        var property = typeof(Application).GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        return (Func<Application, WindowCollection>)Delegate.CreateDelegate(
            typeof(Func<Application, WindowCollection>), property.GetMethod);
    }
}

答案 2 :(得分:0)

如果您从IsWindowOpen方法返回窗口。你可以使用窗口上的InvokeBeginInvoke来分配创建窗口的线程上的工作。