Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 5.00/9: Рейтинг темы: голосов - 9, средняя оценка - 5.00
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
WPF

Не работает binding для анимации

12.01.2024, 16:53. Показов 2389. Ответов 33

Студворк — интернет-сервис помощи студентам
Доброго времени суток.
В этом месте:
XML
1
2
<Storyboard x:Key="Checked" Storyboard.TargetName="root">
    <ColorAnimation Storyboard.TargetProperty="(Ellipse.Stroke).(SolidColorBrush.Color)" To="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleChecked)}" Duration="0:0:0.5" />
Я задаю привязку к свойству RippleChecked, чтобы при срабатывание события Checked y Ellipce менялась заливка Stroke.
Но когда я нажимаю на кнопку цвет заливки остается прежним.
Если указать явно цвет или из динамического ресурса, то цвет меняется, то есть тригеры точно срабатывают.
xaml
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local2="clr-namespace:RadioButtonCustom" 
                     xmlns:local="clr-namespace:RadioButtonCustom.CustomControl" 
                    xmlns:system="clr-namespace:System;assembly=netstandard">
    <local2:BrushToColorConverter x:Key="Conv"></local2:BrushToColorConverter>
    <local2:SolidColorBrushToColorConverter x:Key="SolidColorBrushToColorConverter"></local2:SolidColorBrushToColorConverter>
 
    <SolidColorBrush x:Key="PippleChecked" Color="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleChecked)}"></SolidColorBrush>
    <Style TargetType="{x:Type local:RippleRadioButton_SelectedCircle}">
        <Setter Property="OverridesDefaultStyle" Value="False" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
        <Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
        <Setter Property="HorizontalContentAlignment" Value="Left"></Setter>
        <Setter Property="Background" Value="White"></Setter>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Height" Value="50"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:RippleRadioButton_SelectedCircle}">
                    <ControlTemplate.Resources>
                        <Storyboard x:Key="RippleAnimation" Storyboard.TargetName="CircleEffect">
                            <DoubleAnimation Storyboard.TargetProperty="Width"
                                             To="0" Duration="0:0:0"/>
                            <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                             To=".5" Duration="0:0:0"/>
                            <ThicknessAnimation Storyboard.TargetProperty="Margin" 
                                                Duration="0:0:0.8" FillBehavior="HoldEnd"/>
                            <DoubleAnimation Storyboard.TargetProperty="Width" 
                                             BeginTime="0:0:0" Duration="0:0:0.8" From="0" />
                            <DoubleAnimation Storyboard.TargetProperty="Opacity" 
                                             BeginTime="0:0:0.2" Duration="0:0:0.6"  From=".5" To="0" />
                        </Storyboard>
                        <Storyboard x:Key="Checked" Storyboard.TargetName="root">
                            <ColorAnimation Storyboard.TargetProperty="(Ellipse.Stroke).(SolidColorBrush.Color)" To="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleChecked)}" Duration="0:0:0.5" />
 
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(Ellipse.Width)">
                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="40" />
                            </DoubleAnimationUsingKeyFrames>
 
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(Ellipse.Height)">
                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="40" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                        <Storyboard x:Key="Unchecked" Storyboard.TargetName="root">
                            <ColorAnimation Storyboard.TargetProperty="(Ellipse.Stroke).(SolidColorBrush.Color)" To="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleUnChecked)}" Duration="0:0:0.8" />
 
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(Ellipse.Width)">
                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
 
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(Ellipse.Height)">
                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </ControlTemplate.Resources>
                    <Grid x:Name="templateRoot" ClipToBounds="True" Background="Transparent" 
                           local:RippleRadioButton_SelectedCircle.ParentToggleButton="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <!--Ripple Effect Body-->
                        <Ellipse x:Name="CircleEffect"
                                             Grid.ColumnSpan="30"
                                             Grid.RowSpan="30"
                                             HorizontalAlignment="Left"
                                             VerticalAlignment="Top"
                                             Opacity="0.5"
                                             Width="0"
                                             Panel.ZIndex="0"
                                             Height="{Binding Path=Width, RelativeSource={RelativeSource Self}}"
                                             Fill="{TemplateBinding RippleColor}"/>
                        <Ellipse x:Name="root" Fill="{TemplateBinding Background}" Stroke="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleUnChecked), Converter={StaticResource Conv}}" 
                                 Height="50" Width="50"  StrokeThickness="2"/>
                        <Ellipse x:Name="slider" Fill="{Binding RelativeSource={RelativeSource AncestorType=RadioButton}, Path=(local:RippleRadioButton_SelectedCircle.RippleChecked), Converter={StaticResource Conv}}" Height="0" Width="0" RenderTransformOrigin="0.5,0.5"/>
                        <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <Grid.Tag>
                            <system:Double>0.0</system:Double>
                        </Grid.Tag>
                        <Grid.Triggers>
                            <EventTrigger RoutedEvent="RadioButton.Checked">
                                <BeginStoryboard Storyboard="{StaticResource Checked}"  />
                            </EventTrigger>
                            <EventTrigger RoutedEvent="RadioButton.Unchecked">
                                <BeginStoryboard Storyboard="{StaticResource Unchecked}"  />
                            </EventTrigger>
                        </Grid.Triggers>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Background"  Value="#EFEFEF" TargetName="templateRoot"></Setter>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="true">
                            <Setter Property="Background" Value="#EFEFEF" TargetName="templateRoot"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
c#
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
 
namespace RadioButtonCustom.CustomControl
{
    public class RippleRadioButton_SelectedCircle: RadioButton
    {
        #region RippleChecked
        public Color RippleChecked
        {
            get { return (Color)GetValue(RippleCheckedProperty); }
            set { SetValue(RippleCheckedProperty, value); }
        }
        public static Color GetRippleChecked(UIElement element) => (Color)element.GetValue(RippleCheckedProperty);
 
        public static void SetRippleChecked(UIElement element, Color value) => element.SetValue(RippleCheckedProperty, value);
 
        public static readonly DependencyProperty RippleCheckedProperty =
            DependencyProperty.Register(nameof(RippleChecked), typeof(Color),
                typeof(RippleRadioButton_SelectedCircle), new PropertyMetadata((Color)ColorConverter.ConvertFromString("#46BCFF")));
        #endregion
 
        #region RippleUnChecked
        public Color RippleUnChecked
        {
            get { return (Color)GetValue(RippleUnCheckedProperty); }
            set { SetValue(RippleUnCheckedProperty, value); }
        }
        public static Color GetRippleUnChecked(UIElement element) => (Color)element.GetValue(RippleUnCheckedProperty);
 
        public static void SetRippleUnChecked(UIElement element, Color value) => element.SetValue(RippleUnCheckedProperty, value);
 
        public static readonly DependencyProperty RippleUnCheckedProperty =
            DependencyProperty.Register(nameof(RippleUnChecked), typeof(Color),
                typeof(RippleRadioButton_SelectedCircle), new PropertyMetadata((Color)ColorConverter.ConvertFromString("#BBBBBB")));
        #endregion
 
        #region Ripple effect анимация
        public Brush RippleColor
        {
            get { return (Brush)GetValue(RippleColorProperty); }
            set { SetValue(RippleColorProperty, value); }
        }
 
        public static readonly DependencyProperty RippleColorProperty =
            DependencyProperty.Register("RippleColor", typeof(Brush),
                typeof(RippleRadioButton_SelectedCircle), new PropertyMetadata(Brushes.White));
 
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.AddHandler(MouseDownEvent, new RoutedEventHandler(this.OnMouseDown), true);
        }
 
        public void OnMouseDown(object sender, RoutedEventArgs e)
        {
            Point mousePos = (e as MouseButtonEventArgs).GetPosition(this);
 
            var ellipse = this.GetTemplateChild("CircleEffect") as Ellipse;
 
            ellipse.Margin = new Thickness(mousePos.X, mousePos.Y, 0, 0);
            Storyboard storyboard = (this.FindResource("RippleAnimation") as Storyboard).Clone();
            double effectMaxSize = Math.Max(this.ActualWidth, this.ActualHeight) * 3;
 
            (storyboard.Children[2] as ThicknessAnimation).From =
                new Thickness(mousePos.X, mousePos.Y, 0, 0);
            (storyboard.Children[2] as ThicknessAnimation).To =
                new Thickness(mousePos.X - effectMaxSize / 2, mousePos.Y - effectMaxSize / 2, 0, 0);
            (storyboard.Children[3] as DoubleAnimation).To =
                effectMaxSize;
 
            ellipse.BeginStoryboard(storyboard);
        }
        #endregion
 
 
        #region Логика для переопределения события Checked и UnChecked
        public static readonly DependencyProperty ParentToggleButtonProperty =
         DependencyProperty.RegisterAttached(
             nameof(GetParentToggleButton)[3..],
             typeof(RadioButton),
             typeof(RippleRadioButton_SelectedCircle),
             new PropertyMetadata(null, OnParentToggleButtonChanged));
        public static bool GetParentToggleButton(UIElement element) => (bool)element.GetValue(ParentToggleButtonProperty);
 
        public static void SetParentToggleButton(UIElement element, bool value) => element.SetValue(ParentToggleButtonProperty, value);
 
        public static void OnParentToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is not UIElement element)
                throw new NotImplementedException("Реализовано только для UIElement.");
            if (e.NewValue is RadioButton nbutton)
            {
                RoutedEventHandler onChecked = (sender, e) =>
                {
                    RadioButton button = (RadioButton)sender;
                    if (e.OriginalSource != button)
                        return;
                    RoutedEvent @event = button.IsChecked is true ? RadioButton.CheckedEvent : RadioButton.UncheckedEvent;
                    RoutedEventArgs arg = new RoutedEventArgs(@event);
                    element.RaiseEvent(arg);
                };
                var grid = d as Grid;
                if (grid != null)
                {
                    var width = grid.ActualWidth;
                    grid.Tag = width;
                }
                nbutton.Checked += onChecked;
                nbutton.Unchecked += onChecked;
                CheckedHandlers.Add(element, onChecked);
            }
        }
        public static ConditionalWeakTable<UIElement, RoutedEventHandler> CheckedHandlers = new();
        #endregion
 
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
12.01.2024, 16:53
Ответы с готовыми решениями:

Не работает Binding для DependencyProperty
Вот тут не работает привязка: &lt;da:MyShort Value=&quot;{Binding Path=subId}&quot; /&gt; public class MyShort : FrameworkElement { ...

Скрипт для анимации не работает, почему?
function StartCircleAnimation() { var servicesIllustration = document.getElementById(&quot;services-infographic&quot;); ...

Почему не правильно работает код для анимации изображения
Вот сам код, почему не правильно рабоатет? &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset=&quot;utf-8&quot;&gt; ...

33
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
14.01.2024, 12:14  [ТС]
Студворк — интернет-сервис помощи студентам
Элд Хасп, учту, я первый раз туда что то размещал и через gitHub detscope.
В будущем создам репозиторий с нормальным именем и описанием. Потом в него приглашу.

Добавлено через 4 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
если каких-то "горящих" вопросов нет - я на сегодня всё.
Хорошо спасибо)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Что мне не нравится в текущей реализации: использование кода для запуска анимации MouseDown.
Надо подумать как это убрать
Если сам запуск и можно как то привязать к тригеру, то реализацию с координатами мыши, даже не знаю.
Если только события явно указывать для самого контрола как то.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
14.01.2024, 12:41
Цитата Сообщение от xellan24rus Посмотреть сообщение
Идея хорошая, пойду в гугл.
Создал ветку "eldhasp" и в ней фиксацию Добавлена тема и удаленно подключение словаря в App.
Посмотрите её.

Добавлено через 3 минуты
Ещё фиксация Перенесено создание анимации из ресурсов в триггера.

Добавлено через 44 секунды
Каждую фиксацию загружайте и разбирайте по отдельности. Чтобы понимать, что и для чего изменено.

Добавлено через 12 минут
Не понял для чего это AP-свойство нужно:
C#
85
86
87
88
89
90
91
        #region Логика для переопределения события Checked и UnChecked
        public static readonly DependencyProperty ParentToggleButtonProperty =
         DependencyProperty.RegisterAttached(
             nameof(GetParentToggleButton)[3..],
             typeof(RadioButton),
             typeof(RippleRadioButton_SelectedCircle),
             new PropertyMetadata(null, OnParentToggleButtonChanged));
1
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
14.01.2024, 13:01  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Не понял для чего это AP-свойство нужно:
без него нельзя указать в Storyboard динамические ресурсы или привязки
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Создал ветку "eldhasp" и в ней фиксацию Добавлена тема и удаленно подключение словаря в App.
Посмотрите её.
Посмотрел, удобно. Но нельзя наследоваться от стиля чтобы создать свой стиль со своими настройками цветов и т.п
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Каждую фиксацию загружайте и разбирайте по отдельности. Чтобы понимать, что и для чего изменено.
Я на сайте смотрел. Не особо умею пользоваться git пока что.
Обратил внимание на правки
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
14.01.2024, 13:22
Цитата Сообщение от xellan24rus Посмотреть сообщение
то реализацию с координатами мыши, даже не знаю.
Что-то в таком духе:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System.Windows;
using System.Windows.Input;
 
namespace RadioButtonCustom.CustomControl
{
    public static class MouseHelper
    {
        private static object trueBox = true;
        private static object falseBox = false;
        public static bool GetIsDownPosition(UIElement uie)
        {
            return (bool)uie.GetValue(IsDownPositionProperty);
        }
 
        public static void SetIsDownPosition(UIElement uie, bool value)
        {
            uie.SetValue(IsDownPositionProperty, value ? trueBox : falseBox);
        }
 
        // Using a DependencyProperty as the backing store for IsDownPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsDownPositionProperty =
            DependencyProperty.RegisterAttached(
                "IsDownPosition",
                typeof(bool),
                typeof(MouseHelper),
                new PropertyMetadata(falseBox)
                {
                    PropertyChangedCallback = IsDownPositionChanged
                });
 
        private static void IsDownPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is not UIElement uie)
            {
                throw new Exception("Только для UIElement.");
            }
            if (e.OldValue != e.NewValue)
            {
                if (Equals(e.NewValue, trueBox))
                {
                    uie.AddHandler(UIElement.MouseDownEvent, (MouseButtonEventHandler)OnMouseDown, true);
                    //uie.MouseDown += OnMouseDown;
                }
                else
                {
                    uie.RemoveHandler(UIElement.MouseDownEvent, (MouseButtonEventHandler)OnMouseDown);
                    //uie.MouseDown -= OnMouseDown;
                    uie.ClearValue(LastDownPositionPropertyKey);
                }
            }
        }
 
        private static void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            UIElement uie = (UIElement)sender;
            Point mousePos = e.GetPosition(uie);
            SetLastDownPosition(uie, mousePos);
        }
 
        public static Point? GetLastDownPosition(UIElement uie)
        {
            return (Point?)uie.GetValue(LastDownPositionProperty);
        }
 
        private static void SetLastDownPosition(UIElement uie, Point? value)
        {
            uie.SetValue(LastDownPositionPropertyKey, value);
        }
 
        // Using a DependencyProperty as the backing store for DownPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyPropertyKey LastDownPositionPropertyKey =
            DependencyProperty.RegisterAttachedReadOnly("LastDownPosition", typeof(Point?), typeof(MouseHelper), new PropertyMetadata((Point?)null));
        public static readonly DependencyProperty LastDownPositionProperty = LastDownPositionPropertyKey.DependencyProperty;
    }
}
Фиксацию скинул.

Добавлено через 21 минуту
Пример использования тоже скинул.
1
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
14.01.2024, 13:39  [ТС]
Элд Хасп, Элд Хасп, спасибо больше) буду разбираться с примером)
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
14.01.2024, 13:54
Пример использования тоже скинул.
Цитата Сообщение от xellan24rus Посмотреть сообщение
Но нельзя наследоваться от стиля чтобы создать свой стиль со своими настройками цветов и т.п
Стиль в ресурсах темы является базовым по умолчанию для всех стилей этого типа элемента.
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
14.01.2024, 14:10  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Стиль в ресурсах темы является базовым по умолчанию для всех стилей этого типа элемента.
Темы удобны если нету сильных изменений тем. Но к примеру если есть одна тема и нужно создать три стиля, то кода прибавится порядком. В этом случае удобнее наследоваться от словаря ресурсов и кода будет меньше на выходе.
И в большинстве приложений не выходит одни и те же цвета использовать. А создавать шаблоны стилей, много кода как по мне, хотя и не приходится писать шаблонный код.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
14.01.2024, 19:49
Цитата Сообщение от xellan24rus Посмотреть сообщение
Но к примеру если есть одна тема и нужно создать три стиля, то кода прибавится порядком.
Чё-то вы не так делаете.
Покажите на примере.

Добавлено через 6 минут
xellan24rus, вот этот метод для чего?
C#
96
        public static void OnParentToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
14.01.2024, 20:10  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Чё-то вы не так делаете.
Покажите на примере.
Здесь сначала нужно создать копию шаблона и лишь затем только можно вносить правки. В случае со стилем из словаря, можно было наследоваться и менять цвета по желанию. А так у нас получается и в теме шаблон и в коде дубль
Кликните здесь для просмотра всего текста
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<Window x:Class="RadioButtonCustom.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:RadioButtonCustom"
        xmlns:comtrol="clr-namespace:RadioButtonCustom.CustomControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
     
        <local:BrushToColorConverter x:Key="Conv"/>
        <Style x:Key="RippleRadioButton_SelectedCircleStyle1" TargetType="{x:Type comtrol:RippleRadioButton_SelectedCircle}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="Background" Value="White"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Height" Value="50"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type comtrol:RippleRadioButton_SelectedCircle}">
                        <ControlTemplate.Resources>
                            <Storyboard x:Key="RippleAnimation" Storyboard.TargetName="CircleEffect">
                                <DoubleAnimation Duration="0:0:0" To="0" Storyboard.TargetProperty="Width"/>
                                <DoubleAnimation Duration="0:0:0" To=".5" Storyboard.TargetProperty="Opacity"/>
                                <ThicknessAnimation Duration="0:0:0.8" FillBehavior="HoldEnd" Storyboard.TargetProperty="Margin"/>
                                <DoubleAnimation BeginTime="0:0:0" Duration="0:0:0.8" From="0" Storyboard.TargetProperty="Width"/>
                                <DoubleAnimation BeginTime="0:0:0.2" Duration="0:0:0.6" From=".5" To="0" Storyboard.TargetProperty="Opacity"/>
                            </Storyboard>
                        </ControlTemplate.Resources>
                        <Grid x:Name="templateRoot" Background="{TemplateBinding Background}" ClipToBounds="True" comtrol:RippleRadioButton_SelectedCircle.ParentToggleButton="{Binding RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.Triggers>
                                <EventTrigger RoutedEvent="ToggleButton.Checked">
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <ColorAnimation Duration="0:0:0.5" Storyboard.TargetName="root" To="{Binding (comtrol:RippleRadioButton_SelectedCircle.RippleChecked), RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"/>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Width)">
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="12"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Height)">
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="12"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                                <EventTrigger RoutedEvent="ToggleButton.Unchecked">
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <ColorAnimation Duration="0:0:0.8" Storyboard.TargetName="root" To="{Binding (comtrol:RippleRadioButton_SelectedCircle.RippleUnChecked), RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"/>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Width)">
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Height)">
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                            </Grid.Triggers>
                            <Ellipse x:Name="CircleEffect" Grid.ColumnSpan="30" Fill="{Binding (comtrol:RippleRadioButton_SelectedCircle.RippleColor), RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" HorizontalAlignment="Left" Height="{Binding Width, RelativeSource={RelativeSource Mode=Self}}" Opacity="0.5" Grid.RowSpan="30" VerticalAlignment="Top" Width="0" Panel.ZIndex="0"/>
                            <Ellipse x:Name="root" Fill="{TemplateBinding Background}" Height="25" Stroke="{Binding (comtrol:RippleRadioButton_SelectedCircle.RippleUnChecked), Converter={StaticResource Conv}, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" StrokeThickness="2" Width="25"/>
                            <Ellipse x:Name="slider" Fill="{Binding (comtrol:RippleRadioButton_SelectedCircle.RippleChecked), Converter={StaticResource Conv}, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Height="0" RenderTransformOrigin="0.5,0.5" Width="0"/>
                            <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="templateRoot" Value="#EFEFEF"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="true">
                                <Setter Property="Background" TargetName="templateRoot" Value="#EFEFEF"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="RippleRadioButton_SelectedCircleStyle2" BasedOn="{StaticResource RippleRadioButton_SelectedCircleStyle1}" TargetType="comtrol:RippleRadioButton_SelectedCircle">
            <Setter Property="Background" Value="gray"></Setter>
        </Style>
 
        <Style x:Key="RippleRadioButton_SelectedCircleStyle3" BasedOn="{StaticResource RippleRadioButton_SelectedCircleStyle1}" TargetType="comtrol:RippleRadioButton_SelectedCircle">
            <Setter Property="Background" Value="Green"></Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid>
            <StackPanel>
                <comtrol:RippleRadioButton_SelectedCircle Style="{DynamicResource RippleRadioButton_SelectedCircleStyle1}" FontSize="18" Padding="10 0 0 0" Height="50">Text</comtrol:RippleRadioButton_SelectedCircle>
                <comtrol:RippleRadioButton_SelectedCircle Style="{DynamicResource RippleRadioButton_SelectedCircleStyle2}" FontSize="18" Padding="10 0 0 0">Text</comtrol:RippleRadioButton_SelectedCircle>
                <comtrol:RippleRadioButton_SelectedCircle  Style="{DynamicResource RippleRadioButton_SelectedCircleStyle3}" FontSize="18" Padding="10 0 0 0">Text</comtrol:RippleRadioButton_SelectedCircle>
                <comtrol:RippleRadioButton_SelectedCircle  FontSize="18" Padding="10 0 0 0">Text</comtrol:RippleRadioButton_SelectedCircle>
            </StackPanel>
 
        </Grid>
    </Grid>
</Window>


Добавлено через 34 секунды
Цитата Сообщение от Элд Хасп Посмотреть сообщение
вот этот метод для чего?
Чтобы разморозить события Checked\UnChecked

Добавлено через 18 минут
Элд Хасп, а с другой стороны темы очень удобны если упаковать их в библиотеку, так как появляется доступ к правкам шаблона.
Как мне кажется если элемент сделан в текущем проекте, то можно наследовать стили, а правки делать в исходном файле
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
14.01.2024, 22:26
Лучший ответ Сообщение было отмечено xellan24rus как решение

Решение

xellan24rus, Проверьте последнюю фиксацию.
Сделал анимации без добавления логики в сам элемент.

Цитата Сообщение от xellan24rus Посмотреть сообщение
а с другой стороны темы очень удобны если упаковать их в библиотеку,
Если не брать эксперименты, отладку, то Custom Control всегда создаются в отдельной сборке (проекте).

Цитата Сообщение от xellan24rus Посмотреть сообщение
В случае со стилем из словаря, можно было наследоваться и менять цвета по желанию.
Ваш пример будет без проблем работать и для стиля по умолчанию в теме.
Ничего в этом отношении не менятся.
Если не задаётся BaseOn, то подтягивается стиль по умолчанию из темы.
Если задаётся в BaseOn извлечение стиля по ключу равному типу элемента, то подтягивается стиль по умолчанию из приложения.
То есть это не взаимоисключающее использование, а дополняющее друг друга.
Для CustomControl надо использовать стиль в теме, но если есть необходимость, то можно задать и стиль по умолчанию в приложении.
1
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
15.01.2024, 15:34  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Проверьте последнюю фиксацию.
Сделал анимации без добавления логики в сам элемент.
Хорошая реализация) Теперь даже Custom Control можно удалить и задать как стиль простой.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Ваш пример будет без проблем работать и для стиля по умолчанию в теме.
Ничего в этом отношении не менятся.
Если не задаётся BaseOn, то подтягивается стиль по умолчанию из темы.
Если задаётся в BaseOn извлечение стиля по ключу равному типу элемента, то подтягивается стиль по умолчанию из приложения.
То есть это не взаимоисключающее использование, а дополняющее друг друга.
Для CustomControl надо использовать стиль в теме, но если есть необходимость, то можно задать и стиль по умолчанию в приложении.
Верно подмечено
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
15.01.2024, 16:28
Цитата Сообщение от xellan24rus Посмотреть сообщение
Теперь даже Custom Control можно удалить и задать как стиль простой.
Custom Control нужен только из-за DP-свойств с цветами. Если их задать через ресурсы или AP-свойтства - то можно отказаться от Custom Control.
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
15.01.2024, 16:35  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Custom Control нужен только из-за DP-свойств с цветами.
Есть такие плюсы у Custom Control. Мне привычнее в стиле задать настройки чем в свойствах. Да и изменять удобнее что то потом. Пожалуй откажусь от Custom Control перепишу код, добавлю выбор включения ripple effect. Напишу пару стилей которые чаще использую.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,146
Записей в блоге: 2
15.01.2024, 19:41
Цитата Сообщение от xellan24rus Посмотреть сообщение
Есть такие плюсы у Custom Control. Мне привычнее в стиле задать настройки чем в свойствах.
Если не меняется поведение (логика) - то не вижу смысла в Custom Control.
Я обычно использую ресурсы, но можно и AP-свойства.
Так как доп стили задаются в приложении, то проблем с DynamicResouce тоже не будет.

Добавлено через 2 часа 25 минут
Цитата Сообщение от xellan24rus Посмотреть сообщение
Хорошая реализация)
Не уверен.
Есть сомнения.
Возможно есть утечка памяти - надо проверять.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
15.01.2024, 19:41
Помогаю со студенческими работами здесь

Алгоритмы.Анимации. Написать прогу для изображения анимации
нужно чтобы она двигалась во все стороны. вот эта фигура

TextBox. Binding отлично работает, если создается в code behind, но не работает, будучи описанным в XAML
Window1 : Window { private TestObject _testObj = new TestObject(); public Window1(){ InitializeCompontne(); //...

Не работает Binding
Имеется ряд кнопок с привязкой: xmlns:loc=&quot;clr-namespace:First.ViewModel&quot; &lt;UserControl.Resources&gt; &lt;loc:BoolConverter...

Не работает Binding
Здравствуйте. Есть XAML разметка &lt;TreeView x:Name=&quot;treeRows&quot; ItemsSource=&quot;{Binding TreeRows}&quot;&gt; ...

Не работает Binding
Рассмотрим такую ситуацию. Допустим. Разметка xaml: &lt;Grid x:Name=&quot;LayoutRoot&quot;&gt; &lt;Chart x:Name=&quot;Chart&quot;...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
34
Ответ Создать тему
Новые блоги и статьи
Модель здравосохранения 18. Чем здоровее работник, тем быстрее выгорает
anaschu 24.05.2026
Имитационная модель корпоративного здравоохранения: что показывает математика Сегодня в модели рабочего коллектива на AnyLogic появились три новые механики — выгорание через накопленную усталость,. . .
Модель здравосохранения 17. Планы на выгорание
anaschu 23.05.2026
Вот конкретная схема реализации: В классе Работник добавить: накопленнаяУсталость — растёт каждый час работы, снижается в перерывы и болезни коэффициентПрезентеизма — снижает продуктивность. . .
Изменение цветов в палитре gif файла aka фавикона
russiannick 23.05.2026
Изменение цветов в палитре gif файла, юзаемого как фавиконка в составе html-файла, помещенная в base64, средствами нативного Java Script, навеянное сном в майский день. Для работы необходим браузер,. . .
Модель здравосохранения 16. Слишком хорошие и здоровые сотрудники уходят, недовольные зарплатой
anaschu 23.05.2026
Отладка увольнений и настройка производительности Сегодня во второй половине дня разобрались с механикой увольнений и настроили коэффициент сложности заданий. Вот что было сделано. . . .
Как я стал коммунистом))) Модель сохранения здоровья сотрудников, запись блога номер 15
anaschu 23.05.2026
Внезапно хорошее здоровье сотрудников не нужно капиталистам?))
Модель здравоСохранения 15. Как мы чинили AnyLogic модель рабочего коллектива: сочленение диаграммы состояний болезней и поломок в ресурспул
anaschu 23.05.2026
Как мы чинили AnyLogic модель рабочего коллектива Сегодня разобрались с пятью багами, из-за которых модель либо падала с ошибкой, либо давала совершенно бессмысленные результаты. Каждый баг был. . .
Диалоги с ИИ
zorxor 23.05.2026
Насколько я понимаю - Вы - Искусственный Интеллект. Это так? Да, всё верно. Я — искусственный интеллект. Я представляю собой большую языковую модель, созданную для помощи в самых разных задачах. . . .
Модель здравосохранения 14. Собираем всю модель вместе.
anaschu 22.05.2026
Модель собрана. В будущих постах на видео я покажу, как она работает. В этом посте запускаем её, проверяем результаты и разбираем что можно с ней делать дальше. Перед запуском проверяем. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru