Анимация WPF медленная, когда мышь находится в окне

Я пытаюсь создать движущийся прямоугольник (прямоугольник). Это соответствующий код:

let timer = new Threading.DispatcherTimer();
timer.Tick.Add(fun _ -> Canvas.SetLeft(rect, Canvas.GetLeft(rect)+0.01) |> ignore)
timer.Start()

Проблема в том, что анимация замедляется, если я двигаю мышью по окну. Если я установлю интервал таймера:

timer.Interval <- TimeSpan.FromMilliSeconds(30.0)

Тогда анимация вообще не работает. Что я делаю неправильно? Спасибо.


person Jules    schedule 26.12.2008    source источник


Ответы (1)


У меня есть два предложения:

1) если вы используете интерактивную проверку f #, вы установили цикл событий WFP. Интерактивный F # по умолчанию устанавливает цикл событий winforms, существует некоторая совместимость между циклами событий winforms и WFP, но не все работает правильно. Для установки цикла событий используйте следующий скрипт:

#light

// When running inside fsi, this will install a WPF event loop
#if INTERACTIVE

#I "c:/Program Files/Reference Assemblies/Microsoft/Framework/v3.0";;
#I "C:/WINDOWS/Microsoft.NET/Framework/v3.0/WPF/";;
#r "presentationcore.dll";;
#r "presentationframework.dll";;
#r "WindowsBase.dll";;

module WPFEventLoop = 
    open System
    open System.Windows
    open System.Windows.Threading
    open Microsoft.FSharp.Compiler.Interactive
    open Microsoft.FSharp.Compiler.Interactive.Settings
    type RunDelegate<'b> = delegate of unit -> 'b 

    let Create() = 
        let app  = 
            try 
                // Ensure the current application exists. This may fail, if it already does.
                let app = new Application() in 
                // Create a dummy window to act as the main window for the application.
                // Because we're in FSI we never want to clean this up.
                new Window() |> ignore; 
                app 
             with :? InvalidOperationException -> Application.Current
        let disp = app.Dispatcher
        let restart = ref false
        { new IEventLoop with
             member x.Run() =   
                 app.Run() |> ignore
                 !restart
             member x.Invoke(f) = 
                 try disp.Invoke(DispatcherPriority.Send,new RunDelegate<_>(fun () -> box(f ()))) |> unbox
                 with e -> eprintf "\n\n ERROR: %O\n" e; rethrow()
             member x.ScheduleRestart() =   ()
                 //restart := true;
                 //app.Shutdown()
        } 
    let Install() = fsi.EventLoop <-  Create()

WPFEventLoop.Install();;

#endif

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

#light

open System
open System.Windows
open System.Windows.Controls
open System.Windows.Shapes
open System.Windows.Media
open System.Windows.Media.Animation


let rect = new Rectangle(Fill = new SolidColorBrush(Colors.Red), Height = 20., Width = 20., Name = "myRectangle")

let canvas = 
    let c = new Canvas()
    c.Children.Add(rect) |> ignore
    c

NameScope.SetNameScope(canvas, new NameScope());
canvas.RegisterName(rect.Name, rect)

let window = new Window(Content = canvas)

let nfloat f = new Nullable<float>(f)

let myDoubleAnimation = new DoubleAnimation(From = nfloat 20.0, To = nfloat 50.0,
                                            Duration = new Duration(TimeSpan.FromSeconds(5.)),
                                            RepeatBehavior = RepeatBehavior.Forever)
let myStoryboard = 
    let sb = new Storyboard()
    sb.Children.Add(myDoubleAnimation)
    sb

Storyboard.SetTargetName(myDoubleAnimation, rect.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.WidthProperty))

rect.Loaded.Add(fun _ -> myStoryboard.Begin canvas)

let main() =
    let app = new Application()
    app.Run window |> ignore

[<STAThread>]
do main()
person Robert    schedule 26.12.2008
comment
Спасибо за ваши предложения. Поскольку вы сказали, что это может быть проблема с интерактивным F #, я сделал тот же пример на C #. Код С# тоже не работал. Моя проблема заключалась в том, что 0,01 движения за тик слишком мало. Я думал, что 1.0 будет всем окном, поэтому 0.01 будет 1% окна. - person Jules; 26.12.2008
comment
Если я изменю 0,01 на 1,0, анимация заработает! По-видимому, если вы не установите интервал для таймера, он будет работать как можно быстрее (поэтому он все равно будет двигаться, несмотря на низкое значение 0,01). Цикл событий WPF очень полезен. До сих пор я все время сбрасывал интерактивный режим F#. Спасибо и за раскадровку. - person Jules; 26.12.2008
comment
К сожалению, я не могу использовать решение для раскадровки, потому что анимация не является прямой линией. Я пытаюсь создать симуляцию планет с гравитацией, чтобы узнать больше о WPF. Спасибо за вашу помощь! - person Jules; 26.12.2008