Eksploruję mechanizm zachowania związanego z Silverlight, aby użyć wzorca Model-View-ViewModel w aplikacjach Silverlight. Na początek próbuję uzyskać prosty Hello World działający, ale całkowicie utknąłem w błędzie, dla którego nie jestem w stanie znaleźć rozwiązania.Błąd podczas wiązania właściwości zależności w niestandardowym zachowaniu
To, co mam teraz, to strona zawierająca przycisk, który po kliknięciu powinien wyświetlić komunikat. Zdarzenie click jest obsługiwane przy użyciu klasy pochodnej Behavior, a komunikat jest określany jako właściwość zależności samego zachowania. Problem pojawia się, próbując powiązać właściwość komunikatu z właściwością klasy viewmodel używanej jako kontekst danych: Dostaję wyjątek w wywołaniu do InitializeComponent
w widoku.
Oto cały kod, którego używam, jak widać, jest to dość proste. Najpierw znaczników na stronie głównej, a widok zawiera:
MojaStrona
<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 (TextBlock jest tam po prostu sprawdzić, czy składnia wią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, istnieją dwie klasy: jedna za zachowanie, a drugi dla 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);
}
}
Proste wystarczy, ale ten kod generuje po uruchomieniu wyjątek AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43]
(na samym początku wartości ustawionej dla właściwości Message). Jestem pewna, że czegoś mi brakuje, ale co?
Dodatkowe informacje: jeśli usuniemy powiązanie z właściwości Message w MyBehavior (to znaczy, jeśli ustawię jej wartość na dowolny ciąg statyczny), działa ona poprawnie. Ponadto kieruję reklamy na silverlight 3 RTW.
Wielkie dzięki!
UPDATE
Wydaje się, że w przeciwieństwie do WPF, Silverlight nie obsługuje wiążące dla dowolnego obiektu pochodzącego z DependencyObject danych, ale tylko na przedmiotach pochodzących z FrameworkElement. Nie dzieje się tak w przypadku Zachowania, a więc powiązanie nie działa.
Znalazłem obejście here, w formie czegoś o nazwie surrogate spoiwa. Zasadniczo określasz element i właściwość do wiązania, a także wartość, jako atrybuty obiektu FrameworkElement zawierającego obiekt inny niż obiekt FrameworkElement.
UPDATE 2
Spoiwo zastępczym nie działa, gdy FrameworkElement zawiera podelementu Interaction.Behaviors.
Znalazłem inne rozwiązanie here, a ten wydaje się działać. Tym razem używana jest wersja DeepSetter. Użytkownik definiuje jeden z takich ustawiających jako statyczny zasób na zawierającym StackPanel, a następnie odwołuje się do zasobu z zachowania. Więc w moim przykładzie, powinniśmy rozwinąć sekcję zasobów StackPanel następująco:
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
<local:DeepSetter
x:Key="MyBehaviorSetter"
TargetProperty="Message"
BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>
... i modyfikować deklarację zachowanie przycisku w następujący sposób:
<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>
UPDATE 3
Dobry news: powiązanie danych dla dowolnego DependecyObject będzie dostępne na Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind
Używam zachowania z niestandardowym DP z SL 4, Nie mam błędu, ale wiązanie nie jest używane, to jest getter/setter źródło nigdy nie jest wywoływane. Próbowałeś ? – MatthieuGD
Jestem zamknięty w SilverLight3, więc stworzyłem metodę rozszerzenia, która pozwala mi wyodrębnić zachowanie poza kontrolą i ustawić żądaną właściwość na nim. To jest hacky, ale nie może działać inaczej. – jtruelove