Как настроить поведение взаимодействия из стиля

Я повторяю свой код несколько раз в XAML.

Повторяющийся код =

                    <TextBox Margin = "5" MinWidth = "50">
                        <i:Interaction.Behaviors>
                            <behaviors:TextBoxMaxValueBehavior MaxValue = "4"/>
                            <behaviors:TextBoxNoLettersBehavior/>
                            <behaviors:SelectAllTextOnFocusBehavior/>
                        </i:Interaction.Behaviors>
                    </TextBox>

Код шаблона =

        <Style TargetType = "TextBox">
            <Setter Property = "i:Interaction.Behaviors">
                <Setter.Value>
                    <i:BehaviorCollection>
                        <behaviors:SelectAllTextOnFocusBehavior/>
                    </i:BehaviorCollection>
                </Setter.Value>
            </Setter>
        </Style>

ОШИБКА = 'BehaviorCollection нельзя использовать в качестве элемента объекта, поскольку он не является общедоступным или не определяет общедоступный конструктор без параметров или преобразователь типов.

Недопустимый тип: ожидаемый тип — «DependencyProperty», фактический тип — «Коллекция поведения».

Моя проблема в коде шаблона, который я использую... Я думал, что это может быть проще.

Спасибо за ваше время!

Отвечает ли это на ваш вопрос? Как добавить поведение смешивания в наборе стилей

Sir Rufo 18.06.2024 11:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
75
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Определите присоединенные свойства для поведений Во-первых, вам необходимо определить прикрепленные свойства в отдельном классе для управления поведением. Вот пример на C#:

using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;

namespace YourNamespace
{
    public static class TextBoxBehaviors
    {
        public static readonly DependencyProperty UseBehaviorsProperty =
            DependencyProperty.RegisterAttached(
                "UseBehaviors",
                typeof(bool),
                typeof(TextBoxBehaviors),
                new PropertyMetadata(false, OnUseBehaviorsChanged));

        public static bool GetUseBehaviors(DependencyObject obj)
        {
            return (bool)obj.GetValue(UseBehaviorsProperty);
        }

        public static void SetUseBehaviors(DependencyObject obj, bool value)
        {
            obj.SetValue(UseBehaviorsProperty, value);
        }

        private static void OnUseBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is TextBox textBox)
            {
                var behaviors = Interaction.GetBehaviors(textBox);

                if ((bool)e.NewValue)
                {
                    // Add your behaviors here
                    behaviors.Add(new TextBoxMaxValueBehavior { MaxValue = 4 });
                    behaviors.Add(new TextBoxNoLettersBehavior());
                    behaviors.Add(new SelectAllTextOnFocusBehavior());
                }
                else
                {
                    behaviors.Clear();
                }
            }
        }
    }
}

Используйте прикрепленное свойство в XAML Теперь вы можете использовать прикрепленное свойство в своем XAML, чтобы применить поведение к любому текстовому полю:

<Window x:Class = "YourNamespace.MainWindow"
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i = "http://schemas.microsoft.com/xaml/behaviors"
    xmlns:behaviors = "clr-namespace:YourNamespace"
    Title = "MainWindow" Height = "350" Width = "525">
<Grid>
    <TextBox Margin = "5" MinWidth = "50" behaviors:TextBoxBehaviors.UseBehaviors = "True"/>
    <TextBox Margin = "5" MinWidth = "50" behaviors:TextBoxBehaviors.UseBehaviors = "True"/>
    <!-- Add more TextBox elements as needed -->
</Grid>

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

BionicCode 18.06.2024 14:03

Спасибо за код, попробую позже!

Alexandru Filipescu 18.06.2024 14:42

Я создал вспомогательную утилиту, позволяющую включать класс поведения в определение стиля.

public class BehaviorForStyle<TTarget, TBehavior> : Behavior<TTarget>
    where TTarget : DependencyObject
    where TBehavior : BehaviorForStyle<TTarget, TBehavior>, new()
{
    public static readonly DependencyProperty IsEnabledForStyleProperty =
        DependencyProperty.RegisterAttached("IsEnabledForStyle",
            typeof(bool),
            typeof(BehaviorForStyle<TTarget, TBehavior>),
            new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged));

    public bool IsEnabledForStyle
    {
        get => (bool)GetValue(IsEnabledForStyleProperty);
        set => SetValue(IsEnabledForStyleProperty, value);
    }

    private static void OnIsEnabledForStyleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        if (args.NewValue is bool newValue)
        {
            var behaviors = Interaction.GetBehaviors(sender);
            var existingBehavior = behaviors.FirstOrDefault(b => b.GetType() == typeof(TBehavior)) as TBehavior;

            if (!newValue && existingBehavior != null)
            {
                behaviors.Remove(existingBehavior);
            }
            else if (newValue && existingBehavior == null)
            {
                behaviors.Add(new TBehavior());
            }
        }
    }
}

Теперь класс поведения должен происходить от BehaviorForStyle<>, а не просто от Behavior<>.

Например

public class BlockWindowAltF4CloseBehavior : BehaviorForStyle<Window, BlockWindowAltF4CloseBehavior>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.KeyDown += OnKeyDown;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.KeyDown -= OnKeyDown;
        base.OnDetaching();
    }

    private static void OnKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.System && e.SystemKey == Key.F4)
        {
            e.Handled = true;
        }
    }
}

и использование в стиле

<Style TargetType = "Window">
    <Setter Property = "help:BlockWindowAltF4CloseBehavior.IsEnabledForStyle" Value = "True" />
    ...
</style>

Введение нового базового класса для всех вариантов поведения также является решением. Обратите внимание, что ваше решение не имеет обратной совместимости с существующими вариантами поведения или поведениями сторонних разработчиков. Я бы также удалил параметр удаления универсального типа TBehavior из BehaviorForStyle, поскольку неудобно заставлять наследника указывать свой собственный тип.

BionicCode 18.06.2024 14:15

Я думаю, что использование шаблона метода Factoy более элегантно в этом контексте и упрощает сигнатуры классов. Вы просто заставляете наследника переопределить абстрактный метод BehaviorForStyle.CreateInstance, который возвращает полностью инициализированный экземпляр пользовательского поведения. Это также повышает гибкость наследника, поскольку он больше не ограничен конструктором по умолчанию. Теперь у него есть полный контроль над созданием и даже настройкой экземпляра поведения.

BionicCode 18.06.2024 14:15

Спасибо за ваше время, я попробую это в ближайшее время!

Alexandru Filipescu 18.06.2024 14:45
Ответ принят как подходящий

Простое решение с возможностью повторного использования — создать собственное присоединенное поведение, которое собирает поведение взаимодействия и применяет его к прикрепляемому объекту:

Пример использования

<Style TargetType = "TextBox">
  <Setter Property = "InteractionBehaviorCollector.InteractionBehaviors">
    <Setter.Value>
      <InteractionBehaviorCollection>
        <TextBoxMaxValueBehavior MaxValue = "4"/>
        <TextBoxNoLettersBehavior/>
        <SelectAllTextOnFocusBehavior/>
      </InteractionBehaviorCollection>
    </Setter.Value>
  </Setter>
</Style>

InteractionBehaviorCollection.cs
Создайте коллекцию, которую можно использовать в XAML:

public class InteractionBehaviorCollection : List<Behavior>
{
  public InteractionBehaviorCollection()
  {
  }

  public InteractionBehaviorCollection(IEnumerable<Behavior> collection) : base(collection)
  {
  }

  public InteractionBehaviorCollection(int capacity) : base(capacity)
  {
  }
}

InteractionBehaviorCollector.cs
Создайте прикрепленное поведение, которое собирает и применяет поведение взаимодействия:

public class InteractionBehaviorCollector : DependencyObject
{
  public static InteractionBehaviorCollection GetInteractionBehaviors(DependencyObject attachingElement) 
    => (InteractionBehaviorCollection)attachingElement.GetValue(InteractionBehaviorsProperty);

  public static void SetInteractionBehaviors(DependencyObject attachingElement, InteractionBehaviorCollection value) 
    => attachingElement.SetValue(InteractionBehaviorsProperty, value);

  public static readonly DependencyProperty InteractionBehaviorsProperty = DependencyProperty.RegisterAttached(
    "InteractionBehaviors",
    typeof(InteractionBehaviorCollection),
    typeof(InteractionBehaviorCollector),
    new PropertyMetadata(default(InteractionBehaviorCollection), OnInteractionBehaviorsChanged));

  private static void OnInteractionBehaviorsChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {    
    if (e.OldValue is InteractionBehaviorCollection oldBehaviors)
    {
      BehaviorCollection interactionBehaviors = Interaction.GetBehaviors(attachingElement);
      foreach (Behavior behavior in oldBehaviors)
      {
        _ = interactionBehaviors.Remove(behavior);
      }
    }

    if (e.NewValue is InteractionBehaviorCollection newBehaviors)
    {
      BehaviorCollection interactionBehaviors = Interaction.GetBehaviors(attachingElement);      
      foreach (Behavior behavior in newBehaviors)
      {
        interactionBehaviors.Add(behavior);
      }
    }
  }
}

Другие вопросы по теме

Первый подход к базе данных без использования файла EDMX
Невозможно опубликовать приложение Maui для Windows. Ошибка: «Файл манифеста obj\Release\net8.0\staticwebassets.build.json не найден»
Параллельные задачи. Запускайте параллельные потоки, но не ждите завершения других задач и получайте последние данные из базы данных
Ошибка «МАНИФЕСТ НЕИЗВЕСТНО» при публикации стандартного веб-приложения ASP.NET Core в приложениях-контейнерах Azure
Как сделать так, чтобы журналы отображались в командной строке при использовании команды dotnet run?
Стиль .net 8 blazor navlink не работает для внешнего URL-адреса
Как мне создать проверку токена параллелизма для нескольких столбцов в таблице БД в EF Core?
«Ошибка формата exec» при запуске образа докера Arm64 с использованием dotnet
Отсутствует папка «Платформы» при настройке проекта .NET MAUI для тестирования xUnit
Атрибут NotMapped игнорируется при использовании в списке объектов