0

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

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

有什么解决方法吗?使用反射或其他方式?

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

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

2 回答 2

3

!!!更新!!!

好的,我更进一步。我扩展了扩展以执行所有工作,包括设置触发器集合。

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 在 DataContext 上查找 Command 的自定义 TriggerAction。

<!-- 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);
            }
        }
    }
}

哇长时间阅读器第一次发布代码。我终于明白了为什么代码并不总是那么好剪切和粘贴。提交此更新尝试了很多次。

我确信有诸如磁盘空间、解析或渲染速度之类的原因,并且编辑器会在提交失败时保持状态。

于 2012-12-05T15:48:40.770 回答
0

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

于 2010-11-01T12:18:15.913 回答