Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.86/21: Рейтинг темы: голосов - 21, средняя оценка - 4.86
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
UWP

DragPositionBehavior при быстром перемещении курсора теряется его позиция

21.11.2021, 17:34. Показов 4790. Ответов 55
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Я написал простой Behavior для того, чтобы перемещать элемент внутри Canvas.

Элемент перемещается, всё хорошо, пока я не захватываю элемент за край.
В таком случае елемент легко может потерять текущее местоположение курсора, но OnElementPointerReleased не сработает, а следовательно и не сработает отписка от OnMove.
Это значит, что когда курсор покидает элемент, при том, что до этого я на него нажал, элемент не видит куда я перемещаю курсор, не видит отжал ли я кнопку мыши.
Если я верну курсор мыши обратно в элемент, он продолжит функционировать должным образом.




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
    public partial class DragPositionBehavior : DependencyObject, IBehavior
    {
        public DependencyObject AssociatedObject{ get; set; }
 
        private Point prevPoint;
        private int pointerId = -1;
 
 
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(DragPositionBehavior), new PropertyMetadata(null));
        public ICommand Command { get => (ICommand)GetValue(CommandProperty); set => SetValue(CommandProperty, value); }
 
 
        public static readonly DependencyProperty ZoomFactorProperty = DependencyProperty.Register(nameof(ZoomFactor), typeof(double), typeof(DragPositionBehavior), new PropertyMetadata(0));
        public double ZoomFactor { get => (double)GetValue(ZoomFactorProperty); set => SetValue(ZoomFactorProperty, value); }
 
 
        public static readonly DependencyProperty BaseElementProperty = DependencyProperty.Register(nameof(BaseElement), typeof(Panel), typeof(DragPositionBehavior), new PropertyMetadata(new Grid()));
        public Panel BaseElement { get => (Panel)GetValue(BaseElementProperty); set => SetValue(BaseElementProperty, value); }
 
 
        #region Life circle
        public void Attach(DependencyObject associatedObject)
        {
            if ((associatedObject != AssociatedObject) && !Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            {
                AssociatedObject = associatedObject;
                var element = AssociatedObject as FrameworkElement;
                if (element != null)
                {
                    element.PointerPressed += OnElementPointerPressed;
                    element.PointerReleased += OnElementPointerReleased;
                }
            }
        }
 
        public void Detach()
        {
            var element = AssociatedObject as FrameworkElement;
 
            if (element is not null)
            {
                element.PointerPressed -= OnElementPointerPressed;
                element.PointerReleased -= OnElementPointerReleased;
            }
 
            if (BaseElement is not null)
            {
                BaseElement.PointerMoved -= OnMove;
            }
 
            BaseElement = null;
            AssociatedObject = null;
        }
        #endregion
 
        #region Handle pointer input
        private void OnElementPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            var element = sender as FrameworkElement;
 
            if (element == null)
                return;
 
            if (!(element.RenderTransform is TranslateTransform))
                element.RenderTransform = new TranslateTransform();
 
            BaseElement.PointerMoved += OnMove;
 
            prevPoint = e.GetCurrentPoint(BaseElement).Position;
            pointerId = (int)e.Pointer.PointerId;
        }
 
        private void OnElementPointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (e.Pointer.PointerId != pointerId)
                return;
 
            var element = sender as FrameworkElement;
            if (element == null)
                return;
 
            BaseElement.PointerMoved -= OnMove;
            pointerId = -1;
 
            Point position = e.GetCurrentPoint(element).Position;
 
            if (Command.CanExecute(position))
                Command.Execute(position);
        }
 
        private void OnMove(object o, PointerRoutedEventArgs e)
        {
            var zommFactor = ZoomFactor;
            var element = AssociatedObject as FrameworkElement;
 
            if (e.Pointer.PointerId != pointerId || element is null)
                return;
 
            var pos = e.GetCurrentPoint(BaseElement).Position;
            ((TranslateTransform)element.RenderTransform).X += (pos.X - prevPoint.X) / zommFactor;
            ((TranslateTransform)element.RenderTransform).Y += (pos.Y - prevPoint.Y) / zommFactor;
            prevPoint = pos;
        }
        #endregion
    }

BaseElement нужен в тех случаях, когда у меня Parent может быть этот же элемент. Например когда там будет Grid в Grid'е.
XML
1
2
3
4
5
6
7
8
9
        <Canvas x:Name="baseElement" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
 
            <Border Margin="100,100,0,0" Width="250" Height="400" Background="White" AllowDrop="True">
                <interactivity:Interaction.Behaviors>
                    <behaviors:DragPositionBehavior Command="{Binding TestBoard.SavePositionCommand}" BaseElement="{Binding ElementName=baseElement}" ZoomFactor="1"/>
                </interactivity:Interaction.Behaviors>
            </Border>
 
        </Canvas>
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.11.2021, 17:34
Ответы с готовыми решениями:

При быстром перемещении окон и открытии меню приложение тормозит ubuntu
Последняя версия.

Позиция курсора и его форма
Есть задание, нужно сдвинуть позицию курсора на 300 пикселей вправо и 100 пикселей вниз, а также изменить форму курсора. Есть код на ...

Выделение при перемещении курсора в StringGrid
Всем добрый день. В таблице Stringgrid реализовал Drag&amp;Drop на перетаскивание строк. Так вот, для удобства пользования этой функцией...

55
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
10.12.2021, 00:44  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Вообще объясните что вы делаете?
Саму задачу в целом.

Элд Хасп, у меня есть "прямоугольник".

Прямоугольник содержит элементы.

Когда я перемещаю именно этот прямоугольник -- всё хорошо.

Когда я перемещаю этот прямоугольник за TextBlock -- у меня не "отжимается кнопка". То есть прямоугольник болтается с курсором мыши.

Добавлено через 1 минуту
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Вы даже не представляете сколько будет геморроя только с этой мелочью и каких багов ещё наловите при эксплуатации приложения.
Пока я не собираюсь трогать эту тему. Возможно она, после тестов, канет в лету в дальнейшем.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
10.12.2021, 00:57
Цитата Сообщение от limeniye Посмотреть сообщение
Прямоугольник содержит элементы.
У вас какая реализация в данный момент?
Вы используете мою?
Тогда ваши прямоугольники это ContentPresenter.
И вашем Behavior надо подписываться на его RoutedEvent, а не на прокси свойства.
И вы добавили AP-свойства как я вам писал?
Или по прежнему напрямую управляете трансформацией?
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
10.12.2021, 01:09  [ТС]
Элд Хасп, да, реализация Ваша.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
И вы добавили AP-свойства как я вам писал?
Я добавил комментарии :
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        private void OnElementPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (BaseParent == null)
                return;
 
            BaseParent.PointerReleased += OnElementPointerReleased;
 
            // TODO : Дописать событие выхода за пределы панели.
            // TODO : Это очень криво. Может у элемента есть своя друга трансформация?
            // TODO : Это нужно ОБЯЗАТЕЛЬНО заменить на AP-свойства.
 
            if (!(AssociatedUIElement.RenderTransform is TranslateTransform))
                AssociatedUIElement.RenderTransform = new TranslateTransform();
 
            BaseParent.PointerMoved += OnMove;
 
            prevPoint = e.GetCurrentPoint(BaseParent).Position;
            pointerId = (int)e.Pointer.PointerId;
        }
1. Пока я не совсем даже понимаю что я буду передавать в AP из XAML.
2. Также не понимаю чем Вам не нравится это предложение. Да, оно неполное, ведь если там будет не TranslateTransform -- будет баг -- это поправимо. Но как я нимаю я вообще неправильно сделал всё.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
10.12.2021, 12:01
Цитата Сообщение от limeniye Посмотреть сообщение
Элд Хасп, да, реализация Ваша.
Ваш uno проект у меня не собирается.
Наверное, нужно ещё что-то устанавливать в Студию.
Неохота мне возиться, поэтому показывать буду на своём Репе, а вы уже сами портируйте его в своё решение.

Цитата Сообщение от limeniye Посмотреть сообщение
Элд Хасп, у меня есть "прямоугольник".
Прямоугольник содержит элементы.
Ваш прямоугольник - это ContentPresenter, который создаётся ItemsControl.
А вы присоединяете Behavior (и следовательно перемещаете) Border (или другой элемент) внутри него, выводя его за его границы.
Ничего не смущает?

Я добавил (фиксация 964c602b) AP-свойство:
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
using Microsoft.Xaml.Interactivity;
using System;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
 
namespace DragPositionDemonstrateProject
{
    public partial class DragPositionBehavior
    {
        public static DragPositionData GetDragPosition(DependencyObject obj)
        {
            return (DragPositionData)obj.GetValue(DragPositionProperty);
        }
 
        public static void SetDragPosition(DependencyObject obj, DragPositionData value)
        {
            obj.SetValue(DragPositionProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for DragPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DragPositionProperty =
            DependencyProperty.RegisterAttached("DragPosition", typeof(DragPositionData), typeof(DragPositionBehavior), new PropertyMetadata(null, DragPositionChanged));
 
        private static void DragPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            /*
                        <interactivity:Interaction.Behaviors>
                            <local:DragPositionBehavior ZoomFactor="1" BaseParent="{Binding ElementName=baseListView}"/>
                        </interactivity:Interaction.Behaviors>
             */
 
            var behaviors = Interaction.GetBehaviors(d);
 
            var dragPositionBehavior = behaviors.OfType<DragPositionBehavior>().FirstOrDefault();
            if (dragPositionBehavior != null)
                behaviors.Remove(dragPositionBehavior);
 
            DragPositionData data = (DragPositionData)e.NewValue;
            DragPositionBehavior behavior = new DragPositionBehavior();
            if (data.ZoomFactor is double zoom)
                behavior.ZoomFactor = zoom;
            else
                BindingOperations.SetBinding(behavior, ZoomFactorProperty, (BindingBase)data.ZoomFactor);
            if (data.BaseParent is UIElement parent)
                behavior.BaseParent = parent;
            else
                BindingOperations.SetBinding(behavior, BaseParentProperty, (BindingBase)data.BaseParent);
 
            behaviors.Add(behavior);
        }
 
    }
 
    public class DragPositionData
    {
        private object zoomFactor;
        private object baseParent;
 
        public object ZoomFactor
        {
            get => zoomFactor;
            set
            {
                if ((value ?? 0.0) is double ||
                    value is BindingBase ||
                    (double.TryParse(value.ToString(), out double val) && (value = val) != null))
                    zoomFactor = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
        public object BaseParent
        {
            get => baseParent;
            set
            {
                if (value is UIElement || value is BindingBase)
                    baseParent = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
    }
}
Теперь можно присоединить Behavior туда куда нужно:
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
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="local:SomeType">
 
                    <Border Width="250" Height="400" Background="Orange">
                        <Border.RenderTransform>
                            <TranslateTransform X="{Binding PositionX}" Y="{Binding PositionY}"/>
                        </Border.RenderTransform>
 
                        <!--<interactivity:Interaction.Behaviors>
                            <local:DragPositionBehavior ZoomFactor="1" BaseParent="{Binding ElementName=baseListView}"/>
                        </interactivity:Interaction.Behaviors>-->
                    </Border>
 
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="local:DragPositionBehavior.DragPosition">
                        <Setter.Value>
                            <local:DragPositionData ZoomFactor="1" BaseParent="{Binding ElementName=baseListView}"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
10.12.2021, 12:43
limeniye, хотел продолжить дальше, но споткнулся о привязку свойств Canvas.
Не могу понять почему не работает такой код:
XML
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
        <ItemsControl Name="baseListView" ItemsSource="{Binding Types}">
 
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="SkyBlue"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
 
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="local:SomeType">
 
                    <Border Width="250" Height="400" Background="Orange">
                        <TextBlock VerticalAlignment="Center">
                            <Run Text="{Binding PositionX}"/>
                             <Run Text="{Binding PositionY}"/>
                        </TextBlock>
                    </Border>
 
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding PositionX, Mode=OneWay}"/>
                    <Setter Property="Canvas.Top" Value="{Binding PositionY, Mode=OneWay}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
Попробуйте разобраться - у меня пока времени нет.
Как появится - подключусь.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
10.12.2021, 19:31  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Ваш uno проект у меня не собирается.
Я не удивлён.

Я его недели 2 не мог собрать.

Добавлено через 27 минут
Это ещё хорошо, что он хотя бы уже поднастроен. Из "коробки" он не запустится даже у меня.

Есть свои нюансы.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Наверное, нужно ещё что-то устанавливать в Студию.
1 Начнём с того, какую VisualStudio Вы используете? VisualStudio до 2022 не поддерживает .Net 6.

2.1 Нужно установить расширение Uno. Visual Studio -> Extentions -> Manage Extentions -> "Uno Platform Solution Templates".
2.2 Или по ссылке тут.

3.1 Установить: Universal Windows Platform; ASP.NET and web.
3.2 В инструкции по установке ещё сказано про Mobile development with .NET (Xamarin) , но это для старых версий, скорее всего он и не нужен вовсе для новых. Тем не менее я установил (на всякий случай).

Добавлено через 4 минуты
Также, нужно установить Windows SDK в Visual Studio Installer'е.

Для запуска UWP -- этого должно хватить.

Также, скорее всего, Вам ещё нужно будет лазить в настройки, чтобы включить плюшки, по типу "Hot Reload".

Но на данном этапе будет чудо, если у Вас хотябы просто сбилдится проект.

Добавлено через 16 минут
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Не могу понять почему не работает такой код:
Я не совсем Вас понял, ибо не знаю как это должно быть в конечном итоге, но могу сказать что Canvas.Left и Canvas.Right не будут работать, уже и сам не помню по какой причине.

Поэтому я задавал его следующим образом:

C#
1
2
3
                        <!--<Border.RenderTransform>
                            <TranslateTransform X="{Binding PositionX}" Y="{Binding PositionY}"/>
                        </Border.RenderTransform>-->
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
10.12.2021, 19:32  [ТС]

так они хотябы не будут сливаться в одно целое.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
10.12.2021, 19:41
Цитата Сообщение от limeniye Посмотреть сообщение
Я не совсем Вас понял, ибо не знаю как это должно быть в конечном итоге, но могу сказать что Canvas.Left и Canvas.Right не будут работать, уже и сам не помню по какой причине.
Они работаю, если их задавать напрямую.
А через привязки в стилях они работаю.
Ещё одна особенность за которую я "люблю" UWP.

В WPF такое задание стиля позволяет позиционировать элементы в ItemsControl.

Пока разбирался с этим выяснилось, что в UWP еще нет x:Array и x:Static.
Их же элементарно реализовать!
Почему нет?
Просто жопа... "любый UWP"!

Причину понял только потому, что более-менее знаю WPF.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
10.12.2021, 19:45  [ТС]
Элд Хасп, Да, с течением эксплуатации я уже такого повидал в достатке, но это ещё цветочки. Если бы Вы попробовали Xamarin ... это вообще не платформа а сплошной костыль. Там и половину нет того, что хотябы есть в UWP.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
10.12.2021, 22:50
Лучший ответ Сообщение было отмечено limeniye как решение

Решение

Цитата Сообщение от limeniye Посмотреть сообщение
Да, с течением эксплуатации я уже такого повидал в достатке
Толку от такого виденья?
Без обид, но вы взялись за "изготовление мебели из досок, ещё не умея собирать её из комплекта Икеа".

Так как нормальных сеттеров в UWP нет, пришлось привязки создавать через код.
Для этого добавил свойства в инициализирующий контейнер.
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
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
 
namespace DragPositionDemonstrateProject
{
    /// <summary>Основные данные для создания привязок в DragPositionBehavior.</summary>
    public class DragPositionData
    {
        private object _zoomFactor;
        private object _baseParent;
        private object _offsetX;
        private object _offsetY;
 
        public object ZoomFactor
        {
            get => _zoomFactor;
            set
            {
                if ((value ?? 0.0) is double ||
                    value is BindingBase ||
                    (double.TryParse(value.ToString(), out double val) && (value = val) != null))
                    _zoomFactor = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
        public object BaseParent
        {
            get => _baseParent;
            set
            {
                if (value is UIElement || value is BindingBase)
                    _baseParent = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
        public object OffsetX
        {
            get => _offsetX;
            set
            {
                if ((value ?? 0.0) is double ||
                    value is BindingBase ||
                    (double.TryParse(value.ToString(), out double val) && (value = val) != null))
                    _offsetX = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
        public object OffsetY
        {
            get => _offsetY;
            set
            {
                if ((value ?? 0.0) is double ||
                    value is BindingBase ||
                    (double.TryParse(value.ToString(), out double val) && (value = val) != null))
                    _offsetY = value;
                else
                    throw new ArgumentException(nameof(value));
            }
        }
 
        public Action<DependencyObject> BindingAction { get; set; }
    }
 
}
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
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
 
namespace DragPositionDemonstrateProject
{
    /// <summary>Дополнитеьные данные (специфичные для конкретного использования)
    /// для создания привязок в DragPositionBehavior.</summary>
    public class DragPositionDataBindingCanvas : DragPositionData
    {
        public DragPositionDataBindingCanvas()
        {
            BindingAction = bindingAction;
        }
 
        public Binding LeftBinding { get; set; }
        public Binding TopBinding { get; set; }
 
        private void bindingAction(DependencyObject d)
        {
            BindingOperations.SetBinding(d, Canvas.LeftProperty, LeftBinding);
            BindingOperations.SetBinding(d, Canvas.TopProperty, TopBinding);
        }
 
    }
 
}
Его использование в Behavior для инициализации привязок:
C#
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
        private static void DragPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behaviors = Interaction.GetBehaviors(d);
 
            var dragPositionBehavior = behaviors.OfType<DragPositionBehavior>().FirstOrDefault();
            if (dragPositionBehavior != null)
                behaviors.Remove(dragPositionBehavior);
 
            DragPositionData data = (DragPositionData)e.NewValue;
            DragPositionBehavior behavior = new DragPositionBehavior();
            if (data.ZoomFactor is double zoom)
                behavior.ZoomFactor = zoom;
            else
                BindingOperations.SetBinding(behavior, ZoomFactorProperty, (BindingBase)data.ZoomFactor);
            if (data.BaseParent is UIElement parent)
                behavior.BaseParent = parent;
            else
                BindingOperations.SetBinding(behavior, BaseParentProperty, (BindingBase)data.BaseParent);
 
            if (data.OffsetX is double x)
                SetOffsetX(d, x);
            else
                BindingOperations.SetBinding(d, OffsetXProperty, (BindingBase)data.OffsetX);
 
            if (data.OffsetY is double y)
                SetOffsetY(d, y);
            else
                BindingOperations.SetBinding(d, OffsetYProperty, (BindingBase)data.OffsetY);
 
            behaviors.Add(behavior);
 
            data.BindingAction?.Invoke(d);
        }
Использование всего этого хозяйства в XAML:
XML
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="local:DragPositionBehavior.DragPosition">
                        <Setter.Value>
                            <local:DragPositionDataBindingCanvas
                                ZoomFactor="1"
                                BaseParent="{Binding ElementName=baseListView}"
                                OffsetX="{Binding PositionX, Mode=TwoWay}"
                                OffsetY="{Binding PositionY, Mode=TwoWay}"
                                LeftBinding="{Binding PositionX, Mode=OneWay}"
                                TopBinding="{Binding PositionY, Mode=OneWay}"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
Так же заменил прослушку через прокси свойства на прямое подключение к RoutedEvent:
C#
48
49
50
51
52
53
54
55
            if (associatedObject != AssociatedObject)
            {
                AssociatedObject = associatedObject;
                AssociatedUIElement = associatedUIElement;
 
                //associatedUIElement.PointerPressed += OnElementPointerPressed;
                associatedUIElement.AddHandler(UIElement.PointerPressedEvent, (PointerEventHandler)OnElementPointerPressed, true);
            }
C#
74
75
            //BaseParent.PointerReleased += OnElementPointerReleased;
            BaseParent.AddHandler(UIElement.PointerReleasedEvent, (PointerEventHandler)OnElementPointerReleased, true);
C#
93
94
95
            var basePanel = (UIElement)sender;
            //basePanel.PointerReleased -= OnElementPointerReleased;
            basePanel.RemoveHandler(UIElement.PointerReleasedEvent, (PointerEventHandler)OnElementPointerReleased);
И, не помню писал или нет, заменил в Behavior работу с трансформацией на работу с AP-свойствами.

Теперь без проблем можно перемещать кнопки:
XML
22
23
24
25
26
27
28
29
30
31
32
33
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="local:SomeType">
 
                    <Button Width="250" Height="400" Background="Orange">
                        <TextBlock VerticalAlignment="Center">
                            <Run Text="{Binding PositionX}"/>
                            <Run Text="{Binding PositionY}"/>
                        </TextBlock>
                    </Button>
 
                </DataTemplate>
            </ItemsControl.ItemTemplate>
Фиксация 7a5c6f30:
Сделана нормальная реализация с AP-свойствами и RoutedEvent.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
11.12.2021, 11:49
limeniye, кстати, в текущей реализации, можно всю логику перенести в AP-свойство и отказаться от Behavior.
Это не сложно, но муторно.
Поэтому не сделал.
Когда будет нечего делать - перенесу.
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
12.12.2021, 21:37  [ТС]
Доброго времени суток, Элд Хасп.

Изучил Ваш код. В принципе выглядит логично и очень интересно. Есть пара вопросов:

1. Больше всего меня заинтересовал аттрибут [Bindable]. Что-то о нём вообще ничего не нашёл;

2. Я заметил, что Вы поднимаете событие в том же типе, в котором оно находится, чтобы задать Binding. Тут совсем не понял зачем. По своей сути, если Вы хотели добиться передачи информации через другой поток -- можно было использовать Task. Меня заинтересовал этот подход.

C#
1
2
3
4
5
        private void bindingAction(DependencyObject d)
        {
            BindingOperations.SetBinding(d, Canvas.LeftProperty, LeftBinding);
            BindingOperations.SetBinding(d, Canvas.TopProperty, TopBinding);
        }
3. Зачем были созданы два дополнительных типа: OffsetX и иOffsetY (туда же могу отнести ZoomFactor, который уже был).
Там написано, что это для анимаций, стилизаций и т.д., но что-то не представлю как это.

4. Касательно переноса на AttachedProperty. Вы сказали следующее:
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это не сложно, но муторно.
Это звучит, как какой-то ёмкий рефакторинг. Можно подробнее? -- Пока я вижу это вижу так:
- удалить проверку и добавление в Behavior-лист;
- удалить интерфейс IBehavior;
- метод Detach переименовать в ~DragPositionBehavior();
- метод Attach переименовать в Initialize какой-то;
- Initialize вызывать при изменении свойства DragPositionProperty.

Но уверен что всё куда сложнее.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
12.12.2021, 22:53
Цитата Сообщение от limeniye Посмотреть сообщение
1. Больше всего меня заинтересовал аттрибут [Bindable]. Что-то о нём вообще ничего не нашёл;
Привязки не всегда срабатывали, и быстрый поиск по инету подсказал, что в UWP в некоторых случаях нужен этот атрибут.
Потом ошибку нашёл в другом месте, но атрибут оставил.

Цитата Сообщение от limeniye Посмотреть сообщение
2. Я заметил, что Вы поднимаете событие в том же типе, в котором оно находится, чтобы задать Binding. Тут совсем не понял зачем. По своей сути, если Вы хотели добиться передачи информации через другой поток -- можно было использовать Task. Меня заинтересовал этот подход.
А причём здесь события?
Это создание привязок.
Понадобилось из-за того, что в UWP сеттеры не поддерживают привязки.
Переписывать реализацию самих сеттеров - это слишком громоздко.
Пошёл по более простому пути.

Цитата Сообщение от limeniye Посмотреть сообщение
3. Зачем были созданы два дополнительных типа: OffsetX и иOffsetY (туда же могу отнести ZoomFactor, который уже был).
Там написано, что это для анимаций, стилизаций и т.д., но что-то не представлю как это.
Опять таки из-за сеттетров.
В WPF этого не понадобилось бы.

Цитата Сообщение от limeniye Посмотреть сообщение
Это звучит, как какой-то ёмкий рефакторинг. Можно подробнее? -- Пока я вижу это вижу так:
Ну, примерно.
По сути надо всю логику реализовать через метод DragPositionChanged.
Параметр DependencyObject d - это и есть DependencyObject associatedObject

Сам Behavior, в таком случае, не нуждается в IBehavior, а наследовать его лучше от Freezable.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 01:04  [ТС]
Сам Behavior, в таком случае, не нуждается в IBehavior, а наследовать его лучше от Freezable.
Элд Хасп, не хочу Вас опять огорчать, но по-моему в UWP нет Freezable )). Это нужно проверить, но как я помню — нет такого.

Добавлено через 1 минуту
Я попробую изменить Behavior на AP.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
13.12.2021, 01:46
Цитата Сообщение от limeniye Посмотреть сообщение
, не хочу Вас опять огорчать, но по-моему в UWP нет Freezable )). Это нужно проверить, но как я помню — нет такого.
В WPF для Freezable получает контекст привязок от родительского UI элемента.
А DependencyObject не получает.
Поэтому для работы привязок с источником по умолчанию нужен Freezable.
По сути базовая реализация DependencyObject довольно ущербна и напрямую его практически невозможно использовать (как базовый класс).
Возможно в UWP часть функционала Freezable переместили в базовый DependencyObject - это надо тестить.

Цитата Сообщение от limeniye Посмотреть сообщение
Я попробую изменить Behavior на AP.
Попробуйте.
Потом покажете результаты.

Добавлено через 7 минут
P.S. С DependencyObject, вообще, какая-то странная история.
Сначала они делаю почти все его члены internal.
Потому какуую-то их часть раскрывают в публичные во Freezable.
Но основная часть элементов (в том числе все UI элементы) наследуются от DependencyObject и работают с ним через internal члены.
Поэтому расширение WPF элементов очень часто наталкивается на значительные трудности.
И это происходит в очень многих типах.
Допустим, иногда есть необходимость получить конвертацию типов эквивалентную (чтобы обеспечить повторяемость) конвертации в Binding.
И есть тип в .Net который эту конвертацию обеспечивает DefaultValueConverter.
Но, блин!, он internal!
Зачем его спрятали - одному богу известно.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 11:57  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Параметр DependencyObject d - это и есть DependencyObject associatedObject
Я тоже так думал, но нет. Это тип ContentPresenter.

Я пытаюсь сделать следующим образом:
C#
1
2
var @this = (DragPosition)d;
@this.Attach(d);
DragPositionBehavior был переименован на DragPosition
В этом месте ошибка.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
13.12.2021, 12:06
Цитата Сообщение от limeniye Посмотреть сообщение
Я тоже так думал, но нет. Это тип ContentPresenter.
Всё правильно.
Этим типом представляется элемент ItemsControl.
Именно он находится на Canvas и именно его и нужно перемещать.
А Border или Button (то что вы задаёте в ItemTemplate) - это содержимое (визуальное) этого ContentPresenter.

Добавлено через 50 секунд
Цитата Сообщение от limeniye Посмотреть сообщение
В этом месте ошибка.
Надо код смотреть.
По выложенному здесь не пойму что вы делаете.
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 12:07  [ТС]
Элд Хасп, имею ввиду, что я ожидал увидеть такой же тип, куда оно и приходит. Но как я понял AttachedProperty, в этом плане, работают немного по-другому.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
13.12.2021, 12:09
Цитата Сообщение от limeniye Посмотреть сообщение
имею ввиду, что я ожила увидеть такой же тип, куда оно и приходит. Но как я понял AttachedProperty .в этом плане, работают немного по-другому.
Нет.
В моей реализации, associatedObject - это тоже ContentPresenter.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 12:12  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Надо код смотреть.
По выложенному здесь не пойму что вы делаете.
Имею ввиду следующее. Привык делать так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
    [Bindable]
    public partial class DragPosition
    {
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.RegisterAttached("Data", typeof(DragPositionData), typeof(DragPosition), new PropertyMetadata(null, OnDataChanged));
 
        private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var @this = (DragPosition)d;
            @this.Attach(d);
        }
    }
Но в данном случае я так сделать не могу, так как DragPosition ≠ не ContentPresenter.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
13.12.2021, 12:12

Вызвать событие при перемещении курсора
Здравствуйте всем! Вопрос: Среда разработки — Microsoft Visual Studio 2010 C# ASP.NET Web Form Проблема — при наведении мышью на...

Событие при при перемещении курсора мыши в уже выпавшем списке ComboBox
Всех с Наступившим Новым Годом! Счастья и удачи побольше в этом году! Не подскажите ли какое событие возникает при перемещении курсора...

Процессор нагружается при перемещении курсора мыши
Здравствуйте, проблему описал в заголовке. Не понимаю, что делать, почти всё перепробовал. Такое началось относительно недавно, точную дату...

Word: запускать макрос при перемещении курсора
Возможно ли, чтобы макрос запускался автоматически при изменении позиции курсора? То есть, например, я сейчас нахожусь в ячейке 1-го...

Использование tab при перемещении курсора по объектам
Добрый вечер! Столкнулся с такой проблемой, на форме создаю много объектов, если их создаешь в последовательности, то при нажатии TAB...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru