2011-09-20 66 views
7
设置自定义MarkupExtension

如何从代码设置自定义MarkupExtension从代码

你可以很容易地设置,如果从Xaml。 BindingDynamicResource也是如此。

<TextBox FontSize="{Binding MyFontSize}" 
     Style="{DynamicResource MyStyle}" 
     Text="{markup:CustomMarkup}"/> 

设置通过代码相同的价值观背后需要一点点不同的方法

  1. 绑定:使用textBox.SetBinding或BindingOperations.SetBinding

    Binding binding = new Binding("MyFontSize"); 
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding); 
    
  2. DynamicResource:使用SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle"); 
    
  3. CustomMarkup:如何设置从代码中的自定义MarkupExtension?我应该叫ProvideValue而且这种情况下,我怎么得到的IServiceProvider?*

    CustomMarkupExtension customExtension = new CustomMarkupExtension(); 
    textBox.Text = customExtension.ProvideValue(??); 
    

保持我发现关于这个问题的令人惊讶的一点,所以,能不能做到?


H.B.已回答了问题。只是在这里添加一些细节,为什么我想这样做。我试图为以下问题创建解决方法。

问题是你不能从Binding派生并覆盖ProvideValue,因为它是密封的。你必须这样做,而不是:A base class for custom WPF binding markup extensions。但问题是,当您将Binding返回到Setter时,您会得到一个例外,但在Style之外它可以正常工作。

我读过好几个地方,你应该返还MarkupExtension本身如果TargetObjectSetter允许一旦它被应用于实际FrameworkElement,这是有道理的它reeavaluate。

但是,只有当TargetPropertyobject类型的作品,否则异常又回来了。如果你看看BindingBase的源代码,你可以看到它确实做到了这一点,但它看起来框架有一些秘密成分,使它工作。

回答

6

我认为没有与代码等同的东西,这些服务只能通过XAML获得。从MSDN

MarkupExtension只有一个虚拟方法ProvideValue。输入serviceProvider参数是当XAML处理器调用标记扩展时服务如何传递给实现。

+1

Hey H.B.是的,我也读过,但是我希望还是有办法的。这是一个非常糟糕的消息,自定义的'MarkupExtensions'似乎是一个半工作的概念。如果TargetProperty不是'object'类型,那么它们就不能用在'Style'' Setter'中,所以我希望通过将它自己应用于一个附加的行为来解决这个问题,但是有了这个计划。无论如何,感谢您的回答 –

+0

@Meleak:呃,正如名字所说的MarkupExtensions扩展标记,它们并不真正用于代码中。顺便说一下,我无法在Setters中使用MarkupExtensions时产生任何问题。 –

+1

我同意,这是比名称更隐含的,只是仍然希望有一种方式:)我的评论并不是很清楚,问题是当你给一个'Setter'提供一个'Binding'值。你已经回答了问题,我接受你的回答。添加了一些细节,为什么我想在我的问题中做到这一点 –

2

This Silverlight TV show可能会对这个问题有所了解。我记得他们展示了一些可能有用的代码示例。

+0

没有找到关于这个特定主题的任何内容,但+1为愉快的链接:) –

+0

对不起,它没有帮助,但是,这是一个愉快的节目。 –

1

作为H.B.指出,MarkupExtension仅用于在XAML中使用。

是什么让Binding独特之处在于它实际上是从MarkupExtension这是什么使得它可以使用扩展语法{Binding ...}或完整标记<Binding>...</Binding>并在代码中使用它派生的。

但是,您始终可以尝试创建知道如何使用自定义标记扩展名并将其应用于目标DependencyObject的中介对象(类似于BindingOperations)。为此,我相信您需要使用XamlSetMarkupExtensionAttribute(用于.NET 4)或IReceiveMarkupExtension接口(用于.NET 3.x)。我不完全确定如何使用属性和/或界面,但它可能会指向正确的方向。

+0

我不确定你的意思是“使绑定独特”,它来自'MarkupExtension',所以我的自定义'MarkupExtension',我看不出有什么不同。对于答案的第二部分,你似乎走在了正确的轨道上,这很好理解。 +1 –

+0

我想表达的是'Binding'不遵循创建带MyClassExtension的'MyClass'的通用惯例,它通常会在标记中提供'MyClass'类型的值。 – sellmeadog

+0

仍然不确定我是否关注,但任何'MarkupExtension'都可以像'{markup:Custom}'和''一样设置。无论如何,这并不重要。我在最后使用'MultiBinding'解决了我的问题 –

4

这个作为替代,它的代码生成什么,但不一定优雅的XAML:

 var markup = new CustomMarkup(); 
     markup.ProvideValue(new Target(textBox, TextBox.TextProperty)); 

为目标的实现很简单:

public struct Target : IServiceProvider, IProvideValueTarget 
{ 
    private readonly DependencyObject _targetObject; 
    private readonly DependencyProperty _targetProperty; 

    public Target(DependencyObject targetObject, DependencyProperty targetProperty) 
    { 
     _targetObject = targetObject; 
     _targetProperty = targetProperty; 
    } 

    public object GetService(Type serviceType) 
    { 
     if (serviceType == typeof(IProvideValueTarget)) 
      return this; 
     return null; 
    } 

    object IProvideValueTarget.TargetObject { get { return _targetObject; } } 
    object IProvideValueTarget.TargetProperty { get { return _targetProperty; } } 
} 

的唯一的事情仍然是是能够从XAML对象模型获取引用返回到'CustomMarkup'。有了以上你需要挂上对它的引用。

+0

伟大的帮助我专注于使用标记扩展的代码 –

3

如果你的标记扩展非常简单,并创建一个绑定并返回ProvideValue()的结果,那么你可以添加一个简单的辅助方法:

public class CommandExtension : MarkupExtension 
{ 
    public CommandExtension(string name) 
    { 
     this.Name = name; 
    } 

    public string Name { get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     return GetBinding(this.Name).ProvideValue(serviceProvider); 
    } 

    static Binding GetBinding(string name) 
    { 
     return new Binding("Commands[" + name + "]") { Mode = BindingMode.OneWay }; 
    } 

    public static void SetBinding(DependencyObject target, DependencyProperty dp, string commandName) 
    { 
     BindingOperations.SetBinding(target, dp, GetBinding(commandName)); 
    } 
} 

然后在代码中,你可以叫CommandExtension。 SetBinding()而不是BindingOperations.SetBinding()。

显然,如果你做的任何事情都比这更复杂,那么这个解决方案可能并不合适。

0

如何设置自定义 MarkupExtension从代码?

如果可以修改它,然后简单地提取逻辑到单独SomeMethod其可单独使用和/或从ProvideValue被调用。的

然后代替

textBox.Text = customExtension.ProvideValue(??); 

你只需要调用它

customExtension.SomeMethod(textBox, TextBox.TextProperty); 

很多时候,我们正在创建自定义属性扩展(在XAML像这样使用):

<TextBox Text="{local:SomeExtension ...}" /> 

这可以这样写:

public class SomeExtension : MarkupExtension 
{ 
    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     var provider =  serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 
     var target = provider.TargetObject as DependencyObject; 
     var property = provider.TargetProperty as DependencyProperty; 
     // defer execution if target is data template 
     if (target == null) 
      return this; 
     return SomeMethod(target, property); 
    } 

    public object SomeMethod(DependencyObject target, DependencyProperty property) 
    { 
     ... // do something 
    } 
} 

因为我意识到,有时有必要使用从代码标记扩展,我总是试图把它们写这样的方式。