【问题标题】:WPF MarkupExtension and RowDefinition results in NotImplementedExceptionWPF MarkupExtension 和 RowDefinition 导致 NotImplementedException
【发布时间】:2013-10-14 11:28:32
【问题描述】:

场景: 创建一个 MarkupExtension 以将 Grid.Row=”0” 替换为 Grid.Row=”{namespace:ClassExtension GridRowName}”(列相同)

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>

要求:

  • 当 GridRowName(或 columnName)不正确时显示 XAML 错误 用过的
  • 当 GridRowName(或 columnName)正确时不显示 XAML 错误 被使用了
  • 当 Valid ColumnName 用于 Row 声明时(以及 vica verca) 应显示 XAML 错误

问题: Grid.Column 一切正常,但 Grid.Row 总是在设计时抛出“未实现的异常”(grid.row 带有下划线,grid.column 没有)。

行和列都命名正确,但行总是显示错误。 如果我们指定了无效的列名,则该列会显示错误(这是预期的,因此 Grid.Column 可以正常工作!)

如您所见,列可以正常工作,但行不能。 问题出在名为 GridDefinitionExtension 的 MarkupExtension 内部:

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

异常在行:

var definition = referenceExt.ProvideValue(serviceProvider);

查看调用此方法的 DLL 后,我发现此 ProvideValue 方法的主体如下所示:

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

我已经简化了这个 ProvideValue 方法,只显示它在我的场景中实际使用的代码:

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;

显然异常是由 GetFixUpToken 方法抛出的,但原因是 Resolve 方法。 此 Resolve 方法在通过名称查找 ColumnDefinition 时返回一个有效对象,但在对 RowDefinition 执行完全相同的操作时返回 NULL。

GetFixUpToken 抛出的错误是:“NotImplementedException”,这是预期的,因为在查看 IXamlNameResolver 的源代码时(在本例中是类型:XamlNameResolverImpl)

查看这个XamlNameResolverImpl的源码,可以看到“GetFixUpToken”方法为空并抛出NotImplemented异常(看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();
}

但问题是,正如我已经说过的,是 Resolve 调用,它对 columndefinition 工作正常,但对 rowdefinition 失败......:

栏:

行:

此时,我不知道该怎么办了……

源代码(示例项目)位于:http://www.frederikprijck.net/stuff/MarkupExtension.rar

【问题讨论】:

  • 只是一个建议。在您的帖子中使用实际代码(就像您对 xaml 所做的那样),而不是代码的屏幕截图。在 SO 中调整大小时,其中一些屏幕截图非常小。
  • 我使用屏幕截图是因为我想指出所有问题:Xaml 下划线、c# 运行时值……我用代码替换了一些屏幕截图,因为您是对的。我想我开始使用屏幕截图并尽可能忘记使用文本。顺便说一句,我出于这些原因添加了源项目:-)

标签: c# wpf xaml design-time markup-extensions


【解决方案1】:

这是一个解决方案,虽然与您的实施不同,但可以完成工作。

创建一个 IValueConverter 来代替 MarkupExtension,以便与 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();
        }
    }

然后把它放到你的 XAML 中:

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

并像这样实现它:

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

【讨论】:

  • 显然,此解决方案在设计时未验证:prntscr.com/1w31id 如您所见,TitleRow2 不存在。这就是我想要达到的全部目的。正如我所说,我的解决方案运行良好。唯一的问题是在设计时开发人员需要获得反馈。 (在我的示例中,这不适用于 RowDefinitions...ColumnDefinitions 工作正常)
  • 我的解决方案在设计时有效,而您的则不行。当您的 MarkupExtension 需要访问行时,行未初始化似乎是 WPF 中的一个问题。您的屏幕截图没有显示您使用我推荐的转换器,只是直接绑定到 RowDefinition,这是行不通的。确保包含转换器并创建 IValueConverter。
  • 天哪,我实际上制作了转换器但忘记使用它。我只是想快速对其进行测试,因为它没有使用 MarkupExtension 我并不认为它是解决问题的方法。显然它确实有效。所以现在我有一个可行的解决方法。我担心这种情况下的性能,出于几个原因,我更喜欢不使用绑定,但我想没有绑定就无法工作?非常感谢您的帮助,并不是我从未使用过值(或多值)转换器......我只是专注于 MarkupExtension :(
  • 顺便说一句,我还没有将您的答案标记为答案。我更愿意给它一些时间来获得有关实际问题的响应。
  • 当然。我很想知道是否有人对此也有答案,但我怀疑这只是 WPF Grid 初始化的一个怪癖。
猜你喜欢
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-13
  • 1970-01-01
  • 2011-03-04
相关资源
最近更新 更多