请考虑以下简单的例子,说明我的问题:
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="500"
Title="Click anywhere to animate the movement of the blue thingy...">
<Canvas
x:Name="canvas"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="AntiqueWhite"
MouseDown="canvas_MouseDown" />
</Window>MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.canvas.Children.Add(new Thingy());
}
private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
var thingy = (Thingy)this.canvas.Children[0];
var from = new Point(0.0, 0.0);
var to = new Point(
canvas.ActualWidth - thingy.ActualWidth,
canvas.ActualHeight - thingy.ActualHeight
);
var locAnim = new PointAnimation(
from,
to,
new Duration(TimeSpan.FromSeconds(5))
);
locAnim.Completed += (s, a) =>
{
// Only at this line does the thingy move to the
// correct position...
thingy.Location = to;
};
thingy.Location = from;
thingy.BeginAnimation(Thingy.LocationProperty, locAnim);
}
}
}Thingy.xaml
<UserControl x:Class="WpfApplication1.Thingy"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="50" Height="50" Background="Blue" />Thingy.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class Thingy : UserControl
{
public static DependencyProperty LocationProperty =
DependencyProperty.Register(
"Location",
typeof(Point),
typeof(Thingy)
);
public Thingy()
{
InitializeComponent();
Canvas.SetLeft(this, 0.0);
Canvas.SetTop(this, 0.0);
var xBind = new Binding();
xBind.Source = this;
xBind.Path = new PropertyPath(Canvas.LeftProperty);
xBind.Mode = BindingMode.TwoWay;
var yBind = new Binding();
yBind.Source = this;
yBind.Path = new PropertyPath(Canvas.TopProperty);
yBind.Mode = BindingMode.TwoWay;
var locBind = new MultiBinding();
locBind.Converter = new PointConverter();
locBind.Mode = BindingMode.TwoWay;
locBind.Bindings.Add(xBind);
locBind.Bindings.Add(yBind);
BindingOperations.SetBinding(
this,
Thingy.LocationProperty,
locBind
);
}
public Point Location
{
get
{
return (Point)this.GetValue(LocationProperty);
}
set
{
this.SetValue(LocationProperty, value);
}
}
}
}PointConverter.cs
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1
{
public class PointConverter : IMultiValueConverter
{
public object Convert(object[] v, Type t, object p, CultureInfo c)
{
return new Point((double)v[0], (double)v[1]);
}
public object[] ConvertBack(object v, Type[] t, object p, CultureInfo c)
{
return new object[] { ((Point)v).X, ((Point)v).Y };
}
}
}这里的目标是:
LocationProperty操作和访问Canvas.LeftProperty和Canvas.TopProperty值。LocationProperty和PointAnimation类。目标1似乎工作正常,只有当它试图动画LocationProperty时,它才不会像预期的那样运行。
我所说的“预期”是指Thingy的实例应该随着动画的进展而移动。
我能够使用DoubleAnimation类的两个实例来完成这一任务。
如果问题是Point是一个值类型,那么我怀疑我可以定义自己的Point类型和自己的AnimationTimeline。这不是我想做的。这是一个大得多的项目的一部分,LocationProperty将用于其他事情。
老实说,底线是,在我看来,这应该是有效的,你能告诉我:
我还将提到,我的目标是这个项目的.Net Framework4.5。
谢谢。
发布于 2017-01-20 16:40:25
这是最简单的动画代码。
主窗口:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var x = Canvas.GetLeft(Control1);
var y = Canvas.GetTop(Control1);
x = double.IsNaN(x) ? 0 : x;
y = double.IsNaN(y) ? 0 : y;
var point1 = new Point(x, y);
var point2 = e.GetPosition(this);
var animation = new PointAnimation(point1, point2, new Duration(TimeSpan.FromSeconds(1)));
animation.EasingFunction = new CubicEase();
Control1.BeginAnimation(UserControl1.LocationProperty, animation);
}
}
}主窗口:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" MouseDown="MainWindow_OnMouseDown">
<Canvas>
<local:UserControl1 Background="Red" Height="100" Width="100" x:Name="Control1" />
</Canvas>
</Window>管制:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class UserControl1
{
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
"Location", typeof(Point), typeof(UserControl1), new UIPropertyMetadata(default(Point), OnLocationChanged));
public UserControl1()
{
InitializeComponent();
}
public Point Location
{
get { return (Point) GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static void OnLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control1 = (UserControl1) d;
var value = (Point) e.NewValue;
Canvas.SetLeft(control1, value.X);
Canvas.SetTop(control1, value.Y);
}
}
}管制:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</UserControl>TODO:根据您的需要调整代码:)
Canvas.[Left|Top]Property**:**编辑:一个简单的双向绑定,用于侦听
(有待加强)
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class UserControl1
{
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
"Location", typeof(Point), typeof(UserControl1), new PropertyMetadata(default(Point), OnLocationChanged));
public UserControl1()
{
InitializeComponent();
DependencyPropertyDescriptor.FromProperty(Canvas.LeftProperty, typeof(Canvas))
.AddValueChanged(this, OnLeftChanged);
DependencyPropertyDescriptor.FromProperty(Canvas.TopProperty, typeof(Canvas))
.AddValueChanged(this, OnTopChanged);
}
public Point Location
{
get { return (Point) GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private void OnLeftChanged(object sender, EventArgs eventArgs)
{
var left = Canvas.GetLeft(this);
Location = new Point(left, Location.Y);
}
private void OnTopChanged(object sender, EventArgs e)
{
var top = Canvas.GetTop(this);
Location = new Point(Location.X, top);
}
private static void OnLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control1 = (UserControl1) d;
var value = (Point) e.NewValue;
Canvas.SetLeft(control1, value.X);
Canvas.SetTop(control1, value.Y);
}
}
}发布于 2017-01-20 17:02:47
我喜欢Aybe的回答,但它并没有解决原始代码不能工作的原因。我运行了您的代码,并尝试了一些替代方案,现在发生的情况似乎是,在动画期间,绑定转换器被忽略了。如果在转换器方法中设置断点,或执行Debug.WriteLine,则可以看到转换器在整个动画中没有被调用,而是只有在代码中显式设置属性时才调用。
更深入地讲,问题在于设置Thingy绑定的方式。绑定源属性应该是Thingy.Location,而目标属性应该是Canvas.Left和Canvas.Top。但是,它是向后的--您正在使Canvas.Left和Canvas.Top成为源属性,而Thingy.Location则成为目标属性。您可能会认为将其设置为双向绑定会使其工作(当您显式设置Thingy.Location属性时,它也会这样做),但是对于动画来说,双向绑定方面似乎被忽略了。
一种解决方案是不要在这里使用多绑定。多绑定实际上是当一个属性被多个属性或条件所来源时。在这里,您有多个属性(Canvas.Left和Canvas.Top),您希望使用单个属性- Thingy.Location进行源。因此,在Thingy构造函数中:
var xBind = new Binding();
xBind.Source = this;
xBind.Path = new PropertyPath(Thingy.LocationProperty);
xBind.Mode = BindingMode.OneWay;
xBind.Converter = new PointToDoubleConverter();
xBind.ConverterParameter = false;
BindingOperations.SetBinding(this, Canvas.LeftProperty, xBind);
var yBind = new Binding();
yBind.Source = this;
yBind.Path = new PropertyPath(Thingy.LocationProperty);
yBind.Mode = BindingMode.OneWay;
yBind.Converter = new PointToDoubleConverter();
yBind.ConverterParameter = true;
BindingOperations.SetBinding(this, Canvas.TopProperty, yBind); 另一个区别是绑定转换器。我们需要的不是两个doubles并给您一个Point,而是一个转换程序,它接受一个Point并提取用于Canvas.Left和Canvas.Top属性的double (我使用ConverterParameter来指定需要哪一个)。所以:
public class PointToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var pt = (Point)value;
bool isY = (bool)parameter;
return isY ? pt.Y : pt.X;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}这使得动画在仍然使用绑定和转换器时工作。这里唯一的缺点是Canvas属性和Thingy.Location之间的绑定必须是单向的,因为没有办法单独将Canvas.Left或Canvas.Top转换回完整的Point。换句话说,如果您随后更改了Canvas.Left或Canvas.Top,Thingy.Location将不会更新。(当然,没有绑定的解决方案也是如此。)
但是,如果您确实回到原来的多绑定版本,只需将代码添加到Location属性更改处理程序以更新Canvas.Left和Canvas.Top,您就可以吃蛋糕了。此时它们不需要是TwoWay绑定,因为您负责更新Location属性更改处理程序中的Canvas.Left和Canvas.Top。基本上,所有实际的绑定操作都是确保Location在Canvas.Left和Canvas.Top完成时进行更新。
无论如何,你最初的方法为什么行不通,这个谜团就被解开了。在设置复杂绑定时,正确识别源和目标是至关重要的;TwoWay绑定并不是所有情况下的全部,特别是动画。
https://stackoverflow.com/questions/41767915
复制相似问题