Подтвердить что ты не робот

Ошибка при привязке свойства зависимостей к пользовательскому поведению

Я изучаю механизм прикрепленного поведения Silverlight, чтобы использовать шаблон Model-View-ViewModel в моих приложениях Silverlight. Для начала я пытаюсь получить простой Hello World, но я полностью застрял в ошибке, для которой я не могу найти решение.

Теперь у меня есть страница, которая содержит только кнопку, которая должна отображать сообщение при нажатии. Событие click обрабатывается с использованием класса, полученного из Behavior, и сообщение указывается как свойство зависимости самого поведения. Проблема возникает при попытке привязать свойство message к свойству класса viewmodel, используемого в качестве контекста данных: я получаю exeption в вызове InitializeComponent в представлении.

Вот весь код, который я использую, поскольку вы можете видеть, что это довольно просто. Сначала разметка главной страницы и ее вида:


MyPage

<UserControl x:Class="MyExample.MyPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyExample"
    >
    <Grid x:Name="LayoutRoot">
        <local:MyView/>
    </Grid>
</UserControl>


MyView (в текстовом блоке есть только проверка правильности синтаксиса привязки)

<UserControl x:Class="MyExample.MyView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:local="clr-namespace:MyExample"
    Width="400" Height="300">
    <StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="White">
        <StackPanel.Resources>
            <local:MyViewmodel x:Key="MyResource"/>
        </StackPanel.Resources>
        <TextBlock Text="This button will display the following message:"/>
        <TextBlock Text="{Binding MyMessage, Source={StaticResource MyResource}}" FontStyle="Italic"/>
        <Button x:Name="MyButton" Content="Click me!">
            <i:Interaction.Behaviors>
                <local:MyBehavior Message="{Binding MyMessage, Source={StaticResource MyResource}}"/>
            </i:Interaction.Behaviors>
        </Button>
    </StackPanel>
</UserControl>


Теперь код, есть два класса: один для поведения и еще один для viewmodel:

MyViewmodel

public class MyViewmodel
{
    public string MyMessage
    {
        get { return "Hello, world!"; }
    }
}


MyBehavior

public class MyBehavior : Behavior<Button>
{
    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message",
        typeof(string), typeof(MyBehavior),
        new PropertyMetadata("(no message)"));

    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Click += new RoutedEventHandler(AssociatedObject_Click);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Click -= new RoutedEventHandler(AssociatedObject_Click);
    }

    void AssociatedObject_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(Message);
    }
}


Достаточно просто, но этот код генерирует AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43] (прямо в начале значения, заданного для свойства Message) при запуске. Я уверен, что я что-то упустил, но что?

Дополнительная информация: если я удалю привязку из свойства Message на MyBehavior (то есть, если я установил его значение для любой статической строки), он отлично работает. Кроме того, я нацелен на silverlight 3 RTW.

Спасибо большое!


UPDATE

Похоже, что в отличие от WPF, Silverlight не поддерживает привязку данных к любому объекту, происходящему из DependencyObject, а только к объектам, происходящим из FrameworkElement. Это не относится к Поведению, поэтому привязка не работает.

Я нашел обходное решение здесь, в виде чего-то названного суррогатным связующим. В основном вы указываете связанный элемент и свойство, а также значение как атрибуты элемента FrameworkElement, содержащего объект non-FrameworkElement.


ОБНОВЛЕНИЕ 2

Суррогатное связующее не работает, когда FrameworkElement содержит подэлемент Interaction.Behaviors.

Я нашел другое решение здесь, и этот, похоже, работает. На этот раз используется трюк DeepSetter. Вы определяете один из таких сеттеров как статический ресурс на содержащем StackPanel, а затем ссылаетесь на ресурс из поведения. Поэтому в моем примере мы должны развернуть раздел ресурсов StackPanel следующим образом:

<StackPanel.Resources>
    <local:MyViewmodel x:Key="MyResource"/>
    <local:DeepSetter 
        x:Key="MyBehaviorSetter"
        TargetProperty="Message"
        BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>

... и измените объявление поведения кнопки следующим образом:

<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>

ОБНОВЛЕНИЕ 3

Хорошие новости: привязка данных для любого DependecyObject будет доступна в Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind

4b9b3361

Ответ 1

Чтобы получить поддержку DataBinding, класс должен наследовать от FrameworkElement.Hoping MSFT предоставит поддержку в Silverlight 4