【问题标题】:c# - WPF Window won't update certain features from separate threadc# - WPF 窗口不会从单独的线程更新某些功能
【发布时间】:2017-10-23 04:23:37
【问题描述】:

在我的主类中,我创建了一个 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 = "";
        }
    }
}

仅在第一次时工作得很好,如果我用不同的消息再次调用它,它不会更新消息,但 notif 仍然有效,它只会显示旧消息。

我在更改MainWindow.p.OpacityMainWindow.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();
    }

【问题讨论】:

  • 为什么人们总是坚持生成一个线程只是为了一次睡几秒钟?
  • 这里似乎除了 UI 线程之外没有其他线程。调用Dispatcher.Invoke 不会创建新线程,而只是在 UI 线程上运行委托。但是,调用 Thread.Sleep 会阻止 UI 线程。请改用await Task.Delay(...)
  • 我有另一个线程,我从中调用 NotifProc。如果我删除 Dispatcher.Invoke,我会返回“无法访问对象,因为不同的线程拥有它”

标签: c# wpf multithreading push-notification self-updating


【解决方案1】:

与其在 UI 线程中调用 Thread.Sleep 并因此阻塞它,您应该像这样简单地为 Window 位置设置动画:

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

如果您想从 UI 线程以外的线程调用方法,请这样做:

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

MainWindow.p.Dispatcher.Invoke(() => NotifProc(...));

【讨论】:

  • 我试过这个,但我仍然得到“无法访问对象,因为不同的线程拥有它”。感谢一堆帮助顺便说一句
  • 您是否尝试过Application.Current.Dispatcher.Invoke,如答案所示?您或许应该编辑您的问题并向我们展示您想要做什么。
  • 我试过Application.Current.Dispatcher.Invoke它似乎没有找到它所以我尝试了 MainWindow.p 并找到它但它返回“无法访问..”。对不起,我不是最擅长解释我大约一周前才开始编程的。原始线程启动语音识别器,然后将输入发送到不同命名空间中常规类的公共 void。我可以移动窗口等,但不能多次更改颜色、文本或不透明度。
  • 我还为标签文本创建了一个全局字符串,所以从消息中获取字符串应该不是问题。
猜你喜欢
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-29
相关资源
最近更新 更多