2009-09-28 11 views
10

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

+1

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

+0

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

Odpowiedz

1

To uzyskać obsługę DataBinding, klasa powinna dziedziczyć z FrameworkElement.Hoping MSFT zapewni wsparcie w Silverlight 4

+0

Rzeczywiście, jest to obsługiwane w Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind – Konamiman

Powiązane problemy