2010-11-19 10 views
2

Gibt es eine Möglichkeit, bei jedem Update Code hinter dem Zugriff auf datengebundene Daten zu erhalten? Ich habe den statischen ValidateValueCallback-Delegaten gesehen, der in eine DependencyProperty eingebunden werden kann, aber das ist statisch und wirklich, sein Zweck dient nur der Validierung.WPF: Zugriff auf datengebundene Daten im Code hinter jeder Aktualisierung der Bindungsquelle?

Ich habe eine Reihe von Situationen, in denen ich andere Objekte aktualisieren muss, während eine datengebundene Quelle aktualisiert wird. Ein Beispiel ist eine animierte ListBox, in der ich nur dem ersten neuen Element, das der Box hinzugefügt wurde, eine Animation hinzufügen muss. Ich brauche daher Zugriff auf die neu aktualisierten Bindungsquellelemente, damit ich herausfinden kann, welche Objekte neu sind und welche Objekte aus der Listbox heraus animiert werden sollen.

Um klar zu sein, habe ich ein UserControl mit einer DependencyProperty, die eine Datenquelle extern gebunden ist und eine ListBox.ItemSource intern gebunden ist.

Ich denke, es muss einen einfachen Weg, dies zu erreichen, aber nach mehreren Tagen der Suche habe ich noch keine Beispiele gefunden.

Als Reaktion auf DJacobson Antwort, hier einige Beispiel-Code:

Im Inneren des Usercontrol haben wir eine List-Box:

<ListBox Name="TheAnimatedListBox" ItemsSource="{Binding QueueItems, ElementName=UserControlName}" 
ItemContainerStyle="{DynamicResource QueueItemStyle}" HorizontalContentAlignment="Stretch" 
IsSynchronizedWithCurrentItem="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
MouseDown="QueueItemsListBox_MouseDown" MinHeight="300" MinWidth="300"> 

Die Usercontrol kein Datacontext gesetzt hat, und wird wie folgt erklärt:

<Controls:AnimatedQueue Grid.Column="0" Grid.Row="1" x:Name="FirstResponseQueue" 
QueueItems="{Binding FirstResponseItems}" /> 

QueueItems ist eine DependencyProperty, die im UserControl vom Typ AnimatableObservableCollection-Objekt deklariert ist. AnimatableObservableCollection erweitert ObservableCollection.

Antwort

0

Ich habe herausgefunden, dass ich, wenn ich ein UIPropertyMetadata-Objekt anstelle eines FrameworkPropertyMetadata-Objekts in der DependencyProperty-Deklaration verwende, einen PropertyChangedCallback -Handler anhängen kann, der jedes Mal aufgerufen wird, wenn DependencyProperty aktualisiert wird. Es wird wie folgt verwendet:

public readonly static DependencyProperty AnimatableItemsProperty = 
DependencyProperty.Register("AnimatableItems", typeof(ItemCollection), typeof 
(YourClassNameHere), new UIPropertyMetadata(OnAnimatableItemsChanged)); 

private static void OnAnimatableItemsChanged(DependencyObject dependencyObject, 
DependencyPropertyChangedEventArgs e) 
{ 
    ((YourClassNameHere)dependencyObject).CallAMethodOrPropertyHere(e); 
} 

public ItemCollection AnimatableItems 
{ 
    get { return (ItemCollection)GetValue(AnimatableItemsProperty); } 
    set { SetValue(AnimatableItemsProperty, value); } 
} 

Da die Callback-Handler statisch ist, haben Sie Ihre Kontrolle zu werfen (DependencyObject) auf die Art Ihrer Kontrolle/Klasse und dann können Sie alle nicht statische Mitglieder in Ihrer Klasse aufrufen . Wenn Ihr Callback-Handler-Code nur eine Operation ist (z. B. ein Methodenaufruf oder eine Zuweisung usw.)), dann können Sie ganz auf den Handler verzichten und stattdessen einen Lambda-Ausdruck verwenden. Dies würde wie folgt aussehen:

public readonly static DependencyProperty AnimatableItemsProperty = 
DependencyProperty.Register("AnimatableItems", typeof(ItemCollection), typeof 
(YourClassNameHere), new UIPropertyMetadata(
(d,e) => ((YourClassNameHere)d).CallAMethodOrPropertyHere(e))); 

Eine letzte Sache zu beachten ist, dass, wenn Sie einen Standardwert zur Verfügung stellen möchten, gibt es Überlastungen des UIPropertyMetadata Konstruktor und ein ermöglicht es Ihnen, den Standardwert zu schaffen, mit einem PropertyChangedCallback gefolgt Handler. Ich hoffe das hilft.

0

Wenn Ihre Bindung zweiseitig ist, sollte sich das zugrundeliegende Datenquellenobjekt synchron mit dem Frontend ändern. Sie können alle Änderungen an diesem Objekt erfassen, indem Sie INotifyPropertyChanged implementieren und Handler an das Ereignis anhängen.
http://msdn.microsoft.com/en-us/library/ms743695.aspx

+0

Es ist eine Einwegbindung. – Sheridan

+0

Wenn es sich um eine Einwegquelle handelt, gilt diese Technik weiterhin für die Aktualisierung der Benutzeroberfläche von der zugrunde liegenden Datenquelle. –

+0

Mein Datentyp implementiert INotifyPropertyChanged, aber das Problem ist nicht ein Problem "UI nicht aktualisieren", es ist ein "nicht in der Lage, auf die Änderungen in der gebundenen Datenquelle von Code behind" Problem zu reagieren. – Sheridan

0

Vielleicht versuchen Sie einen anderen Ansatz zu erkennen, wenn ein neues Element zur ListBox hinzugefügt wird. Anstatt zu versuchen, auf die Benutzeroberfläche zu reagieren, die Ihnen sagt, dass sich die Daten geändert haben, hören Sie sich Änderungen an den Daten selbst an.

Wenn Sie beispielsweise ListBox an eine ObservableCollection gebunden haben, hören Sie sich das CollectionChanged-Ereignis der Sammlung an, um Sie darüber zu informieren, dass ein Element hinzugefügt wurde.

private void TestObservableCollection() 
    { 
     // Create you Collection and handle the CollectionChanged event so that 
     // you know when items are being added or removed from the collection. 
     ObservableCollection<Person> people = new ObservableCollection<Person>(); 
     people.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(people_CollectionChanged); 
    } 

    void people_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     // Check if a new item was added to the ObservableCollection<Person> 
     if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) 
     { 
      // Do something in the UI here.... 
     } 
    } 
+0

Ich habe genau das versucht, aber die Programmausführung tritt nie in die Ereignisbehandlungsmethode ein ... Ich bin nicht sicher warum nicht. – Sheridan

+0

Genau das ... außer dass ich eine AnimatedObservableCollection habe, die ich von ObservableCollection erweitert habe, aber ich kann nicht sehen, warum das einen Unterschied machen würde. – Sheridan

2

klar zu sein, habe ich ein Benutzersteuerelement mit a DependencyProperty, dass eine Datenquelle extern gebunden ist und ein ListBox.ItemSource an intern gebunden.

Bedeutet dies das Usercontrol DataContext die Datenquelle und die ListBox innerhalb die Usercontrol dann an die Datenquelle gebunden ist? Weil das Sinn machen würde. Sonst bin ich nicht sicher, was du meinst - würdest du die Frage bearbeiten und etwas von deinem Code/XAML teilen, damit es etwas offensichtlicher ist, mit was du arbeitest?

Angenommen, das Szenario skizzierte, ein ObservableCollection klingt wie der Weg zu gehen, und ich konnte Additionen zu (und vermutlich auch entfernen) von ItemsControl in XAML tatsächlich animieren, ohne Event-Handler zu schreiben.

Nehmen wir an, Sie binden die ItemsSource Ihrer ListBox an eine ObservableCollection<YourListItemDataObjects>. Sie können eine DataTemplate wie folgt erstellen und an die ItemTemplate Eigenschaft List-Box zuordnen:

<DataTemplate> 
    <TextBlock Name="animatedTextBlock" Text="{Binding Name}"> 
     <TextBlock.Background> 
      <LinearGradientBrush> 
       <LinearGradientBrush.StartPoint>0.5,0.0</LinearGradientBrush.StartPoint> 
       <LinearGradientBrush.EndPoint>0.5,1.0</LinearGradientBrush.EndPoint> 
       <GradientStop Color="White" Offset="0.3"/> 
       <GradientStop x:Name="cellBackgroundBottomStopColor" 
           Color="Orange" Offset="0.9"/> 
      </LinearGradientBrush> 
     </TextBlock.Background> 
     <TextBlock.Triggers> 
      <EventTrigger SourceName="animatedTextBlock" 
          RoutedEvent="TextBlock.Loaded"> 
       <BeginStoryboard Name="flashNewCell"> 
        <Storyboard> 
         <ColorAnimation Storyboard.TargetName="cellBackgroundBottomStopColor" 
             Storyboard.TargetProperty="Color" 
             From="White" To="Orange" 
             Duration="0:0:1" AutoReverse="False"/> 
        </Storyboard> 
       </BeginStoryboard> 
      </EventTrigger> 
      <EventTrigger SourceName="animatedTextBlock" 
          RoutedEvent="TextBlock.MouseUp"> 
       <RemoveStoryboard BeginStoryboardName="flashNewCell" /> 
      </EventTrigger> 
     </TextBlock.Triggers> 
    </TextBlock> 
</DataTemplate> 

Sie werden sehen, dass die DataTemplate wird die Listitems verursachen als Textfelder an die Name Eigentum der Objekte in Ihrem ObservableCollection gebunden zu machen (Ändere diese Eigenschaft natürlich in dem Fall, der in deinem Fall angemessen ist).

Das komplexe Bit ist die Animation. Beachten Sie die EventTrigger, deren RoutedEvent Eigenschaft ist "TextBlock.Loaded". Dieses Ereignis wird ausgelöst, wenn ein Element an die an die ListBox gebundene ObservableCollection hinzugefügt wird, da dies zur Erstellung eines neuen ListBoxItem führt - und somit zu einem neuen TextBlock, dessen Loaded -Ereignis ausgelöst wird.

Ebenso gibt es ein Unloaded-Ereignis, das beim Entfernen eines Elements ausgelöst werden kann.

Beachten Sie auch, dass die Storyboard.TargetName Eigenschaft der bezieht sich auf den Namen gaben wir die zweite GradientStop den TextBlock Hintergrund bilden. Dies teilt der Animation mit, welches Element in der visuellen Struktur des TextBlocks geändert werden soll - typische WPF-Animationen werden immer auf die Abhängigkeitseigenschaften von visuellen Elementen angewendet.

Durch Anwenden eines animation auf einen EventTrigger können Sie Effekte (Farbverlaufsfarben in diesem Fall, aber Sie können mit Opazität auch Fade-Ins und Fade-Outs spielen) auf ein Steuerelement anwenden, wenn seine gebundene Datenquelle ist verändert.

Die zweiten EventTrigger im Beispiel auf dem MouseUp Ereignis aktiviert, die auftreten, wenn der Benutzer auf diesem Textblock klickt, und es entfernt die Animation, die wir angewandt, wenn der Textblock (man beachte die AutoReverse="False" Einstellung an dieser ersten Animation geladen wurde, wodurch es seinen Endstatus beibehält, bis wir es explizit entfernen.

Wir haben jetzt eine ListBox, deren Elemente für einige Sekunden leuchten, wenn sie hinzugefügt werden, und die eine hervorgehobene Farbe beibehalten, bis wir sie anklicken.

Offensichtlich ist dies nur ein Ausgangspunkt - DataTemplates und Animationen sind beide tiefere Themen, die Sie möglicherweise weiter erforschen möchten. Aber ich hoffe, Sie finden dies als ein hilfreiches Beispiel für die leistungsstarken Bindungsfunktionen von WPF und deren Potenzial, mit denen Sie Ihre Benutzeroberfläche nur durch XAML definieren können.

+0

Zuerst, danke für Ihre sehr detaillierte Antwort. Allerdings habe ich die gleiche Code-Anordnung wie Sie beschrieben und das funktioniert sehr gut. Nun, ich fand, dass wenn ich eine Animation hätte, die durch das ListBoxItem.Unloaded Ereignis ausgelöst würde, dass ich es nie sehen würde, weil das Element verschwinden würde, sobald es entladen wurde. – Sheridan

+0

Ich muss daher eine Animation hinzufügen, bevor es im Code gelöscht wird. Auch im Code, ich muss die neuen Elemente mit den alten vergleichen und 'Exit Slide' Animationen auf den Elementen in der alten Sammlung, aber nicht die neue und "Folie eingeben" Animationen auf den neuen Elementen durchführen. Deshalb benötige ich Zugriff in Code, während die Elemente aktualisiert werden. – Sheridan

+0

Eine andere Sache - wenn ich die DataContext-Eigenschaft des UserControl auf "this" setze, werden die Elemente überhaupt nicht angezeigt. Der einzige Weg, wie ich es zum Laufen bringen konnte, war die Verwendung von ElementName = "UserControlName" für jede Bindung. – Sheridan

Verwandte Themen