通过Style设置隐藏的附加属性

时间:2010-11-01 09:50:56

标签: wpf expression-blend blend

我使用 System.Windows.Interactivity.Interaction 附加行为类(来自Expression Blend SDK 4)时出现问题。我想在XAML Style元素中为 System.Windows.Window 类定义一对触发器。但由于 System.Windows.Interactivity.Interaction 类的 TriggersProperty 字段是私有的,并且此类中没有 SetTriggers 方法,我已经得到错误'设置属性System.Windows.Setter.Property抛出异常。 - >值不能为空。参数名称:运行以下代码时的属性'

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

问题的解决方法是什么?用反射或其他方式吗?

PS。我已经尝试使用 TriggersProperty 附加的依赖属性声明自定义静态类,在 AddOwner 方法的帮助下注册,但没有帮助 - 最后它仍然尝试访问相同的 TriggersProperty 位于相同的 System.Windows.Interactivity.Interaction 类中。

<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>

2 个答案:

答案 0 :(得分: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上查找Command。

<!-- 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);
            }
        }
    }
}
哇长时间读者第一次发帖代码。我终于明白了为什么代码并不总是剪切和粘贴得那么好。尝试提交此更新花了这么多次。

我确信存在磁盘空间,解析或渲染速度等原因,并且编辑器会在未能提交的情况下保持状态。

答案 1 :(得分:0)

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