0

In my main class I create a wpf window (p) and a function to display the window with a given message on it in a label (content).

public partial class MainWindow : Window
{
    ///Creates Window
    public static Wils0n.Window1 p = new Wils0n.Window1();

    /// <summary>
    /// creates notif bar with message
    /// </summary>
    /// <param name="message">Message for notif bar</param>
    public static void NotifProc(string message)
    {
        ///sets global string to message
        Commands.MyGlobals.inputtext = message;

        p.Dispatcher.Invoke(new Action(delegate ()
        {
        MainWindow.p.Topmost = true;
        MainWindow.p.Top = 0;

        ///sets label content to global string (this only works once)
        MainWindow.p.content.Content = Commands.MyGlobals.inputtext;

        MainWindow.p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (MainWindow.p.Width / 2);
        MainWindow.p.Show();
        Thread.Sleep(2000);
        while (MainWindow.p.Top != -114)
        {
            MainWindow.p.Top -= 3;
            Thread.Sleep(15);
        }
        MainWindow.p.Hide();
        }
        ));
    }
}

Then in another class in another namespace I call it like such..

namespace Wilson.Utils
{
    class Commands
    {
        public void start()
        {
             ///creates notification window thats reads "hello world"
             MainWindow.NotifProc("hello world");

             ///creates notification window thats reads "test1"
             ///For some reason reads "hello world"
             MainWindow.NotifProc("test1");

             ///creates notification window thats reads "test2"
             ///For some reason reads "hello world"
             MainWindow.NotifProc("test2");            
        }

        ///creates global string
        public static class MyGlobals
        {
            public static string inputtext = "";
        }
    }
}

This works perfectly fine the first time only, if I call it again it with a different message it wont update the message but the notif still works it just displays the old message.

I have also had this problem with changing MainWindow.p.Opacity and MainWindow.p.Background

Without Dispatcher.Invoke I get an error reading "cannot access object because a different thread owns it".

If I remove Commands.MyGlobals.inputtext = message; and put Commans.MyGlobals.inputtext = "test1" before MainWindow.NotifProc("test1"); it still doesn't work.

I have also tried creating a class like such:

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate () { };

    public static void Refresh(this UIElement uiElement)
    {
        uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }
}

and adding:

p.content.Refresh();

but had no luck. Any help would be great. :/

EDIT

I managed to find a fix although it is probably not the best: instead of creating a new window at the beginning I create a new window each time the window animation is called. VS said the thread must be STA so I called it in an STA thread and lastly I added a check at the end of the window animation so that no new windows can be created until the first is done.

My new NotifProc function looks like this:

    public static void NotifProc(string message)
    {
        Tools.MyGlobals.notifmessage = message;
        var thread = new Thread(new ThreadStart(STAThread));
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
    }
    private static void STAThread()
    {
        Wils0n.Window1 p = new Wils0n.Window1();
        p.content.Content = Tools.MyGlobals.notifmessage;
        p.content.Refresh();
        p.Topmost = true;
        p.Top = 0;
        p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
        p.Show();

        Thread.Sleep(2000);
        while (p.Top != -114)
        {
            p.Top -= 3;
            Thread.Sleep(15);
        }
        p.Close();
    }
MrMister
  • 1
  • 3
  • Possible duplicate of [How do I update the GUI from another thread in C#?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread-in-c) –  Oct 23 '17 at 04:40
  • Why do people always insist on spawning a thread only to sleep for seconds at a time? –  Oct 23 '17 at 04:41
  • 1
    There seems to be no other thread involved here than the UI thread. Calling `Dispatcher.Invoke` does not create a new thread, but just runs the delegate on the UI thread. However, the UI thread is blocked by calling Thread.Sleep. Use `await Task.Delay(...)` instead. – Clemens Oct 23 '17 at 05:32
  • I have another thread in which i am calling NotifProc from. If I remove the Dispatcher.Invoke I get back "cannot access object because a different thread owns it" – MrMister Oct 23 '17 at 05:45

1 Answers1

0

Instead of calling Thread.Sleep in the UI thread, and thus blocking it, you should simply animate the Window position like this:

public static void NotifProc(string message)
{
    p.Content.Content = message;
    p.Left = SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
    p.Top = 0;
    p.Topmost = true;
    p.Show();

    var animation = new DoubleAnimation
    {
        To = -114,
        BeginTime = TimeSpan.FromSeconds(2),
        Duration = TimeSpan.FromSeconds(0.57)
    };

    animation.Completed += (s, e) => p.Hide();

    p.BeginAnimation(TopProperty, animation);
}

If you want to call the method from a thread other than the UI thread, do it like this:

Application.Current.Dispatcher.Invoke(() => NotifProc(...));

or

MainWindow.p.Dispatcher.Invoke(() => NotifProc(...));
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • I tried this but I'm still getting "cannot access object because a different thread owns it". Thanks a bunch for helping btw – MrMister Oct 23 '17 at 06:09
  • Did you try `Application.Current.Dispatcher.Invoke` as shown in the answer? You should perhaps edit your question and show us what you're trying to do. – Clemens Oct 23 '17 at 06:11
  • I tried `Application.Current.Dispatcher.Invoke` it didnt seem to find it so I tried MainWindow.p and it found it but it returned "cannot access..". I am sorry I am not the best at explaining I only started programming about a week or ago. The original thread starts a speech recognizer then sends the input to a public void in a regular class in a different namespace. I am able to move the window etc. but not able to change the color, text or opacity more than once. – MrMister Oct 23 '17 at 06:23
  • I have also made a global string for the label text so getting the string from message shouldnt be a problem. – MrMister Oct 23 '17 at 06:28