Я повторяю свой код несколько раз в 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);
}
}
}
}
Отвечает ли это на ваш вопрос? Как добавить поведение смешивания в наборе стилей