2

こんにちは、私が抱えている問題を理解できません

private void StartReplay(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i <= 40; i++)
        {
            if (leftCounter < 20)
            {
                leftCounter++;
                leftCounter2 = leftCounter;
                Canvas.SetLeft(Kwadracik, leftCounter);
            }
            else
            {
                leftCounter2--;

                Canvas.SetLeft(Kwadracik, leftCounter2);
            }
        }
    }

今なら2通り

1) ボタンをクリックするたびにループを削除すると、 leftCounter が 1 増加し、キャンバスが移動します。左よりも右に 20 クリックします。期待どおりに動作します。左よりもまず右へ

2)ボタンをクリックしてループで実行すると、ループが実行されます。しかし、期待どおりに機能するとは限りません。右に 20、左に 20 になると予想しました (20+20 = 40)。しかし、いいえ。両側に動いているわけではありません。最後までジャンプするからただ立っているだけです。右への動きが見えません。ループステップごとに小さな遅延が発生しても、GUI がフリーズし、ループが終了した後にフリーズが解除され、要素が元の場所に移動します

なぜこのような違いがあるのでしょうか? どうすればそれを乗り越えることができますか?

この背後にある XAML

    <Canvas x:Name="canvas" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Rectangle Name="Kwadracik" Width="20" Height="20" Canvas.Left="0" Canvas.Top="0" Fill="Blue" />
    </Canvas>
4

1 に答える 1

2

これは、おそらく GUI スレッドから StartReplay を呼び出しているためです。スレッド処理は、関数が終了した後にのみ続行されます。つまり、関数が完了するまで、GUI は変更を処理できません。GUIスレッドでもあるボタンクリックハンドラーに変更を加えると、関数を終了し、その後GUIに変更が反映されます。そのため、最初の方法はクリックごとに機能します。

あなたができることは、ワーカースレッドを開始してそこで変更を加えることです。または、これはアニメーションのような動作のように見えるため、タイマーを使用します。

更新: 例 1:

DispatcherTimer は、呼び出しスレッドでコールバックを実行します。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;


namespace CanvasAnimation
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer uiTimer;
        double directionDelta = 1.0;

        public MainWindow()
        {
            InitializeComponent();
            uiTimer = new DispatcherTimer(); //This timer is created on GUI thread.
            uiTimer.Tick += new EventHandler(uiTimerTick);
            uiTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000/25); // 25 ticks per second
            uiTimer.Start();
        }

        private void uiTimerTick(object sender, EventArgs e)
        {
            double currentLeft = Canvas.GetLeft(Kwadracik);

            if (currentLeft < 0)
            {
                directionDelta = 1.0;
            }
            else if (currentLeft > 80)
            {
                directionDelta = -1.0;
            }

            currentLeft += directionDelta;

            Canvas.SetLeft(Kwadracik, currentLeft);
        }
    }
}

例 2: シンプルなタイマーの使用

    using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace CanvasAnimation
{
    /// <summary>
    /// Interaction logic for WorkerTimer.xaml
    /// </summary>
    public partial class WorkerTimer : Window
    {
        Timer timer;
        double directionDelta = 1.0;

        public WorkerTimer()
        {
            InitializeComponent();
            timer = new Timer(this.timerTick, this, 0, 1000 / 25); // 25 fPS timer
        }

        protected void timerTick(Object stateInfo)
        {
            //This is not a GUI thread!!!!
            //So we need to Invoke delegate with Dispatcher
            this.Dispatcher.Invoke(new MoveCanvasDelegate(this.moveCanvas), null);
        }

        protected delegate void MoveCanvasDelegate();
        protected void moveCanvas()
        {
            //This function must be called on GUI thread!!!

            double currentLeft = Canvas.GetLeft(Kwadracik);

            if (currentLeft < 0)
            {
                directionDelta = 1.0;
            }
            else if (currentLeft > 80)
            {
                directionDelta = -1.0;
            }

            currentLeft += directionDelta;

            Canvas.SetLeft(Kwadracik, currentLeft);
        }
    }
}

同じ手法が BackgroundWorker またはその他の非 GUI スレッドに適用されます。

于 2013-02-19T18:14:04.740 に答える