WPF анимация замедляется, когда мышь находится в окне - PullRequest
0 голосов
/ 26 декабря 2008

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

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)

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

1 Ответ

4 голосов
/ 26 декабря 2008

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

1) если вы используете интерактивную проверку f #, вы установили цикл событий WFP. F # Interactive настраивает цикл событий 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) Я не слишком хорошо это проверил, но я думаю, что использование доски для анимации даст более стабильные результаты, чем использование таймера. Я думаю, это выглядело бы примерно так (изменение свойства width не помогло изменить положение):

#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()
...