WPF:每次更新绑定源时,在代码后面访问数据绑定数据?

时间:2010-11-19 17:04:30

标签: wpf listbox data-binding

有没有办法让代码在每次更新时都能访问数据绑定数据?我已经看到了可以挂钩到DependencyProperty的静态ValidateValueCallback委托,但这是静态的,实际上,它的目的只是用于验证。

我有很多情况需要在更新数据绑定源时更新其他对象。一个例子是动画ListBox,我需要将动画添加到添加到框中的第一个新项目。因此,我需要访问新更新的绑定源项目,以便我可以确定哪些项目是新的以及哪些项目应该从列表框中激活。

要清楚,我有一个带有DependencyProperty的UserControl,数据源绑定到外部,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是在AnimatableObservableCollection对象类型的UserControl中声明的DependencyProperty。 AnimatableObservableCollection扩展了ObservableCollection。

4 个答案:

答案 0 :(得分:2)

  

要清楚,我有一个UserControl   一个DependencyProperty表示一个数据   来源必然是外部的和   ListBox.ItemSource绑定到   内部。

这是否意味着UserControl的DataContext是数据源,然后将UserControl中的ListBox 绑定到数据源?因为那是有道理的。否则,我不确定你的意思 - 你会编辑问题并分享你的一些代码/ XAML,这样你的工作更明显吗?

假设我概述的场景,现在,ObservableCollection听起来像是要走的路,我实际上已经能够在XAML中为ItemsControl添加动画(并且可能是从中删除),而无需写作事件处理程序。

假设您将ListBox的ItemsSource绑定到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呈现为绑定到Name中对象的ObservableCollection属性的TextBoxes(将该属性更改为适合您的属性)情况,显然)。

复杂的位是动画。请注意EventTrigger,其RoutedEvent属性“TextBlock.Loaded”。只要将一个项添加到绑定到ListBox的ObservableCollection,该事件就会触发,因为这会导致创建一个新的ListBoxItem,从而创建一个新的TextBlock,其Loaded事件将被触发。

同样,在删除项目时可能会触发一个Unloaded事件。

另请注意,Storyboard.TargetName的{​​{1}}属性是指我们给第二个ColorAnimation构成TextBlock背景的名称。这告诉动画TextBlock可视树中的哪个元素要改变 - 典型的WPF动画总是应用于视觉元素的依赖属性。

在EventTrigger上应用animation可以应用效果(渐变颜色,在这种情况下,但你可以使用不透明度来制作淡入淡出和淡出效果)绑定数据源已更改。

示例中的第二个GradientStop在用户点击此TextBlock时发生的EventTrigger事件上激活,并删除了TextBlock加载时应用的动画(注意对第一个动画设置MouseUp,这会导致它保持结束状态,直到我们明确删除它为止。)

我们现在有一个ListBox,其项目在添加时会“发光”几秒钟,并且在我们点击它们之前会保持突出显示的颜色。

显然,这只是一个起点 - 数据模板和动画都是您可能想要进一步研究的深层主题。但我希望你能发现这是一个有用的例子,说明WPF强大的绑定功能以及它们让你通过XAML单独定义UI的潜力。

答案 1 :(得分:0)

如果您的绑定是双向的,那么您的基础数据源对象应该与前端同步更改。您可以通过在其上实现INotifyPropertyChanged并将处理程序附加到事件来捕获对此对象的任何更改。
http://msdn.microsoft.com/en-us/library/ms743695.aspx

答案 2 :(得分:0)

也许尝试一种不同的方法来识别何时将新项添加到ListBox。而不是试图回应UI告诉你数据已经改变,而是听取数据本身的变化。

例如,如果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....
        }
    }

答案 3 :(得分:0)

我发现如果我在DependencyProperty声明中使用UIPropertyMetadata对象而不是FrameworkPropertyMetadata对象,那么我可以附加一个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)强制转换为控件/类的类型,然后可以调用类中的任何非静态成员。如果您的回调处理程序代码只是一个操作(例如,方法调用或赋值等),那么您可以完全不使用处理程序并使用lambda表达式。这将如下:

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

最后要注意的是,如果要提供默认值,UIPropertyMetadata构造函数会有重载,并且允许您提供默认值,然后是PropertyChangedCallback处理程序。我希望有所帮助。