konrad
konrad

Reputation: 3706

returning an object from a class in C# (wpf)

I am trying to call a class called ThreadCreator() and then one of its methods called CreateWindow() and have it return a new window it created so that I can use that object somewhere else to invoke a method on it.

Here's the breakdown. My thread creator class that pops a new wpf window on a new thread:

using System;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.ComponentModel;
using System.Windows;

namespace Windamow
{
public static class ThreadCreator
{

    private static NewWindow W;

    public static NewWindow CreateWindow()
    {
        string appName = "";
        try
        {
            appName = Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location);
            const string IE_EMULATION = @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
            using (var fbeKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(IE_EMULATION, true))
            {
                fbeKey.SetValue(appName, 9000, Microsoft.Win32.RegistryValueKind.DWord);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(appName + "\n" + ex.ToString(), "Unexpected error setting browser mode!");
        }

        Thread t = new Thread(ThreadProc);
        t.SetApartmentState(ApartmentState.STA);
        t.Start();

        return W;
    }

    private static void ThreadProc(object obj)
    {
        W = new NewWindow();
        W.ShowDialog();
    }
}
}

New Window.xaml.cs looks like this:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Windamow
{
/// <summary>
/// Interaction logic for NewWindow.xaml
/// </summary>
public partial class NewWindow : Window
{
    string _message;
    public string Message
    {
        get { return _message; }
        set
        {
            _message = value;
            this.Dispatcher.Invoke(() => { this.webBrowser.NavigateToString(_message); });
        }
    }

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();

    IntPtr _handle;
    public IntPtr Handle { get { return _handle; } }

    int _pid;
    public int PID { get { return _pid; } }

    public NewWindow()
    {
        InitializeComponent();
    }

    private void Window_Closed(object sender, EventArgs e)
    {
        WindowNotifier.OnIamClosed(Handle);
    }

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        WindowNotifier.OnIamClosing(Handle);
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        _pid = Process.GetCurrentProcess().Id;
        _handle = GetForegroundWindow();

        WindowNotifier.OnIamCreated(Handle);
    }
}
}

What I am trying to do with the window is invoke a NavigateToString() method every time a method called CheckIfWindowOpen() is run. It will basically check if window is open and either open it and execute NavigateToString() or it will just execute NavigateToString() with possibly new inputs (refresh).

Here's how I am calling it:

namespace Windamow
{
public class Win32F
{
    [DllImport("user32.dll")]
    public static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
}

public class DynamoDataVizNodes
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetLastError();

    // test html string
    public static string HTMLString = "<html></html>";

    IntPtr HandleOfWindowOnNewThread;

    void WindowNotifier_IamCreatedEvent(IntPtr handle)
    {
        HandleOfWindowOnNewThread = handle;

        Debug.WriteLine(string.Format("I am created : {0}", handle));
    }

    void WindowNotifier_IamClosedEvent(IntPtr handle)
    {
        if (HandleOfWindowOnNewThread == handle)
            HandleOfWindowOnNewThread = IntPtr.Zero;

        Debug.WriteLine(string.Format("I am closed : {0}", handle));
    }

    void WindowNotifier_IamClosingEvent(IntPtr handle)
    {
        Debug.WriteLine(string.Format("I am closing : {0}", handle));
    }

    public static NewWindow window;

    public void CheckIfWindowOpen(string SourceString)
    {
        WindowNotifier.IamClosingEvent += WindowNotifier_IamClosingEvent;
        WindowNotifier.IamClosedEvent += WindowNotifier_IamClosedEvent;
        WindowNotifier.IamCreatedEvent += WindowNotifier_IamCreatedEvent;

        if (HandleOfWindowOnNewThread == IntPtr.Zero)
        {
            // create new window and set browser
            window = ThreadCreator.CreateWindow();
            window.Dispatcher.Invoke(() => { window.webBrowser.NavigateToString(SourceString); });

        }
        else
        { 
            // only set browser
            window.Dispatcher.Invoke(() => { window.webBrowser.NavigateToString(SourceString); });
        }  
    }
}
}

The error happens in the CheckIfWindowOpen() method when I am trying to return a NewWindow object to then invoke a NavigateToString() method on it. It basically returns null. Is there a way to make ThreadCreator.CreateWindow() return the created window?

Thanks!

Upvotes: 0

Views: 250

Answers (2)

AnjumSKhan
AnjumSKhan

Reputation: 9827

Solution1 : Without storing reference to NewWindow in MainWindow. Instead raising RefreshBrowser event from MainWindow. Dropbox link

Solution2 : Storing reference to NewWindow in MainWindow : New solution changed to your requirements : Dropbox link

  1. Thread creation routine doesn't wait for ThreadProcedure to run, so return value will be null always. But you can use this value in say some other event handler. Because once this routine finishes, variable window will contain valid value.

  2. However this is not proper way to do what you are trying to do.

  3. There is a cleaner way in today's event driven programming world. Remember, controls/windows fire events and we access the sender and get additional info using corresponding eventargs. Eg;

    private void Window_Loaded(object sender, RoutedEventArgs e)

  4. Following same pattern as above, we modify our delegates like below :

    public static class WindowNotifier2
    {
        public static event CreatedDelegateCallback IamCreatedEvent;
        public delegate void CreatedDelegateCallback(object sender, WindowNotifierEventArgs args);
    
        public static event ClosingDelegateCallback IamClosingEvent;
        public delegate void ClosingDelegateCallback(object sender, WindowNotifierEventArgs args);
    
        public static event ClosedDelegateCallback IamClosedEvent;
        public delegate void ClosedDelegateCallback(object sender, WindowNotifierEventArgs args);
    
    
        public static void OnIamCreated(object sender, WindowNotifierEventArgs args)
        {
            if (IamCreatedEvent != null)
                IamCreatedEvent(sender, args);
        }
    
        public static void OnIamClosing(object sender, WindowNotifierEventArgs args)
        {
            if (IamClosingEvent != null)
                IamClosingEvent(sender, args);
        }
    
        public static void OnIamClosed(object sender, WindowNotifierEventArgs args)
        {
            if (IamClosedEvent != null)
                IamClosedEvent(sender, args);
        }
    
    }
    
    public class WindowNotifierEventArgs : EventArgs
    {
        public IntPtr WindowHandle { get; set; }
    }
    

...

NewWindow.xaml.cs

private void Window_Loaded(object sender, RoutedEventArgs e)
{
   _pid = Process.GetCurrentProcess().Id;                    
   _handle = GetForegroundWindow();

   WindowNotifier2.OnIamCreated(this, new WindowNotifierEventArgs() { WindowHandle = _handle });
}      

MainWindow.xaml.cs

private void btnCheckNewWindow_Click(object sender, RoutedEventArgs e)
        {
            if (HandleOfWindowOnNewThread == IntPtr.Zero)
            {
                WindowNotifier2.IamCreatedEvent += (object source, WindowNotifierEventArgs args) =>
                {
                    _newWindow = (NewWindow)source;

                    HandleOfWindowOnNewThread = args.WindowHandle;
                    Debug.WriteLine(string.Format("I am created : {0}", args.WindowHandle));

                    _newWindow.tbMsgFromMainWindow.Dispatcher.InvokeAsync(() => { 
                        _newWindow.tbMsgFromMainWindow.Text = "I created you ! I am your creator."; 
                    });
                };

                ThreadCreator.CreateWindow();
            }
            else
            {
                _newWindow.tbMsgFromMainWindow.Dispatcher.InvokeAsync(() => { 
                    _newWindow.tbMsgFromMainWindow.Text = "I can see you are already running !"; 
                });
            }
        } 

Upvotes: 1

Wouter
Wouter

Reputation: 2958

W is only set on the another thread and when you return W it most like is still null. Besides that why do you try to run a new thread as dispatcher; have a look at the dispatcher run method.

Upvotes: 0

Related Questions