2010-11-19 3 views
2

Есть ли способ получить код за доступом к данным привязки данных каждый раз, когда он обновляется? Я видел статический делегат ValidateValueCallback, который можно подключить к DependencyProperty, но это статично и действительно, это цель только для проверки.WPF: Доступ к данным привязки данных в коде за каждый раз, когда обновляется источник привязки?

У меня есть ряд ситуаций, когда мне нужно обновить другие объекты, когда обновляется источник данных. Одним из примеров является анимированный ListBox, где мне нужно добавить анимацию только к первому новому элементу, добавленному в поле. Поэтому мне нужен доступ к недавно обновленным элементам источника привязки, чтобы я мог определить, какие элементы являются новыми, а какие элементы должны быть анимированы из списка.

Чтобы быть ясным, у меня есть UserControl с DependencyProperty, что источник данных привязан к внешнему виду, а ListBox.ItemSource привязан к внутреннему.

Я думаю, что должен быть простой способ достичь этого, но после нескольких дней поиска я до сих пор не нашел примеров.

В ответ на ответ DJacobson, вот пример кода:

Внутри UserControl мы имеем ListBox:

<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"> 

UserControl не затвердеет не DataContext и объявлен как это:

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

QueueItems - это DependencyProperty, объявленный в объекте UserControl типа AnimatableObservableCollection. AnimatableObservableCollection расширяет ObservableCollection.

ответ

0

Я обнаружил, что если я использую объект UIPropertyMetadata вместо объекта FrameworkPropertyMetadata в объявлении DependencyProperty, тогда я могу прикрепить обработчик PropertyChangedCallback, который вызывается каждый раз при обновлении DependencyProperty. Она используется следующим образом:

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); } 
} 

Как обработчик обратного вызова является статическим, вы должны отдать свой контроль (DependencyObject) к типу вашего управления/класса, а затем вы можете вызывать любые не статические член в классе , Если код обработчика обратного вызова - всего одна операция (например, вызов метода или назначение и т. Д.), то вы можете обойтись без обработчика и использовать вместо него лямбда-выражение. Это будет выглядеть следующим образом:

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

последнее, что следует отметить, что если вы хотите, чтобы обеспечить значение по умолчанию, есть перегруженный конструктор UIPropertyMetadata и один позволяют обеспечить значение по умолчанию, после чего PropertyChangedCallback обработчик. Надеюсь, это поможет.

0

Если ваша привязка в два раза, ваш базовый объект источника данных должен меняться синхронно с передним концом. Вы можете зафиксировать любые изменения этого объекта, выполнив на нем INotifyPropertyChanged и привязывая обработчики к событию.
http://msdn.microsoft.com/en-us/library/ms743695.aspx

+0

Это односторонняя привязка. – Sheridan

+0

Если это односторонний источник, этот метод по-прежнему применяется для обновления пользовательского интерфейса из базового источника данных. –

+0

Мой тип данных реализует INotifyPropertyChanged, но проблема не в проблеме «не обновляемого интерфейса», это «неспособность реагировать на изменения в связанном источнике данных из-за недостатка кода». – Sheridan

0

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

Например, если вы ListBox привязаны к ObservableCollection, тогда слушайте событие CollectionChanged коллекции, чтобы уведомить вас, что элемент был добавлен.

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

Я пробовал именно это, но выполнение программы никогда не входит в метод обработки событий ... Я не уверен, почему нет. – Sheridan

+0

Точно это ... за исключением того, что у меня есть AnimatedObservableCollection , который я продлил с ObservableCollection , но я не понимаю, почему это имеет значение. – Sheridan

2

Чтобы было ясно, у меня есть UserControl с DependencyProperty что источник данных, связанный с внешним и ListBox.ItemSource привязан к внутри.

Означает ли это UserControl-х DataContext является источником данных, а также ListBox в UserControl затем связывается с источником данных? Потому что это имело бы смысл. В противном случае, я не уверен, что вы имеете в виду, - отредактировал бы вопрос и разделил бы свой код/​​XAML, чтобы было немного более очевидно, с чем вы работаете?

Предполагая, что сценарий, который я изложил, на данный момент звучит как способ, и мне действительно удалось анимировать дополнения (и, предположительно, абзацы) элемента ItemsControl только в XAML, без записи обработчиков событий.

Предположим, вы связываете ItemsSource вашего ListBox с номером ObservableCollection<YourListItemDataObjects>. Вы можете создать DataTemplate как следующий и назначить его в ListBox в ItemTemplate собственности:

<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> 

Вы увидите, что DataTemplate вызовет ListItems оказывать в TextBoxes привязанных к Name собственности объектов в вашей ObservableCollection (измените это свойство на все, что подходит в вашем случае, очевидно).

Комплексный бит - это анимация. Обратите внимание: EventTrigger, RoutedEvent Недвижимость "TextBlock.Loaded". Это событие будет срабатывать всякий раз, когда элемент добавляется в ObservableCollection, связанный с ListBox, поскольку это вызывает создание нового элемента ListBoxItem - и, следовательно, будет создан новый TextBlock, чье событие Loaded.

Аналогично, при удалении элемента происходит событие Unloaded, которое вы можете активировать.

Также обратите внимание, что свойство Storyboard.TargetNameColorAnimation относится к названию, которое мы указали во втором GradientStop, составляющем фон TextBlock. Это говорит анимации, какой элемент в визуальном дереве TextBlock изменяется - типичные анимации WPF всегда применяются к свойствам зависимостей визуальных элементов.

Применение animation на EventTrigger позволяет применять эффекты (в этом случае цвета градиента, но вы можете играть с непрозрачностью, чтобы также делать затухания и затухания), когда его связанный источник данных изменено.

в примере Второй EventTrigger активируется на MouseUp событие, которое произойдет, когда пользователь нажимает на эту TextBlock, и он удаляет анимацию, мы применили, когда TextBlock был загружен (обратите внимание на установку AutoReverse="False" на этой первой анимации, который заставляет его поддерживать свое конечное состояние, пока мы его явно не удалим).

Теперь у нас есть ListBox, чьи элементы «светятся» в течение нескольких секунд, когда они добавлены, и которые поддерживают выделенный цвет, пока мы не нажмем на них.

Очевидно, что это только отправная точка - DataTemplates и Animations - это глубокие темы, которые вы, возможно, захотите исследовать дальше. Но я надеюсь, что вы найдете этот полезный пример мощных возможностей привязки WPF и их возможностей, позволяющих вам определять свой пользовательский интерфейс только с помощью XAML.

+0

Во-первых, спасибо за ваш очень подробный ответ. Тем не менее, у меня такое же кодовое расположение, как вы описали, и это работает отлично. Ну, я обнаружил, что если бы у меня была анимация, вызванная событием ListBoxItem.Unloaded, которое я никогда не увижу, потому что элемент исчезнет, ​​как только он будет выгружен. – Sheridan

+0

Поэтому я должен добавить к нему анимацию, прежде чем она будет выгружена в код позади. Также в коде мне нужно сравнить новые элементы со старыми и выполнить анимацию «выходного слайда» на элементах старой коллекции, но не новые и «ввести слайд» анимации на новые элементы. Вот почему мне нужен доступ в коде, поскольку элементы обновляются. – Sheridan

+0

Еще одна вещь - когда я устанавливаю свойство DataContext UserControl в значение «this», элементы вообще не отображаются. Таким образом, единственный способ заставить его работать - это использовать ElementName = «UserControlName» для каждой привязки. – Sheridan

Смежные вопросы