2011-09-20 17 views
7

¿Cómo se establece un MarkupExtension personalizado desde el código?Establecer CustomupExtension desde el código

Puede establecerlo fácilmente desde Xaml. Lo mismo ocurre con Binding y DynamicResource.

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

Ajuste de los mismos valores a través de código requiere detrás de un enfoque un poco diferente

  1. Encuadernación: Uso textBox.SetBinding o BindingOperations.SetBinding

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

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle"); 
    
  3. CustomMarkup: ¿Cómo se configura una costumbre MarkupExtension de código? ¿Debo llamar a ProvideValue y ese caso, ¿cómo puedo obtener una bodega de un IServiceProvider? *

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

he encontrado sorprendentemente poco sobre el tema es así, ¿Se puede hacer?


H.B. ha respondido la pregunta. Solo agregué algunos detalles aquí sobre por qué quería hacer esto. Traté de crear una solución para el siguiente problema.

El problema es que no puede derivar de Binding y anular ProvideValue ya que está sellado. En su lugar, tendrá que hacer algo como esto: A base class for custom WPF binding markup extensions. Pero entonces el problema es que cuando devuelve un Binding a un Setter obtiene una excepción, pero fuera del Style funciona bien.

que he leído en varios lugares que debe devolver el MarkupExtension sí mismo si el TargetObject es una Setter para permitir que se reeavaluate una vez que se está aplicando a una real FrameworkElement y esto tiene sentido.

Sin embargo, esto sólo funciona cuando el TargetProperty es de tipo object, de lo contrario la excepción está de vuelta. Si nos fijamos en el código fuente de BindingBase, puede ver que hace exactamente esto, pero parece que el marco tiene algún ingrediente secreto que lo hace funcionar.

Respuesta

6

Creo que no hay código equivalente, los servicios solo están disponibles a través de XAML. De MSDN:

MarkupExtension tiene solo un método virtual, ProvideValue.El parámetro input serviceProvider es cómo se comunican los servicios a las implementaciones cuando un procesador XAML llama a la extensión de marcado.

+1

Hey H.B. Sí, lo leí también, sin embargo, esperaba que todavía hubiera una manera. Esta es una muy mala noticia y la costumbre de 'MarkupExtensions' parece un concepto de semi-trabajo. No pueden usarse en un 'Style'' Setter' si TargetProperty no es de tipo 'object', así que esperaba solucionar este problema aplicándolo yo mismo en un comportamiento adjunto, pero ahí va ese plan. De todos modos, gracias por su respuesta –

+0

@Meleak: Bueno, como el nombre dice MarkupExtensions ampliar el marcado, en realidad no están destinados a ser utilizados en el código. Por cierto, no puedo reproducir ningún problema con el uso de MarkupExtensions en Setters. –

+1

Estoy de acuerdo, esto está más que implícito en el nombre, solo esperaba una forma :) No estaba muy claro en mi comentario, el problema es cuando le das un valor 'Vinculante' a un 'Setter'. Has respondido la pregunta y acepto tu respuesta. Agregué algunos detalles de por qué quería hacer esto en mi pregunta –

2

This Silverlight TV show podría arrojar algo de luz sobre este tema. Recuerdo que mostraban algunas muestras de código que podrían ser útiles.

+0

No encontré nada sobre este tema en particular, pero +1 para un enlace agradable :) –

+0

Disculpe, no sirvió de nada, pero sí, fue un espectáculo agradable. –

1

Como H.B. señaló, un MarkupExtension solo está destinado a ser utilizado dentro de XAML.

Lo que hace Binding único es que en realidad se deriva de MarkupExtension que es lo que hace que sea posible el uso de la sintaxis de extensión {Binding ...} o el marcado completo <Binding>...</Binding> y usarlo en el código.

Sin embargo, siempre puede intentar crear un objeto intermediario (algo similar a BindingOperations) que sepa cómo usar su extensión de marcado personalizado y aplicarlo a un destino DependencyObject.

Para hacer esto, creo que necesitaría hacer uso de la interfaz XamlSetMarkupExtensionAttribute (para .NET 4) o IReceiveMarkupExtension (para .NET 3.x). No estoy del todo seguro de cómo hacer uso del atributo y/o interfaz, pero podría indicarle la dirección correcta.

+0

No estoy seguro de lo que quiere decir con "hace que el encuadernado sea único", se deriva de 'MarkupExtension' y lo mismo ocurre con mi' MarkupExtension' personalizado, no puedo ver la diferencia. Para la segunda parte de tu respuesta, pareces estar en el camino correcto, eso es bueno saberlo. +1 –

+0

que estaba tratando de transmitir es que 'Binding' no sigue la convención común de crear' MyClass' acompañado por 'MyClassExtension' que típicamente proporcionaría un valor de tipo' MyClass' en el marcado. – sellmeadog

+0

Todavía no estoy seguro de seguir, pero cualquier 'MarkupExtension' se puede establecer como' {markup: Custom} 'y' '. De todos modos, no importa. Trabajé alrededor de mi problema usando un 'MultiBinding' al final –

4

¿Y esto como una alternativa, que se genera en el código, pero no necesariamente tan elegante como XAML:

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

La aplicación de destino es simplemente:

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; } } 
} 

Lo único que queda es la capacidad de obtener una referencia de regreso a 'CustomMarkup' desde el modelo de objetos XAML. Con lo anterior, necesita atenerse a una referencia al mismo.

+0

Excelente me ayudó a centrarme en el código con la extensión de marcado –

3

Si su extensión de marcado es bastante simple y crea una unión y devuelve el resultado de ProvideValue(), entonces usted puede agregar un método de ayuda sencilla:

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)); 
    } 
} 

Y luego en el código, que sólo puede llamar CommandExtension. SetBinding() en lugar de BindingOperations.SetBinding().

Obviamente, si está haciendo algo más complejo que este, entonces esta solución puede no ser apropiada.

0

¿Cómo se configura un encargo MarkupExtension de código?

Si se puede modificar, a continuación, basta con extraer la lógica separada en SomeMethod que puede ser llamado solo y/o de ProvideValue.

Entonces, en lugar de

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

que acaba de llamar que

customExtension.SomeMethod(textBox, TextBox.TextProperty); 

A menudo estamos creando a medida extensiones de propiedad (utilizado como este en XAML):

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

Esto se puede escribir así:

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 
    } 
} 

Dado que me di cuenta que a veces es una necesidad para usar extensiones de marcado de código Siempre estoy tratando de escribirlas de tal manera.

Cuestiones relacionadas