2013-06-18 12 views
6

Napisałem WindowExtension, który powinien dostarczyć prostą animację Tłumacz do okna. Ale ta animacja zawsze zatrzymuje się, zanim osiągnie docelowe współrzędne. Czy ktoś może mi doradzić dlaczego?Dlaczego moja animacja tłumaczenia WPF jest zatrzymywana przed zakończeniem?

poważaniem Chris

public static class WindowExtensions 
    { 
     public static void Translate(this Window element, double x, double y, TimeSpan duration) 
     { 
     NameScope.SetNameScope(element, new NameScope()); 

     var xAnimation = new DoubleAnimationUsingKeyFrames {Duration = duration}; 
     xAnimation.KeyFrames.Add(new EasingDoubleKeyFrame(element.Left, KeyTime.FromPercent(0))); 
     xAnimation.KeyFrames.Add(new EasingDoubleKeyFrame(x, KeyTime.FromPercent(1))); 

     var yAnimation = new DoubleAnimationUsingKeyFrames {Duration = duration}; 
     yAnimation.KeyFrames.Add(new EasingDoubleKeyFrame(element.Top, KeyTime.FromPercent(0))); 
     yAnimation.KeyFrames.Add(new EasingDoubleKeyFrame(y, KeyTime.FromPercent(1))); 

     var storyboard = new Storyboard() 
     { 
      Children = { xAnimation, yAnimation } 
     }; 

     Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(Window.Left)")); 
     Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(Window.Top)")); 

     storyboard.Duration = duration; 
     storyboard.FillBehavior = FillBehavior.Stop; 

     storyboard.Completed += (sender, args) => 
     { 
      storyboard.SkipToFill(); 
      storyboard.Remove(element); 
     }; 

     storyboard.Begin(element); 
     } 
    } 

Może to być po prostu badane w oknie WPF tak:

public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
     InitializeComponent(); 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
     this.Translate(10,10, TimeSpan.FromMilliseconds(250)); 
     } 
    } 

Odpowiedz

1

Wygląda na literówki. Najprawdopodobniej dlatego, że Twój xAnimation animuje Window.Top i yAnimation animuje Window.Left:

Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(Window.Left)")); 
Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(Window.Top)")); 
+0

Oh! To prawda, to był literówka ... Poprawiłem mój błąd, ale mam taki sam wynik :( – ChrisTTian667

+1

Hmmmm, bardzo dziwne i jest niespójne w moim przypadku Próbowałem usunąć wydarzenie "Completed", zmieniając na "DoubleAnimation" '. Nawet napisał te same animacje w XAML ze stałymi wartościami' To', a czasami dociera do punktu docelowego, a czasami tak nie jest – dkozl

+0

Przede wszystkim do testowania problemu .Próbowałem również wywoływać rekursywnie metodę Translate w CompletedEventHandler. (oczywiście tylko do celów testowych) Tam znalazłem coś zabawnego (dziwnego) Dodałem czek na równość współrzędnych celu oraz faktyczną lewą i najwyższą wartość okna, które były równe, ale okno nie było w tym miejscu pozycja. – ChrisTTian667

-1

znalazłem coś, co działa dla moich potrzeb, ale jest bardzo brudny i chciałbym zmienić implementację z czymś miłym. Tak więc, jeśli ktoś wie, dlaczego ... proszę powiedzieć :)

public static void Translate(this Window element, double x, double y, TimeSpan duration) 
    { 
    var xAnimation = new DoubleAnimationUsingKeyFrames { Duration = duration }; 
    xAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(element.Left, KeyTime.FromPercent(0.0))); 
    xAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(x, KeyTime.FromPercent(1.0))); 

    var yAnimation = new DoubleAnimationUsingKeyFrames { Duration = duration }; 
    yAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(element.Top, KeyTime.FromPercent(0.0))); 
    yAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(y, KeyTime.FromPercent(1.0))); 

    Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(Window.Left)")); 
    Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(Window.Top)")); 

    var storyboard = new Storyboard 
    { 
     Children = { yAnimation, xAnimation }, 
     Duration = duration, 
     FillBehavior = FillBehavior.Stop, 
    }; 

    storyboard.Completed += (sender, args) => 
    { 
     storyboard.SkipToFill(); 
     storyboard.Remove(element); 

     element.InvalidateProperty(Window.LeftProperty); 
     element.InvalidateProperty(Window.TopProperty); 

     if (Math.Abs(element.Left - x) > Double.Epsilon || Math.Abs(element.Top - y) > Double.Epsilon) 
      Translate(element, x, y, TimeSpan.FromTicks(Math.Min(duration.Ticks/2, 100))); 
    }; 

    element.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => element.BeginStoryboard(storyboard))); 
    } 
4

Okno WPF pozycjonowanie/ponownego zaklejania z jego DPI niezależne skalowanie zawsze była problemem dla mnie (zwłaszcza, gdy chcemy animować ruch/rozmiar zmienia się płynnie z ustawieniami DPI monitora i wielu monitorów)

Napisałem niestandardową klasę pomocnika, aby pomóc w animowaniu wymiarów okna, które również mogą ci pomóc.

Główna klasa (NativeWindowSizeManager):

using System; 
using System.Diagnostics.CodeAnalysis; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Interop; 
using System.Windows.Media; 

/// <summary> 
/// C Enumerator to Represent Special Window Handles 
/// </summary> 
public enum SpecialWindowHandles { 
    kHwndTop = 0, 
    kHwndBottom = 1, 
    kHwndTopmost = -1, 
    kHwndNotopmost = -2 
} 

/// <summary> 
/// C Enumerator to Set Window Position Flags 
/// </summary> 
public enum SetNativeWindowPosition { 
    kNoSize = 0x0001, 
    kNoMove = 0x0002, 
    kNoZOrder = 0x0004, 
    kNoRedraw = 0x0008, 
    kNoActivate = 0x0010, 
    kDrawFrame = 0x0020, 
    kFrameChanged = 0x0020, 
    kShowWindow = 0x0040, 
    kHideWindow = 0x0080, 
    kNoCopyBits = 0x0100, 
    kNoOwnerZOrder = 0x0200, 
    kNoReposition = 0x0200, 
    kNoSendChanging = 0x0400, 
    kDeferErase = 0x2000, 
    kAsyncWindowPos = 0x4000 
} 

/// <summary> 
/// Class to perform Window Resize Animations 
/// </summary> 
public class NativeWindowSizeManager { 
    #region Member Variables 
    /// <summary> 
    /// Attached Dependency Property for Native Window Height 
    /// </summary> 
    public static readonly 
    DependencyProperty NativeWindowHeightProperty = DependencyProperty.RegisterAttached(
     "NativeWindowHeight", 
     typeof(double), 
     typeof(Window), 
     new PropertyMetadata(OnNativeDimensionChanged)); 

    /// <summary> 
    /// Attached Dependency Property for Native Window Width 
    /// </summary> 
    public static readonly 
    DependencyProperty NativeWindowWidthProperty = DependencyProperty.RegisterAttached(
     "NativeWindowWidth", 
     typeof(double), 
     typeof(Window), 
     new PropertyMetadata(OnNativeDimensionChanged)); 

    /// <summary> 
    /// Attached Dependency Property for Native Window Left 
    /// </summary> 
    public static readonly 
    DependencyProperty NativeWindowLeftProperty = DependencyProperty.RegisterAttached(
     "NativeWindowLeft", 
     typeof(double), 
     typeof(Window), 
     new PropertyMetadata(OnNativeDimensionChanged)); 

    /// <summary> 
    /// Attached Dependency Property for Native Window Top 
    /// </summary> 
    public static readonly 
    DependencyProperty NativeWindowTopProperty = DependencyProperty.RegisterAttached(
     "NativeWindowTop", 
     typeof(double), 
     typeof(Window), 
     new PropertyMetadata(OnNativeDimensionChanged)); 

    /// <summary> 
    /// Private member holding Dpi Factor 
    /// </summary> 
    private static double? _dpiFactor; 
    #endregion 

    #region Constructors 
    #endregion 

    #region Commands & Properties 
    #endregion 

    #region Methods 
    /// <summary> 
    /// Sets the native height. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <param name="value">The value.</param> 
    public static void SetNativeWindowHeight(UIElement element, double value) { 
    element.SetValue(NativeWindowHeightProperty, value); 
    } 

    /// <summary> 
    /// Gets the native height. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <returns>Native Height in pixels</returns> 
    public static double GetNativeWindowHeight(UIElement element) { 
    return (double)element.GetValue(NativeWindowHeightProperty); 
    } 

    /// <summary> 
    /// Sets the native width. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <param name="value">The value.</param> 
    public static void SetNativeWindowWidth(UIElement element, double value) { 
    element.SetValue(NativeWindowWidthProperty, value); 
    } 

    /// <summary> 
    /// Gets the native width. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <returns>Native Width in pixels</returns> 
    public static double GetNativeWindowWidth(UIElement element) { 
    return (double)element.GetValue(NativeWindowWidthProperty); 
    } 

    /// <summary> 
    /// Sets the native left. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <param name="value">The value.</param> 
    public static void SetNativeWindowLeft(UIElement element, double value) { 
    element.SetValue(NativeWindowLeftProperty, value); 
    } 

    /// <summary> 
    /// Gets the native left. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <returns>Native Left in pixels</returns> 
    public static double GetNativeWindowLeft(UIElement element) { 
    return (double)element.GetValue(NativeWindowLeftProperty); 
    } 

    /// <summary> 
    /// Sets the native top. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <param name="value">The value.</param> 
    public static void SetNativeWindowTop(UIElement element, double value) { 
    element.SetValue(NativeWindowTopProperty, value); 
    } 

    /// <summary> 
    /// Gets the native top. 
    /// </summary> 
    /// <param name="element">The element.</param> 
    /// <returns>Native Top in pixels</returns> 
    public static double GetNativeWindowTop(UIElement element) { 
    return (double)element.GetValue(NativeWindowTopProperty); 
    } 

    /// <summary> 
    /// Method to Get Dpi Factor 
    /// </summary> 
    /// <param name="window">Window Object</param> 
    /// <returns>Dpi Factor</returns> 
    public static double GetDpiFactor(Visual window) { 
    HwndSource windowHandleSource = PresentationSource.FromVisual(window) as HwndSource; 
    if (windowHandleSource != null && windowHandleSource.CompositionTarget != null) { 
     Matrix screenmatrix = windowHandleSource.CompositionTarget.TransformToDevice; 
     return screenmatrix.M11; 
    } 

    return 1; 
    } 

    /// <summary> 
    /// Method to Retrieve Dpi Factor for Window 
    /// </summary> 
    /// <param name="window">Requesting Window</param> 
    /// <param name="originalValue">Dpi Independent Unit</param> 
    /// <returns>Pixel Value</returns> 
    private static int ConvertToDpiDependentPixels(Visual window, double originalValue) { 
    if (_dpiFactor == null) { 
     _dpiFactor = GetDpiFactor(window); 
    } 

    return (int)(originalValue * _dpiFactor); 
    } 

    /// <summary> 
    /// Handler For all Attached Native Dimension property Changes 
    /// </summary> 
    /// <param name="obj">Dependency Object</param> 
    /// <param name="e">Property Arguments</param> 
    private static void OnNativeDimensionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { 
    var window = obj as Window; 
    if (window == null) 
     return; 

    IntPtr handle = new WindowInteropHelper(window).Handle; 
    var rect = new Rect(); 
    if (!GetWindowRect(handle, ref rect)) 
     return; 

    rect.X = ConvertToDpiDependentPixels(window, window.Left); 
    rect.Y = ConvertToDpiDependentPixels(window, window.Top); 
    rect.Width = ConvertToDpiDependentPixels(window, window.ActualWidth); 
    rect.Height = ConvertToDpiDependentPixels(window, window.ActualHeight); 

    if (e.Property == NativeWindowHeightProperty) { 
     rect.Height = ConvertToDpiDependentPixels(window, (double)e.NewValue); 
    } else if (e.Property == NativeWindowWidthProperty) { 
     rect.Width = ConvertToDpiDependentPixels(window, (double)e.NewValue); 
    } else if (e.Property == NativeWindowLeftProperty) { 
     rect.X = ConvertToDpiDependentPixels(window, (double)e.NewValue); 
    } else if (e.Property == NativeWindowTopProperty) { 
     rect.Y = ConvertToDpiDependentPixels(window, (double)e.NewValue); 
    } 

    SetWindowPos(
     handle, 
     new IntPtr((int)SpecialWindowHandles.kHwndTop), 
     rect.X, 
     rect.Y, 
     rect.Width, 
     rect.Height, 
     (uint)SetNativeWindowPosition.kShowWindow); 
    } 
    #endregion 

    #region Native Helpers 
    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool GetWindowRect(IntPtr windowHandle, ref Rect rect); 

    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool SetWindowPos(
    IntPtr windowHandle, IntPtr windowHandleInsertAfter, int x, int y, int cx, int cy, uint windowPositionFlag); 

    /// <summary> 
    /// C Structure To Represent Window Rectangle 
    /// </summary> 
    [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", 
    Justification = "This is an Implementation for C Struct")] 
    [StructLayout(LayoutKind.Sequential)] 
    public struct Rect { 
    public int X; 
    public int Y; 
    public int Width; 
    public int Height; 
    } 
    #endregion 
} 

teraz za swoje wymagania w swojej obsługi Button.Click można mieć coś takiego:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e) { 
    var storyBoard = new Storyboard { Duration = new Duration(new TimeSpan(0, 0, 0, 0, 250)) }; 

    // Top 
    var aniTop = new DoubleAnimationUsingKeyFrames { Duration = new Duration(new TimeSpan(0, 0, 0, 0, 250)) }; 
    aniTop.KeyFrames.Add(new EasingDoubleKeyFrame(Top, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00)))); 
    aniTop.KeyFrames.Add(new EasingDoubleKeyFrame(10, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 250)))); 
    Storyboard.SetTarget(aniTop, this); 
    Storyboard.SetTargetProperty(aniTop, new PropertyPath(NativeWindowSizeManager.NativeWindowTopProperty)); 
    storyBoard.Children.Add(aniTop); 

    // Left 
    var aniLeft = new DoubleAnimationUsingKeyFrames { Duration = new Duration(new TimeSpan(0, 0, 0, 0, 250)) }; 
    aniLeft.KeyFrames.Add(new EasingDoubleKeyFrame(Left, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00)))); 
    aniLeft.KeyFrames.Add(new EasingDoubleKeyFrame(10, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 250)))); 
    Storyboard.SetTarget(aniLeft, this); 
    Storyboard.SetTargetProperty(aniLeft, new PropertyPath(NativeWindowSizeManager.NativeWindowLeftProperty)); 
    storyBoard.Children.Add(aniLeft); 
    storyBoard.Begin(); 
} 

i powinien działać dobrze każdym czasie w ramach wszystkich wyżej wspomniane przypadki.

NativeWindowSizeManager posiada również NativeWindowWidth i NativeWindowHeight umożliwiającą ponowne kalibrowanie być animowany lub podobnego moim przypadku animowanie okno Zmiana rozmiaru podczas centrowania na bieżącym ekranie okna.

Możesz pobrać wersję demonstracyjną tego projektu dla swojego przypadku użycia: Here

Powiązane problemy