2010-11-01 50 views
0

我有一个问题,使用System.Windows.Interactivity.Interaction附加的行为类(从Expression Blend SDK 4)。我想在XAML Style元素中为System.Windows.Window类定义一对触发器。但随着TriggersPropertySystem.Windows.Interactivity.Interaction类是私有的,也没有SetTriggers在这个类的方法,我有一个错误“设置属性System.Windows.Setter.Property扔一个例外。 - >值不能为空。参数名称:属性'运行以下代码时。通过样式设置隐藏的AttachedProperty

我真的想在样式中使用触发器和动作,因为我想将它们用于我的窗口后代控件。当然,我可以使用我的自定义行为或直接编写我的窗口后裔类与触发器模拟逻辑,但我想使用已存在的触发器和表达式库的行为和我自己的,而不是拒绝他们,只是因为Interaction类的TriggersProperty已隐藏,我无法通过样式进行设置。

解决问题的方法有哪些?有反射或其他方式?

PS。我已经尝试过用TriggersProperty附加依赖​​属性声明自定义静态类,与AddOwner方法的帮助下注册,但没有任何帮助 - 在年底它仍然试图访问同一TriggersProperty在同一System.Windows 。互动性。互动类。

<Window 
    x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:windowsInteractivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"> 
    <Window.Style> 
     <Style TargetType="Window"> 
      <Setter Property="Title" Value="WindowStyleTest"/> 
      <Setter Property="windowsInteractivity:Interaction.Triggers"> 
       <Setter.Value> 
        <windowsInteractivity:EventTrigger EventName="MouseDown"/> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </Window.Style> 
</Window> 

回答

0

我明白了,为什么会出现错误。这是因为在运行时,它正在通过字符串名称(即“ShadowTriggers”(如在System.Windows.Interactivity程序集,Interaction静态构造函数中指定的那样)中搜索Attached Dependency属性)。所以我创建了自己的自定义静态类,并从那里继承了System.Windows.Interaction中的Triggers Dependency Property(通过Reflection和AddOwner,只是将该属性公开为ShadowTriggersProperty)。有效!但是...现在我必须为Style的属性值设置器提供一个TriggerCollection实例,并且该类的构造函数是内部的。假设它是一个没有办法进一步。

3

!!!更新!

好吧,我花了一点时间。我扩展了扩展来执行所有工作,包括设置Triggers集合。

TriggerCollectionExtension 扩展完成所有繁重工作。 注意:第一次调用ProvideValue时,它将从装入样式,因此TargetValue是一个Setter。

[ContentProperty("Triggers")] 
public class TriggerCollectionExtension : MarkupExtension 
{ 
    public string EventName { get; set; } 

    public string CommandName { get; set; } 

    public object CommandParameter { get; set; } 


    public System.Windows.Interactivity.TriggerCollection Triggers { get; private set;} 

    public TriggerCollectionExtension() 
    { 
     var trigCollectionType = 
      typeof(System.Windows.Interactivity.TriggerCollection); 

     var triggers = (System.Windows.Interactivity.TriggerCollection) 
         trigCollectionType.GetConstructor( 
         BindingFlags. NonPublic | BindingFlags. Instance, 
         null, Type.EmptyTypes, null).Invoke (null); 

     // Cheat to get around this problem. 
     // must have IsFrozen set to false to modify 
     var methCreateCore = trigCollectionType.GetMethod("CreateInstanceCore", 
      BindingFlags.NonPublic | BindingFlags.Instance); 
     var cloneTriggers = 
      (System.Windows.Interactivity.TriggerCollection) 
      methCreateCore.Invoke(triggers, null); 

     this.Triggers = cloneTriggers; 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as   
      IProvideValueTarget; 

     // The first time this is called is when loading the style. 
     // At that point the TargetObject is of type Setter. 
     // Return this (The MarkupExtension) and it will be reevaluated when the style 
     // is applied. 

     var hostcontrol = target.TargetObject as Control; 
     if (hostcontrol != null) 
     { 
      var cloneTriggers = this.Triggers; 

      var eventTrigger = new EventTrigger(this.EventName); 

      var trigbase = eventTrigger as TriggerBase; 
      trigbase.Attach(hostcontrol); 

      var commandAction = new CommandAction(hostcontrol, this.CommandName, 
       this.CommandParameter); 
      eventTrigger.Actions.Add(commandAction); 

      cloneTriggers.Add(eventTrigger); 

      Interaction.SetShadowTriggers(hostcontrol, this.Triggers); 

      return null; 
     } 
     else 
     { 
      return this; 
     } 

     return null; 
    } 
} 

相互作用 的TriggersCollection的重新所有权/曝光。

<!-- language: c# --> 
/// <summary> 
/// Helps workaround the bug in the deployed interaction DLL. 
/// The DependencyProperty is registered as ShadowTriggers and the Setter Getter is 
/// SetTriggers() GetTriggers(). 
/// The result is compile error for XAML if anything but Shadowtriggers is used and 
/// runtime error. 
/// </summary> 
public static class Interaction 
{ 
    static Interaction() 
    { 
     var interActionType = typeof(System.Windows.Interactivity.Interaction); 
     var triggersProperty = (DependencyProperty)interActionType.InvokeMember(
      "TriggersProperty", 
      BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField, 
      null, null, null); 

     ShadowTriggersProperty = triggersProperty.AddOwner(typeof(Interaction)); 
    } 

    public static readonly DependencyProperty ShadowTriggersProperty; 

    public static System.Windows.Interactivity.TriggerCollection 
     GetShadowTriggers(DependencyObject d) 
    { 
     return 
      (System.Windows.Interactivity.TriggerCollection) 
      d.GetValue(ShadowTriggersProperty); 
    } 

    public static void 
     SetShadowTriggers(
     DependencyObject d, 
     System.Windows.Interactivity.TriggerCollection value) 
    { 
     d.SetValue(ShadowTriggersProperty, value); 
    } 
} 

的commandAction 定制TriggerAction中查找命令上的DataContext。

<!-- language: c# --> 
public class CommandAction : TriggerAction<FrameworkElement> 
{ 
    FrameworkElement control; 
    private string commandName; 
    object commandParameter; 

    private ICommand actualCommand; 

    public CommandAction(FrameworkElement control, string commandName, 
      object commandParameter) 
    { 
     this.control = control; 
     this.commandName = commandName; 
     this.commandParameter = commandParameter; 

     object datacontext; 

     if (this.FindDataContext(this.control, out datacontext)) 
     { 
      var datacontextType = datacontext.GetType(); 
      var propCommand = datacontextType.GetProperty(this.commandName); 

      this.actualCommand = propCommand.GetValue(datacontext, null) as ICommand; 
     } 
    } 

    private bool FindDataContext(FrameworkElement control, out object datacontext) 
    { 
     datacontext = default(object); 

     var parent = VisualTreeHelper.GetParent(control); 
     while (parent != null) 
     { 
      var parentFrame = parent as FrameworkElement; 
      if (parentFrame != null) 
      { 
       datacontext = parentFrame.DataContext; 
       if (datacontext != null) 
       { 
        return true; 
       } 
      } 

      var parentFrameContent = parent as FrameworkContentElement; 
      if (parentFrameContent != null) 
      { 
       datacontext = parentFrameContent.DataContext; 
       if (datacontext != null) 
       { 
        return true; 
       } 
      } 

      parent = VisualTreeHelper.GetParent(parent); 
     } 

     return false; 
    } 

    protected override void Invoke(object parameter) 
    { 
     if (this.actualCommand != null) 
     { 
      if (this.actualCommand.CanExecute(parameter)) 
      { 
       this.actualCommand.Execute(parameter); 
      } 
     } 
    } 
} 

很长时间读者首次发帖的代码。我终于明白了为什么代码不总是剪切和粘贴得那么好。花了很多次尝试提交此更新。

我确定存在磁盘空间,解析或渲染速度等原因,并且编辑器在未能很好地提交时保持状态。