Создание поведений

89

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

Наилучшим способом получить представление о поведениях предусматривает создание собственного поведения. Предположим, что необходимо предоставить любому элементу возможность перетаскивания с помощью мыши в рамках контейнера Canvas. Основные шаги для обеспечения такой возможностью единственного элемента довольно просты: код прослушивает события мыши и изменяет присоединенные свойства, которые соответствующим образом устанавливают координаты элемента Canvas. Приложив еще немного усилий, этот код можно превратить в многократно используемое поведение, которое будет снабжать поддержкой перетаскивания любой элемент в любом контейнере Canvas.

Прежде всего, создайте сборку библиотеки классов WPF (для настоящего примера подойдет имя CustomBehaviorsLibrary) и добавьте в ней ссылку на сборку System.Windows.Interactivity.dll. Затем создайте класс, унаследованный от Behavior — обобщенного класса, который принимает аргумент типа. Можно использовать либо этот аргумент типа для ограничения области действия поведения определенными элементами, либо UIElement или FrameworkElement для охвата всех элементов, как показано ниже:

public class DragInCanvasBehavior : Behavior<UIElement>
    {
        private Canvas canvas;
        
        protected override void OnAttached()
        {
            base.OnAttached();                       

            // Присоединение обработчиков событий            
            this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Удаление обработчиков событий
            this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
        }

        // Отслеживание перетаскивания элемента
        private bool isDragging = false;

        // Запись точной позиции, в которой нажата кнопка
        private Point mouseOffset;

        private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Поиск canvas
            if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas;

            // Режим перетаскивания
            isDragging = true;

            // Получение позиции нажатия относительно элемента
            mouseOffset = e.GetPosition(AssociatedObject);

            // Захват мыши
            AssociatedObject.CaptureMouse();
        }

        private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                // Получение позиции элемента относительно Canvas
                Point point = e.GetPosition(canvas);

                // Move the element.
                AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
                AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
            }
        }

        private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDragging)
            {
                AssociatedObject.ReleaseMouseCapture();
                isDragging = false;
            }
        }
}

Первым шагом при создании любого поведения является переопределение методов OnAttached() и OnDetaching(). В методе OnAttached() можно получить доступ к элементу, в котором размещено поведение (через свойство AssociatedObject), и присоединить обработчики событий. В методе OnDetaching() можно удалить эти обработчики событий.

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

Когда элемент находится в режиме перетаскивания, а мышь перемещается, позиция элемента должна изменяться (обработчик AssociatedObject_MouseMove). И, наконец, при отпускании кнопки мыши операция перетаскивания должна быть завершена (метод AssociatedObject_MouseLeftButtonUp).

Пройди тесты
Лучший чат для C# программистов