I've created a custom control which, when bound to a custom collection of objects, displays the content of those objects.
Usually, I can use this control by simply going:
<local:CustomCollectionDisplayer DataContext="{Binding Source={x:Static Application.Current}, Path=SomeObject.InstanceOfCustomeCollectionOfCustomItems}" />
My problem now comes where I want to recycle this control to show only a single object. In the xaml, I want to make a custom collection where the only item in the collection is bound to that single object.
The code looks like this:
<local:CustomCollectionDisplayer>
<local:CustomCollectionDisplayer.DataContext>
<local:CustomCollection>
<local:CustomItem Reference="{Binding Source={x:Static Application.Current}, Path=SomeObject.InstanceOfCustomItem}"/>-->
</local:CustomCollection>
</local:CustomCollectionDisplayer.DataContext>
</local:CustomCollectionDisplayer>
Obviously, there's no 'Reference' property which I can use to make the CustomItem in the collection point to the instance of CustomItem in 开发者_如何转开发'SomeClass'. How can I achieve this without creating a dummy CustomCollection containing this CustomItem in my object viewmodel?
There already is a x:Reference
markup extension, but it is very limited as it only gets objects by name. You could write your own markup-extension which can get properties. e.g.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.ComponentModel;
namespace Test.MarkupExtensions
{
[ContentProperty("Object")]
public class GetExtension : MarkupExtension
{
public object Object { get; set; }
public string PropertyName { get; set; }
public GetExtension() { }
public GetExtension(string propertyName)
: this()
{
if (propertyName == null)
throw new ArgumentNullException("propertyName");
PropertyName = propertyName;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (PropertyName == null)
throw new InvalidOperationException("PropertyName cannot be null");
if (Object == null)
{
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
Object = target.TargetObject;
}
var prop = Object.GetType().GetProperty(PropertyName);
if (prop == null)
throw new Exception(String.Format("Property '{0}' not found on object of type {1}.", PropertyName, Object.GetType()));
return prop.GetValue(Object, null);
}
}
}
Which could be used like this:
<local:CustomCollectionDisplayer>
<local:CustomCollectionDisplayer.DataContext>
<local:CustomCollection>
<me:Get PropertyName="InstanceOfCustomItem">
<me:Get PropertyName="SomeObject" Object="{x:Static Application.Current}"/>
</me:Get>
</local:CustomCollection>
</local:CustomCollectionDisplayer.DataContext>
</local:CustomCollectionDisplayer>
You could also resolve a whole PropertyPath
at once in the extension if you prefer that, this is just a sketchy example.
Another option is to bind the DataContext
directly to the object and wrap it in the collection using a Converter
.
精彩评论