问题
I am working on a Custom MarkupExtension in wich I need a non string parameters from XAML to construct the new object. Is it possible to use a non-string parameter binding on a field in datacontext scope?
In other words, how can I do something like this?
<ListBox ItemsSource="{Binding Source={local:MyMarkupExtension {x:Type Button},IncludeMethods={Binding Source=CustomerObject.IsProblematic}}}" />
where IncludeMethods=CustomerObject.IsProblematic
give me this error:Binding cannot be set on the 'IncludeMethods' property of type 'TypeDescriptorExtension'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Can anyone help me?
thanks
回答1:
A 'Binding' can only be set on a DependencyProperty of a DependencyObject - it is true. The problem is that MarkupExtension
class does not derive from DependencyObject
, that's why it is not possible to set binding on it's properties.
[EDIT]
Workaround is using ValueConverters. Another workaround is to change C# language to allow multiple inheritance. By the way, in Silverlight MarkupExtension
implements IMarkupExtension
interface, so I tried to implement it in my custom extension and derive it from DependecyObject
, added DependencyProperty
there and set binding to it. It doesn't crash, but the binding is actually set after ProvideValue() is called. So even in Silverlight there's no solution (or it is difficult - see link provided in Klaus78's answer). In WPF MarkupExtension doesn't implement any interface, so you cannot bind to it's properties.
回答2:
This link is informative about
Custom Markup Extension with bindable properties
EDIT Someone make me note that this works only for Silverlight, because in WPF MarkupExtension doesn't implement IMarkupExtension interface. (Thank you EvAlex)
回答3:
I found a workaround for this problem.
The main idea is to define attached property for each parameter that requires binding.
public class MarkupExtensionWithBindableParam : MarkupExtension
{
public BindingBase Param1 { get; set; } // its necessary to set parameter type as BindingBase to avoid exception that binding can't be used with non DependencyProperty
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
DependencyObject targetObject;
DependencyProperty targetProperty;
if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
{
targetObject = (DependencyObject)target.TargetObject;
targetProperty = (DependencyProperty)target.TargetProperty;
}
else
{
return this; // magic
}
// Bind the Param1 to attached property Param1BindingSinkProperty
BindingOperations.SetBinding(targetObject, MarkupExtensionWithBindableParam.Param1BindingSinkProperty, Param1);
// Now you can use Param1
// Param1 direct access example:
object param1Value = targetObject.GetValue(Param1BindingSinkProperty);
// Param1 use in binding example:
var param1InnerBinding = new Binding() { Source = targetObject, Path = new PropertyPath("(0).SomeInnerProperty", Param1BindingSinkProperty) }); // binding to Param1.SomeInnerProperty
return param1InnerBinding.ProvideValue(serviceProvider); // return binding to Param1.SomeInnerProperty
}
private static DependencyProperty Param1BindingSinkProperty = DependencyProperty.RegisterAttached("Param1BindingSink", typeof(object)// set the desired type of Param1 for at least runtime type safety check
, typeof(MarkupExtensionWithBindableParam ), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
}
Usage is straightforward:
<TextBlock Text={local:MarkupExtensionWithBindableParam Param1={Binding Path="SomePathToParam1"}}/>
来源:https://stackoverflow.com/questions/10328802/markupextension-with-binding-parameters