2013-10-07 6 views
7

Le scénario: Créer un MarkupExtension pour remplacer Grid.Row = » 0” par Grid.Row = » {espace de noms: ClassExtension GridRowName} »(même pour la colonne)résultats WPF MarkupExtension et RowDefinition à NotImplementedException

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

L'exigence:

  • Afficher les erreurs XAML lorsqu'un GridRowName de incorrent (ou columnName) est utilisé
  • Afficher aucune erreur XAML lorsqu'un GridRowName correct (ou columnName) est utilisé
  • Lorsqu'un NomColonne valide est utilisé pour une déclaration de ligne (et Vica verca) une erreur de XAML doit être montré

Le problème: Tout fonctionne bien pour Grid.Column, mais grid.Row lance toujours un « Non implémenté Exception » à designtime (grid.row est souligné , grid.colu mn n'est pas). enter image description here

Les lignes et colonnes sont toutes les deux nommées correctes, mais la ligne affiche toujours une erreur. Si nous spécifions un nom de colonne incorrect, la colonne affiche une erreur (qui devrait, si Grid.Column fonctionne très bien!) enter image description here

Comme vous pouvez le voir, la colonne fonctionne très bien, mais les lignes ne le font pas. Le problème se trouve à l'intérieur du MarkupExtension appelé 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'exception est trown sur la ligne:

var definition = referenceExt.ProvideValue(serviceProvider); 

Après avoir regardé à l'intérieur de la DLL à partir de laquelle cette méthode est appelée, j'ai trouvé que le corps de cette méthode de ProvideValue ressemble à ceci:

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

J'ai simplifié cette méthode de ProvideValue pour afficher uniquement le code qu'il i s en utilisant réellement dans mon scénario:

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; 

Apparantly l'exception est levée par la méthode GetFixUpToken, mais la cause est la méthode de Resolve. Cette méthode Resolve renvoie un objet valide lorsque vous recherchez la ColumnDefinition par son nom, mais elle renvoie NULL lorsque vous faites exactement la même chose pour une RowDefinition.

L'erreur lancée par GetFixUpToken est: « NotImplementedException », qui devrait depuis quand on regarde le code source du IXamlNameResolver (qui dans ce cas est de type: XamlNameResolverImpl)

enter image description here

Lorsque l'on regarde le code source de ce XamlNameResolverImpl, vous pouvez voir que la méthode « GetFixUpToken » est vide et renvoie une exception NotImplemented (regardez 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(); 
} 

Mais le problème est, comme je l'ai déjà dit, est l'appel de Resolve, qui fonctionne très bien pour ColumnDefinition mais échoue pour rowdefinitions ...:

Colonne: enter image description here

Row: enter image description here

À ce stade , je ne sais pas quoi faire ...

code source (exemple de projet) disponible à: http://www.frederikprijck.net/stuff/MarkupExtension.rar

+1

Juste une recommandation. Utilisez le code actuel dans votre message (comme ce que vous avez fait avec le xaml) au lieu de captures d'écran du code. Certaines de ces captures d'écran sont assez petites lorsqu'elles sont redimensionnées dans SO. –

+0

J'ai utilisé la capture d'écran parce que je voulais souligner tous les problèmes: Xaml Underline, valeurs d'exécution C#, ... Il y a quelques captures d'écran que j'ai remplacées par du code, puisque vous avez raison. Je suppose que j'ai commencé à utiliser des captures d'écran et j'ai oublié d'utiliser du texte si possible. Btw, j'ai ajouté le projet source pour ces raisons :-) –

Répondre

1

Voici une solution qui, bien que différente de votre implémentation, fait le travail.

Au lieu d'un MarkupExtension, créez un IValueConverter pour une utilisation avec une liaison:

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

ensuite mis dans votre XAML:

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

et mettre en œuvre comme ceci:

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

Apparemment, cette solution ne valide pas au moment du design: http://prntscr.com/1w31id Comme vous pouvez le voir, TitleRow2 n'existe pas. C'est tout le point que je veux atteindre. Ma solution fonctionne bien, comme je l'ai dit. Le seul problème est au moment de la conception, le développeur a besoin d'obtenir des commentaires. (ce qui ne fonctionne pas pour RowDefinitions dans mon exemple ... ColumnDefinitions fonctionne bien) –

+0

Ma solution fonctionne au moment du design, ce qui n'est pas le cas. Il semble être un problème dans WPF que les lignes ne sont pas initialisées lorsque votre MarkupExtension doit y accéder. Votre capture d'écran ne vous montre pas en utilisant le convertisseur que j'ai recommandé, en vous liant directement à RowDefinition, ce qui ne fonctionnera pas. Assurez-vous d'inclure le convertisseur et créez le IValueConverter. –

+0

Oh mon dieu, j'ai effectivement fait le convertisseur mais j'ai oublié de l'utiliser. Je voulais juste lui donner rapidement un test, puisqu'il n'utilise pas MarkupExtension, je ne le vois pas vraiment comme une solution au problème. Apparemment, cela fonctionne. Alors maintenant, je pense avoir une solution de contournement. Je suis préoccupé par les performances dans ce cas, je préfère ne pas utiliser les liaisons pour plusieurs raisons, mais je suppose qu'il n'y a aucun moyen que cela fonctionne sans Binding? Merci beaucoup pour votre aide, ce n'est pas que je n'ai jamais utilisé de convertisseurs Value (ou multiValue) ... Je me suis juste concentré sur le MarkupExtension :( –

Questions connexes