Wpf 如何给出EventTrigger的条件?
是否可以在EventTrigger中给出条件??我已经为单选按钮编写了以下EventTrigger(Mouse.MouseLeave)。我希望对于处于选中状态(IsChecked=True)的项不应触发此操作Wpf 如何给出EventTrigger的条件?,wpf,Wpf,是否可以在EventTrigger中给出条件??我已经为单选按钮编写了以下EventTrigger(Mouse.MouseLeave)。我希望对于处于选中状态(IsChecked=True)的项不应触发此操作 请让我知道我如何才能做到这一点 提前感谢。在您的情况下,您需要: <EventTrigger RoutedEvent="Checked" SourceName="border"> 编辑: 根据您的评论,您正在寻找一个multidatatrigger <Mu
请让我知道我如何才能做到这一点
提前感谢。在您的情况下,您需要:
<EventTrigger RoutedEvent="Checked" SourceName="border">
编辑:
根据您的评论,您正在寻找一个multidatatrigger
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition SourceName="border" Property="IsMouseOver" Value="false" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Name="out_BeginStoryboard" Storyboard="{StaticResource out}" />
<RemoveStoryboard BeginStoryboardName="over_BeginStoryboard" />
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
您不能以这种方式使用EventTrigger。调用EventTriggers的WPF的RoutedEventHandler不提供任何使触发器有条件的机制,并且您不能通过将TriggerAction子类化来解决这一问题,因为没有受保护的Invoke()或Execute()操作要覆盖 但是,使用自定义类可以很容易地完成这项工作。下面是它的使用方法:
<Border>
<my:ConditionalEventTrigger.Triggers>
<my:ConditionalEventTriggerCollection>
<my:ConditionalEventTrigger RoutedEvent="Mouse.MouseLeave"
Condition="{Binding IsChecked, ElementName=checkbox}">
<BeginStoryboard Name="out_BeginStoryboard" Storyboard="{StaticResource out}" />
<RemoveStoryboard BeginStoryboardName="over_BeginStoryboard" />
</my:ConditionalEventTrigger>
</my:ConditionalEventTriggerCollection>
</my:ConditionalEventTrigger.Triggers>
...
...
下面是它将如何实施:
[ContentProperty("Actions")]
public class ConditionalEventTrigger : FrameworkContentElement
{
public RoutedEvent RoutedEvent { get; set; }
public List<TriggerAction> Actions { get; set; }
// Condition
public bool Condition { get { return (bool)GetValue(ConditionProperty); } set { SetValue(ConditionProperty, value); } }
public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register("Condition", typeof(bool), typeof(ConditionalEventTrigger));
// "Triggers" attached property
public static ConditionalEventTriggerCollection GetTriggers(DependencyObject obj) { return (ConditionalEventTriggerCollection)obj.GetValue(TriggersProperty); }
public static void SetTriggers(DependencyObject obj, ConditionalEventTriggerCollection value) { obj.SetValue(TriggersProperty, value); }
public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached("Triggers", typeof(ConditionalEventTriggerCollection), typeof(ConditionalEventTrigger), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
// When "Triggers" is set, register handlers for each trigger in the list
var element = (FrameworkElement)obj;
var triggers = (List<ConditionalEventTrigger>)e.NewValue;
foreach(var trigger in triggers)
element.AddHandler(trigger.RoutedEvent, new RoutedEventHandler((obj2, e2) =>
trigger.OnRoutedEvent(element)));
}
});
public ConditionalEventTrigger()
{
Actions = new List<TriggerAction>();
}
// When an event fires, check the condition and if it is true fire the actions
void OnRoutedEvent(FrameworkElement element)
{
DataContext = element.DataContext; // Allow data binding to access element properties
if(Condition)
{
// Construct an EventTrigger containing the actions, then trigger it
var dummyTrigger = new EventTrigger { RoutedEvent = _triggerActionsEvent };
foreach(var action in Actions)
dummyTrigger.Actions.Add(action);
element.Triggers.Add(dummyTrigger);
try
{
element.RaiseEvent(new RoutedEventArgs(_triggerActionsEvent));
}
finally
{
element.Triggers.Remove(dummyTrigger);
}
}
}
static RoutedEvent _triggerActionsEvent = EventManager.RegisterRoutedEvent("", RoutingStrategy.Direct, typeof(EventHandler), typeof(ConditionalEventTrigger));
}
// Create collection type visible to XAML - since it is attached we cannot construct it in code
public class ConditionalEventTriggerCollection : List<ConditionalEventTrigger> {}
[ContentProperty(“操作”)]
公共类ConditionalEventTrigger:FrameworkContentElement
{
公共路由事件路由事件{get;set;}
公共列表操作{get;set;}
//状况
公共bool条件{get{return(bool)GetValue(ConditionProperty);}set{SetValue(ConditionProperty,value);}
public static readonly dependencProperty ConditionProperty=dependencProperty.Register(“条件”、typeof(bool)、typeof(ConditionalEventTrigger));
//“触发器”附加属性
public static ConditionalEventTriggerCollection GetTriggers(DependencyObject obj){return(ConditionalEventTriggerCollection)obj.GetValue(TriggersProperty);}
公共静态void SetTriggers(DependencyObject对象,ConditionalEventTriggerCollection值){obj.SetValue(TriggersProperty,value);}
公共静态只读DependencyProperty触发器Property=DependencyProperty.RegisterAttached(“触发器”、typeof(ConditionalEventTriggerCollection)、typeof(ConditionalEventTrigger)、new PropertyMetadata
{
PropertyChangedCallback=(对象,e)=>
{
//设置“触发器”后,为列表中的每个触发器注册处理程序
var元素=(FrameworkElement)obj;
var触发器=(列表)e.NewValue;
foreach(触发器中的var触发器)
元素AddHandler(trigger.RoutedEvent,new RoutedEventHandler((obj2,e2)=>
触发器.OnRoutedEvent(元素));
}
});
公共条件通风机()
{
Actions=新列表();
}
//触发事件时,检查条件,如果条件为真,则触发操作
RouteEvent上的void(FrameworkElement)
{
DataContext=element.DataContext;//允许数据绑定访问元素属性
如果(条件)
{
//构造一个包含操作的EventTrigger,然后触发它
var dummyTrigger=neweventtrigger{RoutedEvent=\u triggerationsevent};
foreach(行动中的var行动)
dummyTrigger.Actions.Add(操作);
元素.Triggers.Add(dummyTrigger);
尝试
{
元素。RaiseEvent(新路由EventTargets(_TriggerationSevent));
}
最后
{
元素.Triggers.Remove(dummyTrigger);
}
}
}
静态RoutedEvent _triggerActionsEvent=EventManager.RegisterRoutedEvent(“,RoutingStrategy.Direct,typeof(EventHandler),typeof(ConditionalEventTrigger));
}
//创建对XAML可见的集合类型-因为它是附加的,所以我们不能在代码中构造它
公共类ConditionalEventTriggerCollection:列表{}
享受吧 这是我修改过的版本,它只在设置了源触发器时创建并附加虚拟事件,而不是每次都这样做。我认为这对我的场景来说会更好,因为我正在提出一个关于数百个项目的活动,而不仅仅是一个或两个:
[ContentProperty("Actions")]
public class ConditionalEventTrigger : FrameworkContentElement
{
static readonly RoutedEvent DummyEvent = EventManager.RegisterRoutedEvent(
"", RoutingStrategy.Direct, typeof(EventHandler), typeof(ConditionalEventTrigger));
public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached(
"Triggers", typeof(ConditionalEventTriggers), typeof(ConditionalEventTrigger),
new FrameworkPropertyMetadata(RefreshTriggers));
public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register(
"Condition", typeof(bool), typeof(ConditionalEventTrigger)); // the Condition is evaluated whenever an event fires
public ConditionalEventTrigger()
{
Actions = new List<TriggerAction>();
}
public static ConditionalEventTriggers GetTriggers(DependencyObject obj)
{ return (ConditionalEventTriggers)obj.GetValue(TriggersProperty); }
public static void SetTriggers(DependencyObject obj, ConditionalEventTriggers value)
{ obj.SetValue(TriggersProperty, value); }
public bool Condition
{
get { return (bool)GetValue(ConditionProperty); }
set { SetValue(ConditionProperty, value); }
}
public RoutedEvent RoutedEvent { get; set; }
public List<TriggerAction> Actions { get; set; }
// --- impl ----
// we can't actually fire triggers because WPF won't let us (stupid sealed internal methods)
// so, for each trigger, make a dummy trigger (on a dummy event) with the same actions as the real trigger,
// then attach handlers for the dummy event
public static void RefreshTriggers(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var targetObj = (FrameworkElement)obj;
// start by clearing away the old triggers
foreach (var t in targetObj.Triggers.OfType<DummyEventTrigger>().ToArray())
targetObj.Triggers.Remove(t);
// create and add dummy triggers
foreach (var t in ConditionalEventTrigger.GetTriggers(targetObj))
{
t.DataContext = targetObj.DataContext; // set and Track DataContext so binding works
// targetObj.GetDataContextChanged().WeakSubscribe(dc => t.DataContext = targetObj.DataContext);
var dummyTrigger = new DummyEventTrigger { RoutedEvent = DummyEvent };
foreach (var action in t.Actions)
dummyTrigger.Actions.Add(action);
targetObj.Triggers.Add(dummyTrigger);
targetObj.AddHandler(t.RoutedEvent, new RoutedEventHandler((o, args) => {
if (t.Condition) // evaluate condition when the event gets fired
targetObj.RaiseEvent(new RoutedEventArgs(DummyEvent));
}));
}
}
class DummyEventTrigger : EventTrigger { }
}
public class ConditionalEventTriggers : List<ConditionalEventTrigger> { }
正在使用我编写的反应式框架和一些扩展方法,基本上我们需要订阅目标对象的
.DataContextChanged
事件,但我们需要使用弱引用。如果您的对象从未更改过其datacontext,那么您根本不需要此代码这对我来说很有用
我想根据鼠标悬停在UI元素上以及UI元素的关联所有者处于活动状态(即,允许玩家移动)来执行动画
为了支持这些需求,我使用相对源绑定来克服对事件触发条件的支持不足
例如:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource self}, Path=IsMouseOver}" Value="True" />
<Condition Binding="{Binding Path=IsPlayer1Active}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.GradientStops[0].Color" To="#FF585454" Duration="0:0:.25"/>
<ColorAnimation Storyboard.TargetProperty="Background.GradientStops[1].Color" To="Black" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
我知道这是一篇老文章,但当我来到这里寻求答案时,这里有一些东西对我很有用。基本上,我想要一个面板,可以从鼠标上方屏幕的右侧设置动画,然后在鼠标离开时返回。但是,只有当面板没有固定时。
IsShoppingCartPinned
属性存在于我的ViewModel中。就您的场景而言,您可以使用复选框IsChecked
属性替换IsShoppingCartPinned
属性,并在EventTriggers上运行任何类型的动画
代码如下:
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Margin" Value="0,20,-400,20"/>
<Setter Property="Grid.Column" Value="0"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsShoppingCartPinned}" Value="False"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Name="ExpandPanel">
<Storyboard>
<ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,0,20"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Name="HidePanel">
<Storyboard>
<ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,-400,20"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<DataTrigger Binding="{Binding IsShoppingCartPinned}" Value="True">
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="ExpandPanel"/>
<RemoveStoryboard BeginStoryboardName="HidePanel"/>
</DataTrigger.EnterActions>
<DataTrigger.Setters>
<Setter Property="Margin" Value="0"/>
<Setter Property="Grid.Column" Value="1"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
// targetObj.GetDataContextChanged().WeakSubscribe(dc => t.DataContext = targetObj.DataContext);
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource self}, Path=IsMouseOver}" Value="True" />
<Condition Binding="{Binding Path=IsPlayer1Active}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.GradientStops[0].Color" To="#FF585454" Duration="0:0:.25"/>
<ColorAnimation Storyboard.TargetProperty="Background.GradientStops[1].Color" To="Black" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Margin" Value="0,20,-400,20"/>
<Setter Property="Grid.Column" Value="0"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsShoppingCartPinned}" Value="False"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Name="ExpandPanel">
<Storyboard>
<ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,0,20"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Name="HidePanel">
<Storyboard>
<ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,-400,20"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<DataTrigger Binding="{Binding IsShoppingCartPinned}" Value="True">
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="ExpandPanel"/>
<RemoveStoryboard BeginStoryboardName="HidePanel"/>
</DataTrigger.EnterActions>
<DataTrigger.Setters>
<Setter Property="Margin" Value="0"/>
<Setter Property="Grid.Column" Value="1"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Button x:Name="HoldButton" Content="{Binding Status.Running}"/>
<mut:ConditionalEventTrigger.ConditionTriggers>
<mut:ConditionalEventTriggers>
<mut:ConditionalEventTrigger RoutedEvent="ButtonBase.Click" ConditionProperty="{Binding Status.Running}" ConditionValue="False">
<BeginStoryboard x:Name="OnHold_BeginStoryboard" Storyboard="{StaticResource OnHold}"/>
</mut:ConditionalEventTrigger>
<mut:ConditionalEventTrigger RoutedEvent="ButtonBase.Click" ConditionProperty="{Binding Status.Running}" ConditionValue="True">
<StopStoryboard BeginStoryboardName="OnHold_BeginStoryboard"/>
</mut:ConditionalEventTrigger>
</mut:ConditionalEventTriggers>
</mut:ConditionalEventTrigger.ConditionTriggers>
</Button>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Markup;
namespace MyUtility.Trigger
{
[ContentProperty("Actions")]
public class ConditionalEventTrigger : FrameworkContentElement
{
public static readonly DependencyProperty ConditionTriggersProperty = DependencyProperty.RegisterAttached(
"ConditionTriggers",
typeof(ConditionalEventTriggers),
typeof(ConditionalEventTrigger),
new FrameworkPropertyMetadata(OnConditionalEventTriggersChanged));
public static ConditionalEventTriggers GetConditionTriggers(FrameworkElement element)
{
return (ConditionalEventTriggers)element.GetValue(ConditionTriggersProperty);
}
public static void SetConditionTriggers(FrameworkElement element, List<ConditionalEventTrigger> value)
{
element.SetValue(ConditionTriggersProperty, value);
}
public static readonly DependencyProperty ConditionPropertyProperty = DependencyProperty.Register(
"ConditionProperty",
typeof(bool),
typeof(ConditionalEventTrigger));
public bool ConditionProperty
{
get
{
return (bool)GetValue(ConditionPropertyProperty);
}
set
{
SetValue(ConditionPropertyProperty, value);
}
}
public static readonly DependencyProperty ConditionValueProperty = DependencyProperty.Register(
"ConditionValue",
typeof(bool),
typeof(ConditionalEventTrigger));
public bool ConditionValue
{
get
{
return (bool)GetValue(ConditionValueProperty);
}
set
{
SetValue(ConditionValueProperty, value);
}
}
private static readonly RoutedEvent m_DummyEvent = EventManager.RegisterRoutedEvent(
"ConditionalEventTriggerDummyEvent",
RoutingStrategy.Direct,
typeof(EventHandler),
typeof(ConditionalEventTrigger));
public RoutedEvent RoutedEvent { get; set; }
public List<TriggerAction> Actions { get; set; }
public ConditionalEventTrigger()
{
Actions = new List<TriggerAction>();
}
public static void OnConditionalEventTriggersChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var element = (FrameworkElement)obj;
var triggers = (ConditionalEventTriggers)e.NewValue;
foreach(ConditionalEventTrigger t in triggers)
{
element.RemoveHandler(t.RoutedEvent, new RoutedEventHandler((obj2, e2) => t.OnRoutedEvent(element)));
element.AddHandler(t.RoutedEvent, new RoutedEventHandler((obj2, e2) => t.OnRoutedEvent(element)));
}
}
public void OnRoutedEvent(FrameworkElement element)
{
this.DataContext = element.DataContext;
if (this.ConditionProperty == this.ConditionValue)
{
// .Net doesn't allow us fire a trigger directly, so we bingd trigger on Element and then fire the element.
var dummyTrigger = new EventTrigger { RoutedEvent = m_DummyEvent };
foreach (TriggerAction action in this.Actions)
{
dummyTrigger.Actions.Add(action);
}
element.Triggers.Add(dummyTrigger);
try
{
element.RaiseEvent(new RoutedEventArgs(m_DummyEvent));
}
finally
{
element.Triggers.Remove(dummyTrigger);
}
}
}
}
public class ConditionalEventTriggers : List<ConditionalEventTrigger> {}
}