2016-02-06 3 views
0

люди, я столкнулся с проблемой, когда собирался показать моей дочери силу анимации WPF по решению проблемы Ханоя. Коды, связанные с проблемой, приведены ниже:wpf можно ли блокировать поток ui во время анимации?

void MoveSet(Disk[] disks, int maxIndex, char from, char via, char to) 
    { 
     if (maxIndex > 0) MoveSet(disks, maxIndex - 1, from, to, via); 
     MoveOne(disks[maxIndex], from, to); 
     if (maxIndex > 0) MoveSet(disks, maxIndex - 1, via, from, to); 
    } 

Приведенные выше коды перемещают диски рекурсивно. И я добавил анимацию в метод MoveOne следующим образом:

void MoveOne(Disk disk, char from, char to) 
{ 
    // set animation parameters 
    ... upAnimation... 
    ... levelShiftAnimation... 
    ... downAnimation... 

    Storyboard.SetTarget(upAnimation, disk); 
    Storyboard.SetTarget(levelShiftAnimation, disk); 
    Storyboard.SetTarget(downAnimation, disk); 
    storyboard.Begin(disk); 
} 

Эти коды работают хорошо. Но все анимации работают почти одновременно. После нескольких неупорядоченных анимаций все диски изменили положение. Это выглядит не круто. Поэтому я хочу показать анимацию каждого диска по одному. Я изменил метод MoveOne и внесли такие изменения:

void MoveOne(Disk disk, char from, char to) 
{ 
    ... 
    Storyboard.SetTarget(upAnimation, disk); 
    Storyboard.SetTarget(levelShiftAnimation, disk); 
    Storyboard.SetTarget(downAnimation, disk); 

    AutoResetEvent signaler=new AutoResetEvent(false); 
    EventHandler eh = null; 
    eh = (s, e) => 
    { 
     storyboard.Completed -= eh; 
     signaler.Set(); 
    }; 
    storyboard.Completed+=eh; 
    storyboard.Begin(disk); 
    signaler.WaitOne(); 
} 

Вышеуказанные модификации сделали всю программу застрявшей. Я думаю, причина в том, что и анимация, и метод MoveOne работают только в одном потоке пользовательского интерфейса, блокируя блоки метода MoveOne и анимацию. Поэтому я попытался создать и запустить (используя диспетчер UI для вызова) анимацию в другой вновь созданной задаче, она еще не работала. Наконец-то я определил свои настоящие требования. Я хочу запустить одну анимацию и заблокировать другие анимации, и все они работают на одном единственном потоке пользовательского интерфейса. Кажется, это противоречиво. Я не знаю, есть ли у меня неправильное понимание. И есть ли какое-либо решение по этому поводу?

+0

Попробуйте использовать анимацию ключевого кадра и используйте неперекрывающийся KeyTime в каждом кадре. См. Эту ссылку MSDN для примеров: https://msdn.microsoft.com/en-us/library/ms742524%28v=vs.100%29.aspx – Peter

ответ

1

Вы можете обернуть выполнение хранилища в Задаче и await этой задачей.

Это может выглядеть так, как описано в this answer. В принципе, вы подписываетесь на событие CompletedStoryboard. Тогда вы можете легко дождаться результата. (код слегка принят из предоставленной ссылки).

public static class StoryboardExtensions 
{ 
    public static Task BeginAsync(this Storyboard storyboard, FrameworkContentElement element) 
    { 
     System.Threading.Tasks.TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); 
     if (storyboard == null) 
      tcs.SetException(new ArgumentNullException()); 
     else 
     { 
      EventHandler onComplete = null; 
      onComplete = (s, e) => { 
       storyboard.Completed -= onComplete; 
       tcs.SetResult(true); 
      }; 
      storyboard.Completed += onComplete; 
      storyboard.Begin(element); 
     } 
     return tcs.Task; 
    } 
} 

В вашем случае, все, что вам нужно сделать, это заменить вызов storyboard.Begin(disc); в вашем MoveOn -метода с await storyboard.BeginAsync(disc);.

С помощью этого изменения вы включаете подход, основанный на событиях (с использованием события Completed), в ожидаемую задачу, которая упрощает обработку.

Смежные вопросы