Невозможно обновить угол RotateTransform для изображения в WPF - PullRequest
0 голосов
/ 26 марта 2020

Я пытаюсь обновить угол поворота изображения в пользовательском WPF UserControl с именем MarkerImage. У меня есть свойство в MarkerImage с именем Heading, и когда оно меняется, я хочу, чтобы угол изображения менялся. Я перепробовал множество методов, и все они правильно установили начальный угол, но ни один из них не смог обновить угол. Вот XAML элемента управления:

Вот некоторые из методов, которые я пробовал:

1) Создал свойство зависимости для MarkerImage с именем Heading.

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

Затем я устанавливаю DataContext для MarkerImage в {RelativeSource Self} и привязываю свойство Angle свойства RotateTransform к этому свойству 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>

Это правильно устанавливает угол, когда Элемент управления создан, и свойство Заголовок установлено. Однако, поскольку свойство Заголовок обновляется в наборе {} свойства Заголовок:

SetValue(HeadingProperty, value % 360);

, угол изображения не изменяется.

2) Попытка установить свойство Angle непосредственно из свойства Код набора заголовка:

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

Это снова сработало для установки начального угла, но не изменило поворот изображения, когда Заголовок был обновлен.
3) Попытка заменить RotateTransform новым каждый раз, когда заголовок изменялся:

    userImage.RenderTransform = new RotateTransform(_Heading);

Это также работало для установки начального угла, но ничего не делало, когда значение заголовка изменилось после этого.

4) Попытка анимации свойства Angle при изменении заголовка. (rotateTransform - это x: имя элемента RotateTransform):

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

И снова, начальное значение было установлено правильно, но обновление не имело никакого эффекта.

Вот код, где Свойство заголовка устанавливается при использовании DependencyProperty.

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

Конечно, я подтвердил, что код выполнялся и значение заголовка изменялось после начальной настройки, но изображение не поворачивалось.

Я попробовал более 10 других вариантов этих и других предложений на форумах, но они либо выдавали ошибки, не устанавливали начальное значение или имели тот же результат, что и эти 4.

Казалось бы что существует определенный способ динамического изменения угла поворота изображения в пользовательском элементе управления WPF из кода позади или через Binding, но я не знаю, что это будет. ЛЮБАЯ помощь будет принята с благодарностью.

1 Ответ

0 голосов
/ 26 марта 2020

Вот небольшое приложение, которое, кажется, работает и демонстрирует изменение свойства Angle объекта RotateTransform. На самом деле, есть много способов сделать это. Это тот, который использует подход DependencyProperty.

Вот XAML окна приложения без стандартного содержимого Window:

    <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;
        }
    }
}

Вот объект Marker, который будет управлять 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);
        }
    }
}

Обратите внимание на закомментированную строку в установщике заголовков. Этот метод изменения угла изображения также работал ... Связывание не требуется.

Вот XAML ImageMarker:

<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 код с добавлением callback-функций свойства Change, принудительного вызова и проверки в соответствии с рекомендациями Keith:

    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 за их предложения. По крайней мере, я знаю, что это можно сделать. Видимо, в моем реальном проекте что-то еще вызывает его провал.

...