Установить скрытый AttachedProperty через стиль - PullRequest
0 голосов
/ 01 ноября 2010

У меня проблема с использованием присоединенного класса поведения System.Windows.Interactivity.Interaction (из Expression Blend SDK 4).Я хотел бы определить пару триггеров для System.Windows.Window класса в элементе стиля XAML.Но поскольку поле TriggersProperty класса System.Windows.Interactivity.Interaction является закрытым, и в этом классе нет метода SetTriggers , возникла ошибка 'Установить свойство System.Windows.Setter.Property для исключения.-> Значение не может быть нулевым.Имя параметра: свойство ' при выполнении следующего кода.

Я действительно хочу использовать триггеры и действия в стилях, потому что я хотел бы использовать их для моего элемента управления "окно-потомок".Конечно, я могу использовать свое собственное поведение или напрямую кодировать мой класс-потомок с помощью триггеров-аналоговой логики, но я бы хотел использовать уже существующие триггеры и действия библиотеки выражений и свои собственные, не отклоняя их, просто потому, что TriggersProperty класса Interaction скрыт, и я не могу установить его с помощью стиля.

Есть ли обходной путь для этой проблемы?С отражением или каким-то другим?

PS.Я уже пытался объявить пользовательский статический класс с прикрепленным свойством зависимостей TriggersProperty , зарегистрированным с помощью метода AddOwner , но без помощи - в конце он все еще пытается получить доступ к тому же TriggersProperty в том же System.Windows.Interactivity.Interaction class.

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

3 голосов
/ 05 декабря 2012

!!! Обновление !!!

Хорошо, я взял это немного дальше.Я расширил расширение для выполнения всей работы, включая настройку коллекции Triggers.

TriggerCollectionExtension Расширение, которое выполняет всю тяжелую работу.Примечание. При первом вызове ProvideValue будет происходить загрузка стиля, поэтому TargetValue является сеттером.

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

Wow-ридер, впервые публикующий код.Наконец-то я понял, почему код не всегда так хорошо вырезан и вставлен.Чтобы отправить это обновление потребовалось очень много попыток.

Я уверен, что существуют такие причины, как дисковое пространство, анализ или скорость рендеринга, и редактор поддерживает состояние при неудачной отправке.

0 голосов
/ 01 ноября 2010

Понял, почему появляется ошибка. Это потому, что во время выполнения он ищет свойство Attached Dependency по имени строки, то есть «ShadowTriggers» (как указано в сборке System.Windows.Interactivity, статический конструктор Interaction). Поэтому я создал собственный статический класс и унаследовал свойство зависимости триггеров от System.Windows.Interaction там (через Reflection и AddOwner просто выставил свойство как ShadowTriggersProperty). Это сработало! Но ... Теперь я должен предоставить экземпляр TriggerCollection для установки значения свойства стиля, и конструктор класса является внутренним. Предположим, что дальше нет пути.

...