Я повторяю свой код несколько раз в 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», фактический тип — «Коллекция поведения».
Моя проблема в коде шаблона, который я использую... Я думал, что это может быть проще.
Спасибо за ваше время!





Определите присоединенные свойства для поведений Во-первых, вам необходимо определить прикрепленные свойства в отдельном классе для управления поведением. Вот пример на 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>
Это решение не масштабируется, поскольку используемые варианты поведения жестко запрограммированы в прикрепленном поведении.
Спасибо за код, попробую позже!
Я создал вспомогательную утилиту, позволяющую включать класс поведения в определение стиля.
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, поскольку неудобно заставлять наследника указывать свой собственный тип.
Я думаю, что использование шаблона метода Factoy более элегантно в этом контексте и упрощает сигнатуры классов. Вы просто заставляете наследника переопределить абстрактный метод BehaviorForStyle.CreateInstance, который возвращает полностью инициализированный экземпляр пользовательского поведения. Это также повышает гибкость наследника, поскольку он больше не ограничен конструктором по умолчанию. Теперь у него есть полный контроль над созданием и даже настройкой экземпляра поведения.
Спасибо за ваше время, я попробую это в ближайшее время!
Простое решение с возможностью повторного использования — создать собственное присоединенное поведение, которое собирает поведение взаимодействия и применяет его к прикрепляемому объекту:
<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);
}
}
}
}
Отвечает ли это на ваш вопрос? Как добавить поведение смешивания в наборе стилей