2013-10-07 10 views
7

Lo scenario: Creare un MarkupExtension per sostituire Grid.Row =”0” di Grid.Row =” {namespace: ClassExtension GridRowName}”(lo stesso per la colonna)WPF MarkupExtension e RowDefinition risultati in NotImplementedException

XAML:

<Grid> 
    <Grid.RowDefinitions> 
    <RowDefinition Height="Auto" x:Name="TitleRow" /> 
    <RowDefinition Height="Auto" x:Name="LastNameRow" /> 
    <RowDefinition Height="Auto" x:Name="FirstNameRow" /> 
    <RowDefinition Height="Auto" x:Name="EmailRow" /> 
    </Grid.RowDefinitions> 
    <Grid.ColumnDefinitions> 
    <ColumnDefinition x:Name="LabelColumn" /> 
    <ColumnDefinition x:Name="ValueColumn" /> 
    </Grid.ColumnDefinitions> 

    <Label Grid.Row="{me:GridDefinition Name=TitleRow}" Grid.ColumnSpan="2" FontWeight="Bold" FontSize="14" /> 
    <Label Grid.Row="{me:GridDefinition Name=LastNameRow}" Grid.Column="{me:GridDefinition Name=LabelColumn}" FontWeight="Bold" FontSize="14" /> 
</Grid> 

Il requisito:

  • Visualizza XAML errori quando un GridRowName incorrent (o columnName) è utilizzato
  • Mostra errori XAML quando una corretta GridRowName (o columnName) viene utilizzato
  • Quando un ColumnName valido viene utilizzato per una dichiarazione di fila (e vica verca) un errore di XAML dovrebbe essere mostrato

Il problema: Tutto funziona bene per Grid.Column, ma Grid.Row getta sempre un “eccezione non implementato” a designtime (Grid.Row è sottolineato , grid.colu mn non è).

Le righe e le colonne hanno entrambi un nome corretto, ma la riga mostra sempre un errore. Se specifichiamo un nome di colonna non valida, la colonna mostra un errore (che si prevede, quindi Grid.Column funziona bene!) enter image description here

Come si può vedere, colonna funziona bene, ma non lo fanno righe. Il problema risiede all'interno del MarkupExtension chiamato GridDefinitionExtension:

[MarkupExtensionReturnType(typeof(int))] 
public class GridDefinitionExtension : MarkupExtension 
{ 
    public string Name { private get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     var referenceExt = new Reference(Name); 
     var definition = referenceExt.ProvideValue(serviceProvider); 

     if (definition is DefinitionBase) 
     { 
      var grid = (definition as FrameworkContentElement).Parent as Grid; 

      if (grid != null && definition is RowDefinition) 
       return grid.RowDefinitions.IndexOf(definition as RowDefinition); 

      if (grid != null && definition is ColumnDefinition) 
       return grid.ColumnDefinitions.IndexOf(definition as ColumnDefinition); 
     } 

     // This Extension only works for DefinitionBase Elements. 
     throw new NotSupportedException(); 
    } 
} 

L'eccezione è trown sulla linea:

var definition = referenceExt.ProvideValue(serviceProvider); 

Dopo aver guardato all'interno della DLL da cui viene chiamato questo metodo ho scoperto che il corpo di questo metodo ProvideValue si presenta così:

public override object ProvideValue(IServiceProvider serviceProvider) 
{ 
    if (serviceProvider == null) 
    throw new ArgumentNullException("serviceProvider"); 
    IXamlNameResolver xamlNameResolver = serviceProvider.GetService(typeof (IXamlNameResolver)) as IXamlNameResolver; 
    if (xamlNameResolver == null) 
    throw new InvalidOperationException(System.Xaml.SR.Get("MissingNameResolver")); 
    if (string.IsNullOrEmpty(this.Name)) 
    throw new InvalidOperationException(System.Xaml.SR.Get("MustHaveName")); 
    object obj = xamlNameResolver.Resolve(this.Name); 
    if (obj == null) 
    { 
    string[] strArray = new string[1] 
    { 
     this.Name 
    }; 
    obj = xamlNameResolver.GetFixupToken((IEnumerable<string>) strArray, true); 
    } 
    return obj; 
} 

ho semplificato questo metodo ProvideValue per mostrare solo il codice cui i In realtà è utilizzare nel mio scenario:

if (serviceProvider == null) 
    throw new ArgumentNullException("serviceProvider"); 

IXamlNameResolver xamlNameResolver = serviceProvider.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver; 

object obj = xamlNameResolver.Resolve(this.Name); 
if (obj == null) 
{ 
    var strArray = new string[1]{ this.Name }; 
    obj = xamlNameResolver.GetFixupToken((IEnumerable<string>)strArray, true); 
} 
return obj; 

quanto pare l'eccezione viene generata dal metodo GetFixUpToken, ma la causa è il metodo Resolve. Questo metodo Resolve restituisce un oggetto valido durante la ricerca di ColumnDefinition in base al nome, ma restituisce NULL quando fa esattamente la stessa cosa per una RowDefinition.

L'errore lanciati da GetFixUpToken è: “NotImplementedException”, che si prevede da quando guardando il codice sorgente del IXamlNameResolver (che in questo caso è di tipo: XamlNameResolverImpl)

enter image description here

Se si guarda a il codice sorgente di questo XamlNameResolverImpl, si può vedere che il metodo “GetFixUpToken” è vuota e genera un'eccezione NotImplemented (guarda http://dotnetinside.com/en/framework/Microsoft+Expression/Microsoft.Expression.WpfPlatform/WpfMarkupExtensionValueSetter)

public object GetFixupToken(IEnumerable<string> names, bool canAssignDirectly) 
{ 
     throw new NotImplementedException(); 
} 
public object GetFixupToken(IEnumerable<string> names) 
{ 
     throw new NotImplementedException(); 
} 

Ma il problema è, come ho già detto, è la chiamata Resolve, che funziona bene per ColumnDefinition ma non riesce per rowdefinitions ...:

Colonna: enter image description here

Fila: enter image description here

A questo punto , non so più cosa fare ...

codice sorgente (progetto di esempio) disponibile all'indirizzo: http://www.frederikprijck.net/stuff/MarkupExtension.rar

+1

Solo una raccomandazione. Usa il codice reale nel tuo post (come quello che hai fatto con xaml) invece di screenshot del codice. Alcuni di questi screenshot sono piuttosto piccoli se ridimensionati in SO. –

+0

Ho usato lo screenshot perché volevo sottolineare tutti i problemi: Xaml Underline, valori di runtime C#, ... Ci sono alcuni screenshot che ho sostituito con il codice, dato che hai ragione. Immagino di aver iniziato a usare schermate e di aver dimenticato di usare il testo dove possibile. A proposito, ho aggiunto il progetto sorgente per questi motivi :-) –

risposta

1

Ecco una soluzione che, pur diversa dalla tua implementazione, porta a termine il lavoro.

Invece di un MarkupExtension, creare un IValueConverter per l'uso con una rilegatura:

public class GridDefinitionConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      var definition = value as DefinitionBase; 
      int toReturn = 0; 
      if (definition != null) 
      { 
       var grid = (definition as FrameworkContentElement).Parent as Grid; 

       if (grid != null && definition is RowDefinition) 
        toReturn = grid.RowDefinitions.IndexOf(definition as RowDefinition); 

       if (grid != null && definition is ColumnDefinition) 
        toReturn = grid.ColumnDefinitions.IndexOf(definition as ColumnDefinition); 
      } 
      return toReturn; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

Poi mettere questo nel tuo XAML:

<Grid.Resources> 
    <me:GridDefinitionConverter x:Key="gridDefinitionConverter" /> 
</Grid.Resources> 

e realizzare in questo modo:

<Label Grid.Row="{Binding ElementName=TitleRow, Converter={StaticResource gridDefinitionConverter}}" /> 
+0

A quanto pare, questa soluzione NON convalida in fase di progettazione: http://prntscr.com/1w31id Come potete vedere, TitleRow2 non esiste. Questo è il punto che voglio raggiungere. La mia soluzione funziona bene, come ho detto. L'unico problema è che in fase di progettazione lo sviluppatore deve ottenere un feedback. (che non funziona con RowDefinitions nel mio esempio ... ColumnDefinitions funziona correttamente) –

+0

La mia soluzione funziona in fase di progettazione, cosa che il tuo no. Sembra che in WPF sia un problema il fatto che le righe non siano inizializzate quando MarkupExtension deve accedervi. Il tuo screenshot non ti mostra di usare il convertitore che ho consigliato, collegandoti direttamente a RowDefinition, che non funzionerà. Assicurati di includere il convertitore e crea il IValueConverter. –

+0

Oh dio, in realtà ho fatto il convertitore ma ho dimenticato di usarlo. Volevo semplicemente dargli un test, dal momento che non utilizza MarkupExtension, non la vedo davvero come una soluzione al problema. Apparentemente funziona. Quindi ora ho idd una soluzione funzionante. In questo caso mi sto preoccupando delle prestazioni, preferisco non utilizzare i binding per diversi motivi, ma immagino che non ci sia modo di funzionare senza Binding? Grazie mille per il tuo aiuto, non è che non ho mai usato convertitori Value (o multiValue) ... Mi sono concentrato sulla MarkupExtension :( –

Problemi correlati