Job*_*Joy 84 c# wpf xaml expression-blend
我为Button创建了一个Blend行为.如何将其设置为应用程序中的所有按钮.
<Button ...>
<i:Interaction.Behaviors>
<local:MyBehavior />
</i:Interaction.Behaviors>
</Button>
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试:
<Style>
<Setter Property="i:Interaction.Behaviors">
<Setter.Value>
<local:MyBehavior />
</Setter.Value>
</Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)
我收到了错误
"行为"属性没有可访问的setter.
Ric*_*key 75
我有同样的问题,我想出了一个解决方案.在我解决之后我发现了这个问题,我发现我的解决方案与马克有很多共同之处.但是,这种方法略有不同.
主要问题是行为和触发器与特定对象相关联,因此您不能对多个不同的关联对象使用相同的行为实例.当您定义行为内联时,XAML会强制执行此一对一关系.但是,当您尝试在样式中设置行为时,可以将样式重用于它应用的所有对象,这将在基本行为类中引发异常.事实上,作者付出了相当大的努力来阻止我们甚至试图这样做,因为他们知道它不会起作用.
第一个问题是我们甚至无法构造行为设置器值,因为构造函数是内部的.所以我们需要自己的行为并触发集合类.
下一个问题是行为和触发器附加属性没有setter,因此它们只能添加到内联XAML中.我们用自己的附加属性解决了这个问题,这些属性操纵主要行为和触发属性.
第三个问题是我们的行为集合仅适用于单个样式目标.我们通过利用一个很少使用的XAML功能x:Shared="False"
来解决这个问题,该功能在每次引用时都会创建资源的新副本.
最后一个问题是行为和触发器不像其他样式设置器; 我们不希望用新行为替换旧行为,因为它们可能会做出截然不同的事情.因此,如果我们接受一旦你添加一个行为你就不能把它拿掉(这就是行为当前的工作方式),我们可以得出结论,行为和触发器应该是加法的,这可以由我们的附加属性来处理.
以下是使用此方法的示例:
<Grid>
<Grid.Resources>
<sys:String x:Key="stringResource1">stringResource1</sys:String>
<local:Triggers x:Key="debugTriggers" x:Shared="False">
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:DebugAction Message="DataContext: {0}" MessageParameter="{Binding}"/>
<local:DebugAction Message="ElementName: {0}" MessageParameter="{Binding Text, ElementName=textBlock2}"/>
<local:DebugAction Message="Mentor: {0}" MessageParameter="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"/>
</i:EventTrigger>
</local:Triggers>
<Style x:Key="debugBehavior" TargetType="FrameworkElement">
<Setter Property="local:SupplementaryInteraction.Triggers" Value="{StaticResource debugTriggers}"/>
</Style>
</Grid.Resources>
<StackPanel DataContext="{StaticResource stringResource1}">
<TextBlock Name="textBlock1" Text="textBlock1" Style="{StaticResource debugBehavior}"/>
<TextBlock Name="textBlock2" Text="textBlock2" Style="{StaticResource debugBehavior}"/>
<TextBlock Name="textBlock3" Text="textBlock3" Style="{StaticResource debugBehavior}"/>
</StackPanel>
</Grid>
Run Code Online (Sandbox Code Playgroud)
该示例使用触发器,但行为的工作方式相同.在示例中,我们显示:
这是一个示例行为,我们的DebugAction
.更恰当的是它是一种行为,但通过滥用语言,我们将行为,触发器和行为称为"行为".
public class DebugAction : TriggerAction<DependencyObject>
{
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(DebugAction), new UIPropertyMetadata(""));
public object MessageParameter
{
get { return (object)GetValue(MessageParameterProperty); }
set { SetValue(MessageParameterProperty, value); }
}
public static readonly DependencyProperty MessageParameterProperty =
DependencyProperty.Register("MessageParameter", typeof(object), typeof(DebugAction), new UIPropertyMetadata(null));
protected override void Invoke(object parameter)
{
Debug.WriteLine(Message, MessageParameter, AssociatedObject, parameter);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我们的集合和附加属性使这一切都有效.通过类比Interaction.Behaviors
,调用您定位的SupplementaryInteraction.Behaviors
属性,因为通过设置此属性,您将为Interaction.Behaviors
触发器添加行为,同样也为触发器添加行为.
public class Behaviors : List<Behavior>
{
}
public class Triggers : List<TriggerBase>
{
}
public static class SupplementaryInteraction
{
public static Behaviors GetBehaviors(DependencyObject obj)
{
return (Behaviors)obj.GetValue(BehaviorsProperty);
}
public static void SetBehaviors(DependencyObject obj, Behaviors value)
{
obj.SetValue(BehaviorsProperty, value);
}
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached("Behaviors", typeof(Behaviors), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyBehaviorsChanged));
private static void OnPropertyBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviors = Interaction.GetBehaviors(d);
foreach (var behavior in e.NewValue as Behaviors) behaviors.Add(behavior);
}
public static Triggers GetTriggers(DependencyObject obj)
{
return (Triggers)obj.GetValue(TriggersProperty);
}
public static void SetTriggers(DependencyObject obj, Triggers value)
{
obj.SetValue(TriggersProperty, value);
}
public static readonly DependencyProperty TriggersProperty =
DependencyProperty.RegisterAttached("Triggers", typeof(Triggers), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyTriggersChanged));
private static void OnPropertyTriggersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var triggers = Interaction.GetTriggers(d);
foreach (var trigger in e.NewValue as Triggers) triggers.Add(trigger);
}
}
Run Code Online (Sandbox Code Playgroud)
并且你有它,通过样式应用的全功能行为和触发器.
Rom*_*dov 22
总结答案和这篇伟大的文章混合行为风格,我来到这个通用的简短而方便的解决方案:
我创建了泛型类,可以通过任何行为继承.
public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent>
where TComponent : System.Windows.DependencyObject
where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior> , new ()
{
public static DependencyProperty IsEnabledForStyleProperty =
DependencyProperty.RegisterAttached("IsEnabledForStyle", typeof(bool),
typeof(AttachableForStyleBehavior<TComponent, TBehavior>), new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged));
public bool IsEnabledForStyle
{
get { return (bool)GetValue(IsEnabledForStyleProperty); }
set { SetValue(IsEnabledForStyleProperty, value); }
}
private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = d as UIElement;
if (uie != null)
{
var behColl = Interaction.GetBehaviors(uie);
var existingBehavior = behColl.FirstOrDefault(b => b.GetType() ==
typeof(TBehavior)) as TBehavior;
if ((bool)e.NewValue == false && existingBehavior != null)
{
behColl.Remove(existingBehavior);
}
else if ((bool)e.NewValue == true && existingBehavior == null)
{
behColl.Add(new TBehavior());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以你可以简单地重复使用它,如下所示:
public class ComboBoxBehaviour : AttachableForStyleBehavior<ComboBox, ComboBoxBehaviour>
{ ... }
Run Code Online (Sandbox Code Playgroud)
在XAML中足以声明:
<Style TargetType="ComboBox">
<Setter Property="behaviours:ComboBoxBehaviour.IsEnabledForStyle" Value="True"/>
Run Code Online (Sandbox Code Playgroud)
基本上,AttachableForStyleBehavior类创建了xaml的东西,为样式中的每个组件注册行为的实例.有关详细信息,请参阅链接.
Rom*_*kin 18
1.创建附属财产
public static class DataGridCellAttachedProperties
{
//Register new attached property
public static readonly DependencyProperty IsSingleClickEditModeProperty =
DependencyProperty.RegisterAttached("IsSingleClickEditMode", typeof(bool), typeof(DataGridCellAttachedProperties), new UIPropertyMetadata(false, OnPropertyIsSingleClickEditModeChanged));
private static void OnPropertyIsSingleClickEditModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dataGridCell = d as DataGridCell;
if (dataGridCell == null)
return;
var isSingleEditMode = GetIsSingleClickEditMode(d);
var behaviors = Interaction.GetBehaviors(d);
var singleClickEditBehavior = behaviors.SingleOrDefault(x => x is SingleClickEditDataGridCellBehavior);
if (singleClickEditBehavior != null && !isSingleEditMode)
behaviors.Remove(singleClickEditBehavior);
else if (singleClickEditBehavior == null && isSingleEditMode)
{
singleClickEditBehavior = new SingleClickEditDataGridCellBehavior();
behaviors.Add(singleClickEditBehavior);
}
}
public static bool GetIsSingleClickEditMode(DependencyObject obj)
{
return (bool) obj.GetValue(IsSingleClickEditModeProperty);
}
public static void SetIsSingleClickEditMode(DependencyObject obj, bool value)
{
obj.SetValue(IsSingleClickEditModeProperty, value);
}
}
Run Code Online (Sandbox Code Playgroud)
2.创造一种行为
public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown;
}
void DataGridCellPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
{
if (!cell.IsFocused)
{
cell.Focus();
}
DataGrid dataGrid = LogicalTreeWalker.FindParentOfType<DataGrid>(cell); //FindVisualParent<DataGrid>(cell);
if (dataGrid != null)
{
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
cell.IsSelected = true;
}
else
{
DataGridRow row = LogicalTreeWalker.FindParentOfType<DataGridRow>(cell); //FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
3.创建样式并设置附加属性
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/>
</Style>
Run Code Online (Sandbox Code Playgroud)
小智 11
我有另一个想法,以避免为每个行为创建附加属性:
行为创建者界面:
public interface IBehaviorCreator
{
Behavior Create();
}
Run Code Online (Sandbox Code Playgroud)小帮手收藏:
public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { }
Run Code Online (Sandbox Code Playgroud)附加行为的助手类:
public static class BehaviorInStyleAttacher
{
#region Attached Properties
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached(
"Behaviors",
typeof(BehaviorCreatorCollection),
typeof(BehaviorInStyleAttacher),
new UIPropertyMetadata(null, OnBehaviorsChanged));
#endregion
#region Getter and Setter of Attached Properties
public static BehaviorCreatorCollection GetBehaviors(TreeView treeView)
{
return (BehaviorCreatorCollection)treeView.GetValue(BehaviorsProperty);
}
public static void SetBehaviors(
TreeView treeView, BehaviorCreatorCollection value)
{
treeView.SetValue(BehaviorsProperty, value);
}
#endregion
#region on property changed methods
private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is BehaviorCreatorCollection == false)
return;
BehaviorCreatorCollection newBehaviorCollection = e.NewValue as BehaviorCreatorCollection;
BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj);
behaviorCollection.Clear();
foreach (IBehaviorCreator behavior in newBehaviorCollection)
{
behaviorCollection.Add(behavior.Create());
}
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)现在你的行为,它实现了IBehaviorCreator:
public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>, IBehaviorCreator
{
//some code ...
public Behavior Create()
{
// here of course you can also set properties if required
return new SingleClickEditDataGridCellBehavior();
}
}
Run Code Online (Sandbox Code Playgroud)现在在xaml中使用它:
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="helper:BehaviorInStyleAttacher.Behaviors" >
<Setter.Value>
<helper:BehaviorCreatorCollection>
<behaviors:SingleClickEditDataGridCellBehavior/>
</helper:BehaviorCreatorCollection>
</Setter.Value>
</Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)我找不到原始文章,但我能够重新创建效果。
#region Attached Properties Boilerplate
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(ScrollIntoViewBehavior), new PropertyMetadata(false, OnIsActiveChanged));
public static bool GetIsActive(FrameworkElement control)
{
return (bool)control.GetValue(IsActiveProperty);
}
public static void SetIsActive(
FrameworkElement control, bool value)
{
control.SetValue(IsActiveProperty, value);
}
private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviors = Interaction.GetBehaviors(d);
var newValue = (bool)e.NewValue;
if (newValue)
{
//add the behavior if we don't already have one
if (!behaviors.OfType<ScrollIntoViewBehavior>().Any())
{
behaviors.Add(new ScrollIntoViewBehavior());
}
}
else
{
//remove any instance of the behavior. (There should only be one, but just in case.)
foreach (var item in behaviors.ToArray())
{
if (item is ScrollIntoViewBehavior)
behaviors.Remove(item);
}
}
}
#endregion
Run Code Online (Sandbox Code Playgroud)
<Style TargetType="Button">
<Setter Property="Blah:ScrollIntoViewBehavior.IsActive" Value="True" />
</Style>
Run Code Online (Sandbox Code Playgroud)
基于这个答案,我做了一个更简单的解决方案,只需要一个类,不需要在你的行为中实现其他东西。
public static class BehaviorInStyleAttacher
{
#region Attached Properties
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached(
"Behaviors",
typeof(IEnumerable),
typeof(BehaviorInStyleAttacher),
new UIPropertyMetadata(null, OnBehaviorsChanged));
#endregion
#region Getter and Setter of Attached Properties
public static IEnumerable GetBehaviors(DependencyObject dependencyObject)
{
return (IEnumerable)dependencyObject.GetValue(BehaviorsProperty);
}
public static void SetBehaviors(
DependencyObject dependencyObject, IEnumerable value)
{
dependencyObject.SetValue(BehaviorsProperty, value);
}
#endregion
#region on property changed methods
private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is IEnumerable == false)
return;
var newBehaviorCollection = e.NewValue as IEnumerable;
BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj);
behaviorCollection.Clear();
foreach (Behavior behavior in newBehaviorCollection)
{
// you need to make a copy of behavior in order to attach it to several controls
var copy = behavior.Clone() as Behavior;
behaviorCollection.Add(copy);
}
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
示例用法是
<Style TargetType="telerik:RadComboBox" x:Key="MultiPeriodSelectableRadComboBox">
<Setter Property="AllowMultipleSelection" Value="True" />
<Setter Property="behaviors:BehaviorInStyleAttacher.Behaviors">
<Setter.Value>
<collections:ArrayList>
<behaviors:MultiSelectRadComboBoxBehavior
SelectedItems="{Binding SelectedPeriods}"
DelayUpdateUntilDropDownClosed="True"
SortSelection="True"
ReverseSort="True" />
</collections:ArrayList>
</Setter.Value>
</Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)
不要忘记添加此 xmlns 以使用 ArrayList:
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Run Code Online (Sandbox Code Playgroud)
行为代码需要视觉对象,因此我们只能将其添加到视觉对象上。因此,我能看到的唯一选择是添加到 ControlTemplate 内的元素之一,以便将行为添加到样式并影响特定控件的所有实例。
归档时间: |
|
查看次数: |
37147 次 |
最近记录: |