WPF MarkupExtension and RowDefinition lead to NotImplementedException

Scenario: Create MarkupExtension to replace Grid.Row = "0" with Grid.Row = "{namespace: ClassExtension GridRowName}" (same for column)

Xaml code:

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

Demand:

  • Show XAML errors when GridRowName (or columnName) is used
  • Show XAML errors when the correct GridRowName (or columnName) is used
  • If the Valid ColumnName (and vica verca) value is used for the Row declaration, a XAML error should be displayed

Problem: Everything works fine for Grid.Column, but Grid.Row always throws an "Not implemented exception" at design time (grid.row is underlined, grid.column is not). enter image description here

Rows and columns are called correct, but a row always shows an error. If we specify an invalid column name, the column shows an error (which is expected, so Grid.Column is working fine!) enter image description here

As you can see, the column works fine, but the rows are not used. The problem is a MarkupExtension called 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(); } } 

The exception occurs on the line:

 var definition = referenceExt.ProvideValue(serviceProvider); 

After looking inside the DLL from which this method is called, I found that the body of this ProvideValue method looks like this:

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

Ive simplified this ProvideValue method only to display the code that it actually uses in my script:

 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; 

Obviously, the exception is thrown by the GetFixUpToken method, but the reason is the Resolve method. This Resolve method returns a valid object when searched by ColumnDefinition by its name, but it returns NULL when it does the same for RowDefinition.

The error caused by GetFixUpToken: "NotImplementedException", which is expected when you look at the source code of IXamlNameResolver (which in this case is of type: XamlNameResolverImpl)

enter image description here

When viewing the source code of this XamlNameResolverImpl, you can see that the GetFixUpToken method is empty and throws a NotImplemented exception (see 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(); } 

But the problem is that, as I said, this is a Resolve call that works fine for defining columns, but doesn't work for rowdefinitions ...:

Speaker: enter image description here

Row: enter image description here

At this moment I do not know what to do next ...

The source code (example project) is available at: http://www.frederikprijck.net/stuff/MarkupExtension.rar

+7
c # wpf xaml design-time markup-extensions
source share
1 answer

Here is a solution that, unlike your implementation, does its job.

Instead of MarkupExtension, create an IValueConverter to use with binding:

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

Then put this in your XAML:

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

And implement it as follows:

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

All Articles