WPF:使用计时器在线程中构建队列

时间:2022-06-13 21:01:43

With reference to the Software Project I am currently working on.

参考我目前正在进行的软件项目。

I have the below methods that basically move a canvas with a Timer:

我有以下方法基本上使用Timer移动画布:

DispatcherTimer dt = new DispatcherTimer(); //global
public void Ahead(int pix)
    {
            var movx = 0;
            var movy = 0;
            dt.Interval = TimeSpan.FromMilliseconds(5);
            dt.Tick += new EventHandler((object sender, EventArgs e) =>
            {
                if (movx >= pix || movy >= pix)
                {
                    dt.Stop();
                    return;
                }
                Bot.Body.RenderTransform = new TranslateTransform(movx++, movy++);
            });
            dt.Start();
    }
public void TurnLeft(double deg)
    {

        var currAngle = 0;
        dt.Interval = TimeSpan.FromMilliseconds(5);
        dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
        {
            if (currAngle <= (deg - (deg * 2)))
            {
                dt.Stop();
            }
            Bot.Body.RenderTransform = new RotateTransform(currAngle--, BodyCenter.X, BodyCenter.Y);
        });
        dt.Start();
    }

Now, from another library, these methods are called like such:

现在,从另一个库中,这些方法被调用如下:

public void run()
{
    Ahead(200);
    TurnLeft(90);
}

Now of course, I want these animations to happen after another, but what is happening is that the dt.Tick event handler of the DispatchTimer is being overwritten when the second method (in this case, TurnLeft(90)) is invoked and thus, only the second method gets executed as it should.

当然,我希望这些动画能够在另一个之后发生,但是发生的事情是当调用第二个方法(在本例中为TurnLeft(90))时,DispatchTimer的dt.Tick事件处理程序被覆盖,因此,只有第二种方法才会被执行。

I need to create some sort of queue that will allow me to push and pop methods to that queue so that dt (the DispatchTimer timer) executes them one by one...in the order they are in the 'queue'

我需要创建一些队列,允许我将方法推送到该队列,以便dt(DispatchTimer计时器)逐个执行它们......按照它们在“队列”中的顺序

Any way I can go about doing this ? Am I on the right track here, or completely off course?

我有什么办法可以做到这一点?我是在正确的轨道上,还是完全偏离正轨?

2 个解决方案

#1


When you call Invoke() or BeginInvoke() on the Dispatcher, the operation will be queued up and run when the thread associated with the Dispatcher is free. So instead of using the Tick event, use the overload of Dispatcher.Invoke that takes a Timespan.

当您在Dispatcher上调用Invoke()或BeginInvoke()时,操作将排队并在与Dispatcher关联的线程空闲时运行。因此,不要使用Tick事件,而是使用带有Timespan的Dispatcher.Invoke的重载。

#2


I have fixed this problem by myself. What I did was create a global Queue of type Delegate and instead of executing the methods directly, I add them to this queue.

我自己解决了这个问题。我所做的是创建一个类型为Delegate的全局队列,而不是直接执行这些方法,我将它们添加到此队列中。

Then I would have a separate thread in the constructor that will dequeue methods one by one and executing them:

然后我会在构造函数中有一个单独的线程,它将逐个出列方法并执行它们:

    Queue<TimerDelegate> eventQueue = new Queue<TimerDelegate>();

    public Vehicle(IVehicle veh, Canvas arena, Dispatcher battleArenaDispatcher)
    {
         DispatcherTimer actionTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
         actionTimer.Tick += new EventHandler(delegate(object sender, EventArgs e)
    {
        if (IsActionRunning || eventQueue.Count == 0)
        {
            return;
        }
        eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
    });
    actionTimer.Start();
    }

    public void TurnRight(double deg)
    {
        eventQueue.Enqueue((TimerDelegate)delegate(DispatcherTimer dt)
        {
            IsActionRunning = true;
            var currAngle = 0;
            dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
            {
                lock (threadLocker)
                {
                    if (currAngle >= deg)
                    {
                        IsActionRunning = false;
                        dt.Stop();
                    }
                    Rotator_Body.Angle++;
                    currAngle++;
                }
            });
            dt.Start();
        });
    }

#1


When you call Invoke() or BeginInvoke() on the Dispatcher, the operation will be queued up and run when the thread associated with the Dispatcher is free. So instead of using the Tick event, use the overload of Dispatcher.Invoke that takes a Timespan.

当您在Dispatcher上调用Invoke()或BeginInvoke()时,操作将排队并在与Dispatcher关联的线程空闲时运行。因此,不要使用Tick事件,而是使用带有Timespan的Dispatcher.Invoke的重载。

#2


I have fixed this problem by myself. What I did was create a global Queue of type Delegate and instead of executing the methods directly, I add them to this queue.

我自己解决了这个问题。我所做的是创建一个类型为Delegate的全局队列,而不是直接执行这些方法,我将它们添加到此队列中。

Then I would have a separate thread in the constructor that will dequeue methods one by one and executing them:

然后我会在构造函数中有一个单独的线程,它将逐个出列方法并执行它们:

    Queue<TimerDelegate> eventQueue = new Queue<TimerDelegate>();

    public Vehicle(IVehicle veh, Canvas arena, Dispatcher battleArenaDispatcher)
    {
         DispatcherTimer actionTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
         actionTimer.Tick += new EventHandler(delegate(object sender, EventArgs e)
    {
        if (IsActionRunning || eventQueue.Count == 0)
        {
            return;
        }
        eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
    });
    actionTimer.Start();
    }

    public void TurnRight(double deg)
    {
        eventQueue.Enqueue((TimerDelegate)delegate(DispatcherTimer dt)
        {
            IsActionRunning = true;
            var currAngle = 0;
            dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
            {
                lock (threadLocker)
                {
                    if (currAngle >= deg)
                    {
                        IsActionRunning = false;
                        dt.Stop();
                    }
                    Rotator_Body.Angle++;
                    currAngle++;
                }
            });
            dt.Start();
        });
    }