有没有办法让代码在每次更新时都能访问数据绑定数据?我已经看到了可以挂钩到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。
答案 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处理程序。我希望有所帮助。