Задайте скрито AttachedProperty чрез Style

Имам проблем с използването на прикачен клас на поведение на System.Windows.Interactivity.Interaction (от Expression Blend SDK 4). Бих искал да дефинирам двойка тригери за клас System.Windows.Window в XAML Style елемент. Но тъй като полето TriggersProperty на класа System.Windows.Interactivity.Interaction е частно и в този клас няма метод SetTriggers, аз получи грешка 'Set property System.Windows.Setter.Property хвърли изключение. -> Стойността не може да бъде нула. Име на параметър: свойство' при изпълнение на следния код.

Наистина искам да използвам тригерите и действията в стилове, защото бих искал да ги използвам за моя контрол на прозореца-наследник. Разбира се, мога да използвам моето персонализирано поведение или директно да кодирам своя клас наследник на прозорец с аналогова логика на тригери, но бих искал да използвам вече съществуващи тригери и действия на библиотеката с изрази и моите собствени, без да ги отказвам, просто защото < em>TriggersProperty на класа Interaction е скрит и не мога да го задам чрез стил.

Има ли някакво решение за проблема? С Отражение или по друг начин?

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>

person Victor Ponomaryov    schedule 01.11.2010    source източник


Отговори (2)


!!!Актуализация!!!

Добре, отидох малко по-далеч. Разширих разширението, за да изпълнява цялата работа, включително настройка на колекцията 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);
            }
        }
    }
}

Уау, отдавна четещ за първи път код за публикуване. Най-накрая научих защо кодът не винаги изрязва и поставя толкова добре. Изпращането на тази актуализация отне толкова много опити.

Сигурен съм, че има причини като дисково пространство, синтактичен анализ или скорост на изобразяване и редакторът поддържа състояние при неуспешно изпращане отлично.

person cbuteau    schedule 05.12.2012

Разбрах, защо се появява грешката. Това е така, защото по време на изпълнение той търси свойство на прикачена зависимост по име на низ, което е „ShadowTriggers“ (както е посочено в сборката System.Windows.Interactivity, статичен конструктор на взаимодействие). Така че създадох собствен персонализиран статичен клас и наследих Triggers Dependency Property от System.Windows.Interaction там (чрез Reflection и AddOwner, току-що изложих свойството като ShadowTriggersProperty). Проработи! Но... Сега трябва да осигуря екземпляр на TriggerCollection на Setter на Property Value на Style, а конструкторът на класа е вътрешен. Да предположим, че няма път по-нататък.

person Victor Ponomaryov    schedule 01.11.2010