c # - окно WPF не будет обновлять определенные функции из отдельного потока

В моем основном классе я создаю окно wpf (p) и функцию для отображения окна с заданным сообщением на нем в метке (содержимом).

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();
        }
        ));
    }
}

Затем в другом классе в другом пространстве имен я называю это так.

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 = "";
        }
    }
}

Это отлично работает только в первый раз, если я снова вызову его с другим сообщением, оно не обновит сообщение, но уведомление все еще работает, оно просто отображает старое сообщение.

У меня также была эта проблема с изменением MainWindow.p.Opacity и MainWindow.p.Background

Без Dispatcher.Invoke я получаю сообщение об ошибке «не могу получить доступ к объекту, потому что им владеет другой поток».

Если я уберу Commands.MyGlobals.inputtext = message; и поставлю Commans.MyGlobals.inputtext = "test1" перед MainWindow.NotifProc("test1");, это все равно не сработает.

Я также попытался создать такой класс:

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

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

и добавив:

p.content.Refresh();

но не повезло. Любая помощь будет здорово. :/

РЕДАКТИРОВАТЬ

Мне удалось найти исправление, хотя оно, вероятно, не самое лучшее: вместо того, чтобы создавать новое окно в начале, я создаю новое окно каждый раз, когда вызывается анимация окна. VS сказал, что поток должен быть STA, поэтому я назвал его в потоке STA и, наконец, добавил проверку в конце анимации окна, чтобы нельзя было создавать новые окна, пока не будет выполнено первое.

Моя новая функция NotifProc выглядит так:

    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();
    }

person MrMister    schedule 23.10.2017    source источник
comment
Возможный дубликат Как обновить графический интерфейс с другой поток на C#?   -  person MickyD    schedule 23.10.2017
comment
Почему люди всегда настаивают на создании потока только для того, чтобы заснуть на несколько секунд?   -  person MickyD    schedule 23.10.2017
comment
Кажется, здесь не задействован никакой другой поток, кроме потока пользовательского интерфейса. Вызов Dispatcher.Invoke не создает новый поток, а просто запускает делегат в потоке пользовательского интерфейса. Однако поток пользовательского интерфейса блокируется вызовом Thread.Sleep. Вместо этого используйте await Task.Delay(...).   -  person Clemens    schedule 23.10.2017
comment
У меня есть другой поток, из которого я вызываю NotifProc. Если я удалю Dispatcher.Invoke, я не смогу получить доступ к объекту, потому что им владеет другой поток   -  person MrMister    schedule 23.10.2017


Ответы (1)


Вместо того, чтобы вызывать Thread.Sleep в потоке пользовательского интерфейса и, таким образом, блокировать его, вы должны просто анимировать положение окна следующим образом:

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);
}

Если вы хотите вызвать метод из потока, отличного от потока пользовательского интерфейса, сделайте это следующим образом:

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

or

MainWindow.p.Dispatcher.Invoke(() => NotifProc(...));
person Clemens    schedule 23.10.2017
comment
Я пробовал это, но я все еще не могу получить доступ к объекту, потому что им владеет другой поток. Большое спасибо за помощь кстати - person MrMister; 23.10.2017
comment
Вы пробовали Application.Current.Dispatcher.Invoke, как показано в ответе? Возможно, вам следует отредактировать свой вопрос и показать нам, что вы пытаетесь сделать. - person Clemens; 23.10.2017
comment
Я попробовал Application.Current.Dispatcher.Invoke, он, похоже, не нашел его, поэтому я попробовал MainWindow.p, и он нашел его, но не смог получить доступ... Извините, я не лучший в объяснениях, я начал программировать только неделю или назад. Исходный поток запускает распознаватель речи, а затем отправляет входные данные в public void в обычном классе в другом пространстве имен. Я могу перемещать окно и т. д., но не могу изменить цвет, текст или прозрачность более одного раза. - person MrMister; 23.10.2017
comment
Я также сделал глобальную строку для текста метки, поэтому получение строки из сообщения не должно быть проблемой. - person MrMister; 23.10.2017