Błąd podczas wiązania właściwości zależności na niestandardowym zachowaniu
Badam mechanizm dołączonych zachowań Silverlight w celu użycia wzorca Model-View-ViewModel w moich aplikacjach Silverlight. Na początek staram się uzyskać prostą funkcjonalność Hello World, ale całkowicie utknąłem w błędzie, którego nie jestem w stanie znaleźć.
Mam teraz stronę, która zawiera przycisk, który po kliknięciu powinien wyświetlać komunikat. Zdarzenie click jest obsługiwane za pomocą klasy pochodzącej z Behavior, a komunikat jest określony jako właściwość zależności samego zachowania. Problem pojawia się, gdy próbujesz powiązać właściwość message z właściwością klasy viewmodel używanej jako kontekst danych: otrzymuję wyjątek w wywołaniuInitializeComponent
w widoku.
Oto cały kod, którego używam, jak widać, jest raczej prosty. Najpierw znacznik strony głównej i widok, który zawiera:
Moja strona
<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>
Mój widok (TextBlock jest tylko po to, aby sprawdzić, czy składnia powiązania jest poprawna)
<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>
Teraz kod, są dwie klasy: jedna dla zachowania i druga dla modelu widoku:
MyViewmodel
public class MyViewmodel
{
public string MyMessage
{
get { return "Hello, world!"; }
}
}
Moje zachowanie
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);
}
}
Prosty, ale ten kod rzucaAG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43]
(po prawej na początku wartości ustawionej dla właściwości Message) wyjątek podczas uruchamiania. Jestem pewien, że coś mi brakuje, ale co?
Dodatkowe informacje: jeśli usuwam powiązanie z właściwości Message na MyBehavior (to znaczy, jeśli ustawię jego wartość na dowolny ciąg statyczny), działa dobrze. Poza tym mam na celu silverlight 3 RTW.
Wielkie dzięki!
AKTUALIZACJA
Wydaje się, że w przeciwieństwie do WPF, Silverlight nie obsługuje powiązania danych na żadnym obiekcie pochodzącym z DependencyObject, ale tylko na obiektach pochodzących z FrameworkElement. Nie dotyczy to zachowania, dlatego wiązanie nie działa.
Znalazłem obejścietutaj, w formie czegoś o nazwie spoiwa zastępcze. Zasadniczo określasz element i właściwość, która ma być powiązana, a także wartość, jako atrybuty obiektu FrameworkElement zawierającego obiekt inny niż FrameworkElement.
AKTUALIZACJA 2
Zastępcze spoiwo nie działa, gdy FrameworkElement zawiera podelement Interaction.Behaviors.
Znalazłem inne rozwiązanietutaji ten wydaje się działać. Tym razem używaną sztuczką jestDeepSetter. Definiujesz jeden z takich ustawników jako statyczny zasób na zawierającym StackPanel, a następnie odwołujesz się do zasobu z zachowania. W moim przykładzie powinniśmy rozwinąć sekcję zasobów StackPanel w następujący sposób:
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
<local:DeepSetter
x:Key="MyBehaviorSetter"
TargetProperty="Message"
BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>
... i zmodyfikuj deklarację zachowania przycisku w następujący sposób:
<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>
AKTUALIZACJA 3
Dobra wiadomość: powiązanie danych dla dowolnego DependecyObject będzie dostępne w Silverlight 4:http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind