代码之家  ›  专栏  ›  技术社区  ›  Sheridan

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

  •  2
  • Sheridan  · 技术社区  · 14 年前

    每次更新数据时,是否有方法获取访问数据绑定数据背后的代码?我已经看到了静态ValidateValueCallback委托,它可以挂接到DependencyProperty中,但这是静态的,实际上,它的目的只是为了验证。

    我有很多情况需要更新其他对象,因为数据绑定源已更新。一个例子是一个动画列表框,我需要在其中添加一个动画到添加到框中的第一个新项。因此,我需要访问最新更新的绑定源项,以便能够确定哪些项是新的,哪些项应该在列表框中设置动画。

    为了清楚起见,我有一个具有DependencyProperty的用户控件,数据源绑定到外部,ListBox.ItemSource绑定到内部。

    我想一定有一个简单的方法来实现这一点,但经过几天的搜索,我仍然没有找到任何例子。

    针对DJacobson的回复,下面是一些示例代码:

    在用户控件中,我们有一个列表框:

        <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对象类型的用户控件中声明的DependencyProperty。AnimatableObservableCollection扩展了ObservableCollection。

    4 回复  |  直到 14 年前
        1
  •  2
  •   Dan J    14 年前

    为了清楚起见,我有一个用户控件 数据的依赖属性 源绑定到外部 ListBox.ItemSource已绑定到 在内部。

    这是否意味着用户控件 DataContext 是数据源,列表框 在内部 然后用户控件被绑定到数据源?因为那是有道理的。否则,我不确定你的意思-你会编辑这个问题并分享你的一些代码/XAML,让它更明显你在用什么?

    假设我所描述的场景,现在,一个 ObservableCollection 听起来像是一种方法,实际上我已经能够在不编写事件处理程序的情况下,单独在XAML中设置ItemsControl的添加(可能是从中删除)的动画。

    假设你把 ItemsSource 从你的列表框到 ObservableCollection<YourListItemDataObjects> . 您可以创建 DataTemplate 像下面这样把它分配给列表框的 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>
    

    你会看到 数据模板 将导致列表项呈现为绑定到 Name 对象的属性 可观测集合 (显然,将该属性更改为适合您的情况)。

    复杂的部分是动画。注意 EventTrigger ,谁的 RoutedEvent 财产是 “文本块。已加载” . 每当项目添加到 可观测集合 绑定到ListBox,因为这会导致创建一个新的ListBoxItem,从而创建一个新的TextBlock Loaded 事件将触发。

    类似地,在移除项时,可以触发一个已卸载的事件。

    还要注意 Storyboard.TargetName 财产 ColorAnimation 指的是我们给第二个的名字 GradientStop 组成文本块的背景。这将告诉动画文本块的可视树中要更改的元素-典型的WPF动画始终应用于可视元素的依赖属性。

    应用 animation 在EventTrigger上,当控件的绑定数据源发生更改时,您可以将效果(在本例中是渐变色,但也可以使用不透明度来淡入和淡出)应用于控件。

    第二次 事件触发器 在本例中,在 MouseUp 当用户单击此文本块时将发生的事件,它将删除在加载文本块时应用的动画(请注意 AutoReverse="False" 设置第一个动画,这将导致它保持其结束状态,直到我们显式删除它)。

    现在我们有了一个列表框,当添加项时,该列表框的项会“发光”几秒钟,并且在单击它们之前保持高亮显示的颜色。

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

        2
  •  0
  •   Kaido    14 年前

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

        3
  •  0
  •   ChrisNel52    14 年前

    也许可以尝试一种不同的方法来识别何时将新项添加到列表框中。不要试图对告诉您数据已更改的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....
            }
        }
    
        4
  •  0
  •   Sheridan    14 年前

    我发现,如果在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处理程序。我希望这会有帮助。