【问题标题】:Unable to Update Angle of a RotateTransform for an Image in WPF无法更新 WPF 中图像的 RotateTransform 角度
【发布时间】:2020-03-25 23:16:43
【问题描述】:

我正在尝试在名为 MarkerImage 的自定义 WPF UserControl 中更新图像的旋转角度。我在 MarkerImage 上有一个名为 Heading 的属性,当它发生变化时,我希望图像的角度发生变化。我尝试了很多方法,它们都正确设置了初始角度,但没有一个能够更新角度。这是控件的 XAML:

以下是我尝试过的一些方法:

1) 在 MarkerImage 上创建了一个名为 Heading 的依赖属性。

        public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register("Heading", typeof(uint), typeof(MarkerImage), new PropertyMetadata(default(uint)));

然后我将 MarkerImage 的 DataContext 设置为 {RelativeSource Self} 并将 RotateTransform 的 Angle 属性绑定到该 Heading 属性:

<UserControl x:Class="Pilot2ATC_EFB.Map.MarkerImage"
         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:Pilot2ATC_EFB.Map"
         mc:Ignorable="d" 
         DataContext="{Binding RelativeSource={RelativeSource Self}}"
         d:DesignHeight="450" d:DesignWidth="800" Width="35" Height="35" x:Name="ctlMarkerImage">
<Image x:Name="userImage" HorizontalAlignment="Center" Height="34" Width="34" RenderTransformOrigin="0.5,0.5" Source="/myApplication;component/Resources/arrow.png" Margin="0,0,0,0" VerticalAlignment="Center">
    <Image.RenderTransform>
        <RotateTransform x:Name="rotateTransform" Angle="{Binding  Path=Heading,  UpdateSourceTrigger=PropertyChanged}"/>
    </Image.RenderTransform>
</Image>

这会在创建控件并设置标题属性时正确设置角度。但是,随着 Heading 属性在 Heading 属性的 Set{} 中更新:

SetValue(HeadingProperty, value % 360);

图像角度不变。

2)尝试直接从属性Heading Set代码设置Angle属性:

    _Heading = value % 360;
    rotateTransform.Angle = _Heading;

这再次设置了初始角度,但在标题更新时没有改变图像的旋转。
3) 每次 Heading 改变时都尝试用一个新的 RotateTransform 替换:

    userImage.RenderTransform = new RotateTransform(_Heading);

这也可以设置初始角度,但是当 Heading 值随后发生变化时什么也没做。

4) 尝试在 Heading 更改时对 Angle 属性进行动画处理。 (rotateTransform 是 RotateTransform 元素的 x:Name):

     var rotAnimation = new DoubleAnimation(Heading, TimeSpan.FromMilliseconds(1));
     rotateTransform.BeginAnimation(RotateTransform.AngleProperty, rotAnimation);

再一次,初始值设置正确,但更新无效。

这是使用 DependencyProperty 时设置 Heading 属性的代码。

    public double Heading
    {
        get  { return (double)GetValue(HeadingProperty); }  
        set
        {
            if (value < 0)
                value = 360;
            SetValue(HeadingProperty, value % 360);
        }
    }

当然,我确认代码正在执行,初始设置后航向值发生变化,但图像没有被旋转。

我在论坛中尝试了这些和其他建议的 10 多个其他变体,但它们要么抛出错误,要么没有设置初始值,要么与这 4 个结果相同。

似乎有一种确定的方法可以从后面的代码或通过绑定动态更改 WPF UserControl 中图像的旋转角度,但我不知道那会是什么。任何帮助将不胜感激。

【问题讨论】:

  • 不完全确定,但可能存在一个问题。 DataContext 是由托管您的MarkerImageWindow 设置的吗?这可能会在设置初始值后停止绑定。要进行测试,请尝试删除 DataContext="{Binding RelativeSource={RelativeSource Self}}" 并将 DataContext="{Binding RelativeSource={AncestorType local:MarkerImage}}" 添加到根元素(在您的情况下为 Image)。
  • 首先,UserControl 不应该显式设置它自己的DataContext。这样做会破坏通常的 DataContext 绑定,例如Heading="{Binding Something}" 将不再起作用。与 UserControl 自身属性的绑定应类似于 Angle="{Binding Heading, RelativeSource={AncestorType=UserControl}},没有 UpdateSourceTrigger,这在 OneWay 绑定中无效。
  • 那么属性的类型应该是double,而不是uint。除此之外,在您向我们展示您实际尝试设置 Heading 属性的代码之前,我们无法回答您的问题。如果那是一个 Binding,由于上述原因,它可能无法正常工作。
  • 感谢两位的建议。我添加了用于将 Heading DependencyProperty 值设置到问题的代码。我还尝试了您关于设置 DataContext 和 Binding 值的两个建议,以及两者的几种变体。他们的初始设置工作结果相同,但更新不起作用。就好像 RotateTransform 的角度无法更改......这就是促使方法 #3 替换它而不是尝试更改它的原因。但这也没有用。
  • 嗯,你绝对可以改变旋转变换的角度(我只是自己测试过)。如果没有可重复的例子,我想的不多。旁注:您对Heading 的逻辑和验证不应该放在setter 中,而应该放在validation and/or coercion callbacks 中。这是因为如果通过绑定更新属性(WPF 绑定系统直接调用SetValue),则不会调用该setter。

标签: c# wpf animation data-binding rotatetransform


【解决方案1】:

这是一个似乎可以工作的小应用程序,它演示了 RotateTransform 的 Angle 属性的变化。事实上,有很多方法可以做到这一点。这是一种使用 DependencyProperty 方法的方法。

这是没有 Window 标准内容的应用程序窗口 XAML:

    <Grid Margin="0,0,2,1">
    <local:ImageMarker x:Name="imgMarker" HorizontalAlignment="Left" Height="32" 
                       Margin="45,35,0,0" VerticalAlignment="Top" Width="32"/>
    <TextBox x:Name="txtHeading" HorizontalAlignment="Left" Height="23" Margin="174,44,0,0" 
             TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="120" TextChanged="txtHeading_TextChanged"/>

</Grid>

这是后面的应用程序窗口代码:

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

    private Marker marker;
    private void txtHeading_TextChanged(object sender, TextChangedEventArgs e)
    {
        if(marker == null)
            marker = new Marker(imgMarker);
        if (txtHeading.Text.Length > 0)
        {
            double val;
            var result = double.TryParse(txtHeading.Text, out val);
            if (result)
                marker.Heading = val;
        }
    }
}

这是控制 ImageMarker 的标记对象:

    class Marker : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private ImageMarker imageMarker;

    public Marker(ImageMarker imgMarker)
    {
        imageMarker = imgMarker;
    }


    private double _Heading;
    public double Heading
    {
        get { return _Heading; }
        set
        {
            if (value <= 0)
                value = 360;
            _Heading = value % 360;
            imageMarker.Heading = _Heading;
            //imageMarker.RenderTransform = new RotateTransform(_Heading);
        }
    }
}

注意标题设置器中的注释行。这种改变图像角度的方法也很有效……不需要绑定。

这是 ImageMarker XAML:

<UserControl x:Class="TestRotateTransform.ImageMarker"
         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:TestRotateTransform"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800" Width="32" Height="32">
<Image Source="Resources/arrow_up.png" RenderTransformOrigin="0.5,0.5">
    <Image.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform Angle="{Binding Heading, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=UserControl}}" />
            <TranslateTransform/>
        </TransformGroup>
    </Image.RenderTransform>

</Image>

最后,这是 ImageMarker 代码,添加了 Keith 建议的 propertychanged、coercion 和验证回调:

    public partial class ImageMarker : UserControl
{
    public ImageMarker()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register("Heading",
        typeof(double), typeof(ImageMarker), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnHeadingChanged),
            new CoerceValueCallback(CoerceHeading)),new ValidateValueCallback(IsValidHeading));

    private static void OnHeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(HeadingProperty);
    }

    private static object CoerceHeading(DependencyObject d, object value)
    {
        double hdg = (double)value;
        if (hdg <= 0)
            hdg = 360;
        return hdg % 360;
    }
    private static bool IsValidHeading(object value)
    {
        return true;
    }
    public double Heading
    {
        get  { return (double)GetValue(HeadingProperty); }  // { return _Heading; }     // 
        set
        {
            SetValue(HeadingProperty, value % 360);
        }
    }

}

感谢 Keith 和 Clemens 的建议。至少我知道这是可以做到的。显然,在我的实际项目中,其他原因导致它失败。

【讨论】:

  • 用一个漂亮的蝴蝶结来解决这个问题。我刚刚解决了我正在处理的程序中的问题。设置 DependencyProperty 时,在初始加载标记后出现线程问题。我在它周围添加了适当的“INVOKE”代码,一切正常。
猜你喜欢
  • 1970-01-01
  • 2020-02-15
  • 1970-01-01
  • 2019-11-22
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 2021-06-19
  • 2021-03-28
相关资源
最近更新 更多