2011-09-20 16 views
7

Come si imposta un codice personalizzato MarkupExtension?Imposta markupExtension personalizzato dal codice

È possibile impostare facilmente se da Xaml. Lo stesso vale per Binding e DynamicResource.

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

Impostazione gli stessi valori attraverso il codice richiede dietro un po 'diverso approccio

  1. Binding: Usa textBox.SetBinding o BindingOperations.SetBinding

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

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle"); 
    
  3. CustomMarkup: Come faccio a impostare una consuetudine MarkupExtension dal codice? Devo chiamare ProvideValue e quel caso, come faccio a ottenere una sospensione di un IServiceProvider? *

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

ho trovato sorprendentemente poco su questo argomento così, si può fare?


H.B. ha risposto alla domanda. Aggiungo solo alcuni dettagli al motivo per cui ho voluto farlo. Ho provato a creare una soluzione alternativa per il seguente problema.

Il problema è che non è possibile derivare da Binding e sovrascrivere ProvideValue poiché è sigillato. Dovrai fare qualcosa di simile a questo: A base class for custom WPF binding markup extensions. Ma il problema è che quando si restituisce un Binding a un Setter si ottiene un'eccezione, ma al di fuori dello Style funziona correttamente.

ho letto in diversi luoghi che si dovrebbe restituire il MarkupExtension stesso se il TargetObject è un Setter per permettergli di reeavaluate una volta che viene applicato ad un vero e proprio FrameworkElement e questo ha un senso.

tuttavia, che funziona solo quando il TargetProperty è di tipo object, altrimenti l'eccezione è tornato. Se si guarda il codice sorgente per BindingBase si può vedere che fa esattamente questo ma sembra che il framework abbia qualche ingrediente segreto che lo fa funzionare.

risposta

6

Penso che non ci sia un equivalente di codice, i servizi sono disponibili solo tramite XAML. Da MSDN:

MarkupExtension ha solo un metodo virtuale, ProvideValue.Il parametro serviceProvider di input è il modo in cui i servizi vengono comunicati alle implementazioni quando l'estensione di markup viene richiamata da un processore XAML.

+1

Hey H.B. Sì, ho letto anche questo, tuttavia speravo che ci fosse ancora un modo. Questa è una pessima notizia e il custom 'MarkupExtensions' sembra un concetto semi-lavorativo. Non possono essere usati in un 'Setter' di stile se la TargetProperty non è di tipo' object', quindi speravo di aggirare questo problema applicandomi a me stesso in un comportamento collegato, ma il piano è stato risolto. Comunque, grazie per la tua risposta –

+0

@Meleak: beh, come dice il nome MarkupExtensions estende il markup, non sono pensati per essere usati nel codice. A proposito, non riesco a riprodurre alcun problema con l'uso di MarkupExtensions in Setters. –

+1

Sono d'accordo, questo è più che implicito nel nome, speravo ancora in un modo :) Non ero molto chiaro nel mio commento, il problema è quando fornisci un valore 'Binding' a un' Setter'. Hai risposto alla domanda e accetto la tua risposta. Aggiunti alcuni dettagli sul motivo per cui ho voluto farlo nella mia domanda –

2

This Silverlight TV show potrebbe far luce su questo problema. Li ricordo che mostrano alcuni esempi di codice che potrebbero essere utili.

+0

Non ho trovato nulla su questo particolare argomento ma +1 per un link divertente :) –

+0

Spiacente, non è stato d'aiuto, ma sì, è stato uno spettacolo divertente. –

1

Come H.B. sottolineato, un MarkupExtension è destinato solo per essere utilizzato all'interno di XAML.

Ciò che rende Binding unica è che in realtà deriva da MarkupExtension che è ciò che rende possibile utilizzare la sintassi estensione {Binding ...} o l'intero markup <Binding>...</Binding> e usarlo nel codice.

Tuttavia, è sempre possibile provare a creare un oggetto intermedio (qualcosa di simile a BindingOperations) che sa come utilizzare l'estensione di markup personalizzata e applicarlo a un target DependencyObject.

Per fare ciò, credo che sarebbe necessario utilizzare l'interfaccia XamlSetMarkupExtensionAttribute (per .NET 4) o l'interfaccia IReceiveMarkupExtension (per .NET 3.x). Non sono completamente sicuro su come utilizzare l'attributo e/o l'interfaccia, ma potrebbe indirizzarti nella giusta direzione.

+0

Non sono sicuro di cosa intendi per "rende Binding univoco", deriva da "MarkupExtension" e così anche il mio personalizzato "MarkupExtension", non riesco a vedere la differenza. Per la seconda parte della tua risposta sembra che tu sia sulla strada giusta, è bello saperlo. +1 –

+0

Stavo cercando di comunicare che 'Binding' non segue la convenzione comune di creare' MyClass' accompagnata da 'MyClassExtension' che di solito fornisce un valore di tipo' MyClass' nel markup. – sellmeadog

+0

Non sono ancora sicuro di seguire, ma qualsiasi 'MarkupExtension' può essere impostato come' {markup: Custom} 'e' '. Ad ogni modo, non importa. Ho lavorato attorno al mio problema usando un 'MultiBinding' alla fine –

4

Che dire di questo come alternativa, che viene generato dal codice, ma non necessariamente elegante come XAML:

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

L'implementazione per Target è semplicemente:

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

L'unica cosa che rimane è la possibilità di ottenere un riferimento a "CustomMarkup" dal modello a oggetti XAML. Con quanto sopra è necessario aggrapparsi a un riferimento ad esso.

+0

Grande mi ha aiutato a concentrarsi sul codice utilizzando l'estensione di markup –

3

Se l'interno di markup è abbastanza semplice e crea un legame e restituisce il risultato da ProvideValue() allora si può aggiungere un metodo di supporto semplice:

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

E poi nel codice, si può chiamare CommandExtension. SetBinding() invece di BindingOperations.SetBinding().

Ovviamente, se si sta facendo qualcosa di più complesso di questo, questa soluzione potrebbe non essere appropriata.

0

Come faccio a impostare un personalizzato MarkupExtension dal codice?

Se è possibile modificarlo, quindi è sufficiente estrarre la logica in separata SomeMethod che può essere chiamato solo e/o da ProvideValue.

Allora, invece di

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

basta chiamare lo

customExtension.SomeMethod(textBox, TextBox.TextProperty); 

Spesso si stanno creando su misura Estensioni di proprietà (utilizzato come questo in XAML):

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

questo può essere scritto in questo modo:

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

Da quando ho capito che a volte è una necessità di utilizzare le estensioni di markup dal codice che sto sempre cercando di scrivere loro in questo modo.