我想创建一个 Collection 类型的 AttachedProperty,其中包含对其他现有元素的引用,如下所示:
<Window x:Class="myNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:myNamespace"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter>
<ContentPresenter.Content>
<Button>
<local:DependencyObjectCollectionHost.Objects>
<local:DependencyObjectCollection>
<local:DependencyObjectContainer Object="{Binding ElementName=myButton}"/>
</local:DependencyObjectCollection>
</local:DependencyObjectCollectionHost.Objects>
</Button>
</ContentPresenter.Content>
</ContentPresenter>
<Button x:Name="myButton" Grid.Row="1"/>
</Grid>
</Window>
因此,我创建了一个名为 ObjectContainer 的泛型类,以便能够通过 Binding 实现此目的:
public class ObjectContainer<T> : DependencyObject
where T : DependencyObject
{
static ObjectContainer()
{
ObjectProperty = DependencyProperty.Register
(
"Object",
typeof(T),
typeof(ObjectContainer<T>),
new PropertyMetadata(null)
);
}
public static DependencyProperty ObjectProperty;
[Bindable(true)]
public T Object
{
get { return (T)this.GetValue(ObjectProperty); }
set { this.SetValue(ObjectProperty, value); }
}
}
public class DependencyObjectContainer : ObjectContainer<DependencyObject> { }
public class DependencyObjectCollection : Collection<DependencyObjectContainer> { }
public static class DependencyObjectCollectionHost
{
static DependencyObjectCollectionHost()
{
ObjectsProperty = DependencyProperty.RegisterAttached
(
"Objects",
typeof(DependencyObjectCollection),
typeof(DependencyObjectCollectionHost),
new PropertyMetadata(null, OnObjectsChanged)
);
}
public static DependencyObjectCollection GetObjects(DependencyObject dependencyObject)
{
return (DependencyObjectCollection)dependencyObject.GetValue(ObjectsProperty);
}
public static void SetObjects(DependencyObject dependencyObject, DependencyObjectCollection value)
{
dependencyObject.SetValue(ObjectsProperty, value);
}
public static readonly DependencyProperty ObjectsProperty;
private static void OnObjectsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var objects = (DependencyObjectCollection)e.NewValue;
if (objects.Count != objects.Count(d => d.Object != null))
throw new ArgumentException();
}
}
我无法在集合中建立任何绑定(bind)。我想我已经弄清楚问题出在哪里了。 Collection 中的元素没有与 Binding 相关的 DataContext。然而,我不知道我能做些什么来对抗它。
编辑: 修复了按钮缺失的名称属性。 注意:我知道绑定(bind)无法工作,因为每个未显式声明 Source 的 Binding 都会使用它的 DataContext 作为 Source。正如我已经提到的:我们的 Collection 中没有这样的 DataContext,也没有 VisualTree,不存在的 FrameworkElement 可能是其中的一部分;)
也许有人过去遇到过类似的问题并找到了合适的解决方案。
与 H.B. 的帖子相关的 EDIT2: 通过对集合中的项目进行以下更改,它现在似乎可以工作:
<local:DependencyObjectContainer Object="{x:Reference myButton}"/>
有趣的行为: 当调用 OnObjectsChanged 事件处理程序时,集合包含零个元素...我认为这是因为元素的创建(在 InitializeComponent 方法中完成)尚未完成。
顺便说一句。正如你 H.B.表示使用x:Reference时不需要使用Container类。使用 x:Reference 时是否有我一开始没有看到的缺点?
EDIT3 解决方案: 我添加了一个自定义附加事件,以便在集合更改时收到通知。
public class DependencyObjectCollection : ObservableCollection<DependencyObject> { }
public static class ObjectHost
{
static KeyboardObjectHost()
{
ObjectsProperty = DependencyProperty.RegisterAttached
(
"Objects",
typeof(DependencyObjectCollection),
typeof(KeyboardObjectHost),
new PropertyMetadata(null, OnObjectsPropertyChanged)
);
ObjectsChangedEvent = EventManager.RegisterRoutedEvent
(
"ObjectsChanged",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(KeyboardObjectHost)
);
}
public static DependencyObjectCollection GetObjects(DependencyObject dependencyObject)
{
return (DependencyObjectCollection)dependencyObject.GetValue(ObjectsProperty);
}
public static void SetObjects(DependencyObject dependencyObject, DependencyObjectCollection value)
{
dependencyObject.SetValue(ObjectsProperty, value);
}
public static void AddObjectsChangedHandler(DependencyObject dependencyObject, RoutedEventHandler h)
{
var uiElement = dependencyObject as UIElement;
if (uiElement != null)
uiElement.AddHandler(ObjectsChangedEvent, h);
else
throw new ArgumentException(string.Format("Cannot add handler to object of type: {0}", dependencyObject.GetType()), "dependencyObject");
}
public static void RemoveObjectsChangedHandler(DependencyObject dependencyObject, RoutedEventHandler h)
{
var uiElement = dependencyObject as UIElement;
if (uiElement != null)
uiElement.RemoveHandler(ObjectsChangedEvent, h);
else
throw new ArgumentException(string.Format("Cannot remove handler from object of type: {0}", dependencyObject.GetType()), "dependencyObject");
}
public static bool CanControlledByKeyboard(DependencyObject dependencyObject)
{
var objects = GetObjects(dependencyObject);
return objects != null && objects.Count != 0;
}
public static readonly DependencyProperty ObjectsProperty;
public static readonly RoutedEvent ObjectsChangedEvent;
private static void OnObjectsPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Observable.FromEvent<NotifyCollectionChangedEventArgs>(e.NewValue, "CollectionChanged")
.DistinctUntilChanged()
.Subscribe(args =>
{
var objects = (DependencyObjectCollection)args.Sender;
if (objects.Count == objects.Count(d => d != null)
OnObjectsChanged(dependencyObject);
else
throw new ArgumentException();
});
}
private static void OnObjectsChanged(DependencyObject dependencyObject)
{
RaiseObjectsChanged(dependencyObject);
}
private static void RaiseObjectsChanged(DependencyObject dependencyObject)
{
var uiElement = dependencyObject as UIElement;
if (uiElement != null)
uiElement.RaiseEvent(new RoutedEventArgs(ObjectsChangedEvent));
}
}
最佳答案
您可以使用x:Reference
在 .NET 4 中,它比 ElementName
“更智能”,并且与绑定(bind)不同,它不需要目标是依赖属性。
您甚至可以摆脱容器类,但您的属性需要具有正确的类型,以便 ArrayList 可以直接转换为属性值,而不是将整个列表添加为项目。直接使用x:References
将不起作用。
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
<local:AttachedProperties.Objects>
<col:ArrayList>
<x:Reference>button1</x:Reference>
<x:Reference>button2</x:Reference>
</col:ArrayList>
</local:AttachedProperties.Objects>
public static readonly DependencyProperty ObjectsProperty =
DependencyProperty.RegisterAttached
(
"Objects",
typeof(IList),
typeof(FrameworkElement),
new UIPropertyMetadata(null)
);
public static IList GetObjects(DependencyObject obj)
{
return (IList)obj.GetValue(ObjectsProperty);
}
public static void SetObjects(DependencyObject obj, IList value)
{
obj.SetValue(ObjectsProperty, value);
}
将x:References
进一步编写为
<x:Reference Name="button1"/>
<x:Reference Name="button2"/>
会导致一些更好的错误。
关于wpf - 在类型集合的 AttachedProperty 内绑定(bind)到其他元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6162830/