即使正在使用Dispatcher,也会抛出InvalidOperationException

时间:2015-02-26 22:23:28

标签: c# wpf ui-automation dispatcher

我必须在wpf中做一些用户行为回放。我让用户运行程序,然后我将文件写入我感兴趣的事件。现在我需要重新读取这些事件,然后让计算机以现实的方式调用它们(确切的时间不是重要)。我知道UI是变幻无常的,你必须担心线程所有权,但是,我通常使用Dispatcher。这一次,它不起作用。

我有一个全局定义的文本框:

TextBox fromBox; 

然后放在构造函数中:( myGrid是Canvas)

fromBox = textBoxFactory(260, 36, 244);  
fromBox.GotFocus += fromBox_GotFocus;  
fromBox.LostFocus += textBox_LostFocus;  
fromBox.TextChanged += fromBox_TextChanged;  
fromBox.Tag = "From TextBox";  
myGrid.Children.Add(fromBox); 

我的模拟鼠标点击的代码:

    public static void LeftMouseClick(int xpos, int ypos)
    {
        //Random rand = new Random(Environment.TickCount);
        double scale = 1.25;
        SetCursorPos((int)(xpos * scale), (int)(ypos * scale));
        mouse_event(MOUSEEVENTF_LEFTDOWN, (int)(xpos * scale), (int)(ypos * scale), 0, 0);
        Thread.Sleep(200);
        mouse_event(MOUSEEVENTF_LEFTUP, (int)(xpos * scale), (int)(ypos * scale), 0, 0);
    }

现在,在测试中,我知道这一行可以创建一个能够成功激活文本框的事件。

Dispatcher.BeginInvoke(new Action(() => LeftMouseClick(200, 200)));

但是,在我的播放中,它会抛出一个InvalidOperationException,因为该线程不拥有它。

播放时,我初始化一个计时器:

System.Timers.Timer viz = new System.Timers.Timer(30);
viz.Elapsed += viz_Elapsed;
viz.AutoReset = true;
viz.Enabled = true;

然后是经过的方法:

  void viz_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {

        if (indexer >= readText.Length) {
            ((System.Timers.Timer)sender).AutoReset = false;
            return;
        }
        String line = readText[indexer];

        string[] tokens = line.Split(':');

        if (tokens[4].Equals("Gaze"))
        {
            int x = Int32.Parse(tokens[5]);
            int y = Int32.Parse(tokens[6]);
            Dispatcher.BeginInvoke(new Action(() => UpdateUI(x, y)));
        }
        else if (tokens[4].Equals("FromBox"))
        {
            if (tokens[5].Equals("Focus")) {
                Dispatcher.BeginInvoke(new Action(() => LeftMouseClick(200, 200)));
            }
            else if (tokens[5].Equals("TextChanged"))
            {
                 Dispatcher.BeginInvoke(new Action(() =>fromBox.Text = tokens[6]));
            }
        }
        else
        {
            Console.WriteLine(tokens);
        }
        indexer++;
    }

最终将动态读取数字。这纯粹是概念代码的证明。用于更改文本的行有效,用于聚焦文本框的行则不然。为什么Dispatcher在这种情况下不起作用,会发生什么?

1 个答案:

答案 0 :(得分:0)

更新:答案的目的是避免访问文本框,无论操作如何。使用共享ViewModel来回传递状态操作的MVVM模型应该在非概念验证应用程序的下一次运行时考虑。

  

全局定义了一个文本框:

可以全局访问它,但文本框的性质仅限于GUI线程。


即使是读操作也会导致失败,因为文本框是在GUI线程上创建的。遗憾的是,并非所有内容都已被隔离,即使发送了该过程,文本框的重点也会导致问题。

如果将操作更改为View Model上的标志设置(或任何具有INotifyPropertyChanged的共享资源)并绑定到通过样式设置(?)会更好地关注文本框,那会更好。

我遇到了类似情况并在我的博客文章C# WPF: Linq Fails in BackgroundWorker DoWork Event上记录了它。