1

我的 C#/.NET 应用程序出现异常,内容如下:

“CommandCoverter”无法将“MyNamespace.MyDerivedFromICommandSubclass”转换为“System.String”。

正如MSDN ICommand文档所述,我正在做的事情相当简单:

public class MyDerivedFromICommandSubclass : ICommand
{
  // Implement interface
  ...
}

我有一个带有超链接的FlowDocument。超链接允许有一个命令属性,我将其设置为派生的 ICommand,以便在单击链接时执行我的自定义操作。

那部分有效。

这就是我遇到麻烦的地方:如果我选择超链接并右键单击复制(或按 Control-C)。

.NET 框架立即抛出 System.NotSupportedException 并带有上述异常详细信息。堆栈跟踪显示:

在 System.ComponentModel.TypeConverter.GetConvertToException(对象值,类型 destinationType)
在 System.Windows.Input.CommandConverter.ConvertTo(ITypeDescriptorContext 上下文,CultureInfo 文化,对象值,类型 destinationType)

此时我求助于Red Gate 的免费 .NET Reflector并查看源代码ConvertTo

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
  if (destinationType == null) // We know it isn't, it's System.String
  {
    throw new ArgumentNullException("destinationType"); // We don't see this exception
  }
  if (destinationType == typeof(string)) // It is, so control passes in
  {
    if (value == null) // It isn't, so this condition is skipped
    {
      return string.Empty;  // Confirmed we don't get this return value
    }
    RoutedCommand command = value as RoutedCommand;
    if (((command != null) && (command.OwnerType != null) && IsKnownType(command.OwnerType))
    { // Is a user-defined ICommand a known type? Doubtful. This gets skipped.
      return command.Name;  // Confirmed we don't get this return value
    }
    // It has to fall through then if no return is done!
  }
  throw base.GetConvertToException(value, destinationType); // BOOM!
  // value is my custom ICommand and destinationType is System.String
}

那么问题就变成了,因为所有这些都发生在 .NET 内部,我是不是做错了什么,如果是,怎么办?或者,这是一个 .NET 错误,如果是,是否有解决方法?

谢谢你的帮助。

4

5 回答 5

1

直觉上这感觉不对。无论命令做什么,复制超链接都应该复制文本。但是,您可以通过为您的命令类(如何实现类型转换器)实现自己的 TypeConverter 来解决此问题。让它委托给 CommandConverter,CanConvertTo 除外:从该方法返回 false 以告诉框架您的命令不能转换为字符串(或者也将 CanConvertTo 委托给 CommandConverter,然后从 ConvertTo 返回代表字符串。

于 2010-07-09T18:53:45.237 回答
1

ICommand 的精彩描述位于SkySigal 的此博客条目中,尽管由于当时的博客配置问题,我需要Google 的缓存。不幸的是,解决这个问题的文章结尾在关于 ICommand 应该是静态的还是非静态的措辞上有点模棱两可。

然而,事实证明,有一篇关于 dotnet mania 的文章讨论了使用自定义命令复制超链接如何导致应用程序崩溃。

似乎这个错误至少自 2007 年以来就存在于 .NET 中,问题在于代码明确检查“已知命令”,正如上面的反射器分析所示。

.NET 想要序列化命令及其父对象,这就是问题所在。本文的解决方案涉及创建一个辅助对象,序列化过程会忽略该对象,它与命令执行相同的操作。

<Hyperlink Command="{x:Static myns:MyCommands.CustomCommand1}" .../>

变成

<Hyperlink myns:HyperlinkHelper.Command="{x:Static myns:MyCommands.CustomCommand1}" .../>

在 myns 命名空间的 HyperlinkHelper 类中使用一些支持代码作为名为 Command 的属性。这是聪明的把戏,应该是不必要的。

Eric Burke 致敬,感谢他解决了这个问题。

于 2010-07-16T19:18:01.283 回答
1

也把我的头撞在了这个上面很长一段时间。我会将此添加为对 Walt Stoneburner 答案的评论,但目前似乎需要更多的积分才能做到这一点。

反正。由于原始解决方案的链接似乎被破坏了,我做了更多的谷歌搜索。我目前正在运行 .Net Framework 版本 4,这似乎仍然没有解决(!)

有一个错误问题发布到 Microsoft 以及一个花费时间的解决方法,我认为这是一个类似于 Walt Stoneburner 描述的解决方案。您只需在复制粘贴时使用另一个创建的依赖属性而不是麻烦的“命令”属性,其余部分由助手类处理。您可以从此处下载 zip,按“显示链接”访问它。感谢鲍勃鲍张贴:

http://connect.microsoft.com/VisualStudio/feedback/details/637269/copying-a-command-bound-hyperlink-in-a-flowdocument-throws-an-exception

微软现在似乎已经发布了一个解决方案,它被称为“HyperlinkHelper”。出于某种尴尬的原因,他们似乎选择在一些团队基金会 dll(?)中分发它。您可以在下面的链接中找到它的文档。如果您有幸使用 Team Foundation 服务器,您甚至可以直接使用该类。否则,我建议重用上面提供的解决方案。

http://technet.microsoft.com/en-us/subscriptions/microsoft.teamfoundation.controls.wpf.hyperlinkhelper

于 2013-03-15T15:54:18.667 回答
0

我刚刚在一个小型 POC 中遇到了这个问题,虽然我认为我让它在一个更大的项目中工作并且我设法找到了原因。不知道它是否有帮助,但这里是他的上下文和解决方案。

尝试将命令绑定到 Xceed 数据网格中的 Button 时出现问题,该 itemSource 绑定在我的 viewModel 中公开的集合上。

风景 :

<UserControl x:Class="UnIfied.Module.UI.Client.Screens.Alerts.AlertsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DataGrid="clr-namespace:Xceed.Wpf.DataGrid;assembly=Xceed.Wpf.DataGrid" xmlns:xcdg="clr-namespace:Xceed.Wpf.DataGrid.Views;assembly=Xceed.Wpf.DataGrid" xmlns:ThemePack="clr-namespace:Xceed.Wpf.DataGrid.ThemePack;assembly=Xceed.Wpf.DataGrid.ThemePack.1">
<Grid>
    <DataGrid:DataGridControl  Grid.Column="0" 
                               Name="alertsBlotter" 
                               ItemsSource="{Binding AlertsSource}" 
                               SelectionMode="Single" 
                               NavigationBehavior="RowOnly" 
                               ItemScrollingBehavior="Immediate" ReadOnly="True"
                               AutoCreateColumns="false">
        <DataGrid:DataGridControl.Columns>
            <DataGrid:UnboundColumn FieldName="Acquit" Title="Acquit Alert" ReadOnly="True" ShowInColumnChooser="False">
                <DataGrid:UnboundColumn.CellContentTemplate>
                    <DataTemplate>
                        <Button 
                            DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type DataGrid:DataRow}}}"
                            Content="X" Command="{Binding AcquitAlertCommand}"/>
                    </DataTemplate>
                </DataGrid:UnboundColumn.CellContentTemplate>
            </DataGrid:UnboundColumn>
            <DataGrid:Column FieldName="AlertId" ReadOnly="True" Title="Alert Id" IsMainColumn="True" />
            <DataGrid:Column FieldName="Time" ReadOnly="True" Title="Creation Time" />
            <DataGrid:Column FieldName="AlertStatus" ReadOnly="True" Title="Status" />
            <DataGrid:Column FieldName="RelatedTrade"  ReadOnly="True" Title="CT Id" />
            <DataGrid:Column FieldName="Status" ReadOnly="True" Title="CT Status" />
        </DataGrid:DataGridControl.Columns>

        <DataGrid:DataGridControl.Resources>
            <Style x:Key="{x:Type DataGrid:ScrollTip}" TargetType="DataGrid:ScrollTip">
                <Setter Property="HorizontalAlignment" Value="Center" />
                <Setter Property="VerticalAlignment" Value="Center" />
            </Style>
        </DataGrid:DataGridControl.Resources>
        <DataGrid:DataGridControl.View>
            <xcdg:TableView>
                <xcdg:TableView.Theme>
                    <ThemePack:WMP11Theme />
                </xcdg:TableView.Theme>
            </xcdg:TableView>
        </DataGrid:DataGridControl.View>
    </DataGrid:DataGridControl>
</Grid>

视图模型

class AlertsViewModel : Presenter<IAlerts>
{
    private readonly IAlertsService alertsService;
    public AlertsViewModel(IAlerts view, IAlertsService aService)
        : base(view)
    {
        alertsService = aService;
        view.SetDataContext(this);
    }

    public ObservableCollection<AlertAdapter> AlertsSource
    {
        get { return alertsService.AlertsSource; }
    }
}

适配器(然后将由数据网格中的一行表示)。中继命令是一个基本的 ICommand 实现。

public class AlertAdapter : BindableObject
{
    private readonly RelayCommand acquitAlert;

    public AlertAdapter()
    {
        AlertStatus = AlertStatus.Raised;
        acquitAlert = new RelayCommand(ExecuteAqcuiteAlert);
    }

    public RelayCommand AcquitAlertCommand
    {
        get { return acquitAlert; }
    }

    private void ExecuteAqcuiteAlert(object obj)
    {
        AlertStatus = AlertStatus.Cleared;
    }

    private static readonly PropertyChangedEventArgs AlertStatusPropertyChanged = new PropertyChangedEventArgs("AlertStatus");
    private AlertStatus alertStatus;
    /// <summary>
    /// Gets or sets the AlertStatus
    /// </summary>
    public AlertStatus AlertStatus
    {
        get { return alertStatus; }
        set
        {
            if (AlertStatus != value)
            {
                alertStatus = value;
                RaisePropertyChanged(AlertStatusPropertyChanged);
            }
        }
    }

    private static readonly PropertyChangedEventArgs AlertIdPropertyChanged = new PropertyChangedEventArgs("AlertId");
    private Guid alertId;
    /// <summary>
    /// Gets or sets the AlertId
    /// </summary>
    public Guid AlertId
    {
        get { return alertId; }
        set
        {
            if (AlertId != value)
            {
                alertId = value;
                RaisePropertyChanged(AlertIdPropertyChanged);
            }
        }
    }


    private static readonly PropertyChangedEventArgs StatusPropertyChanged = new PropertyChangedEventArgs("Status");
    private ComponentTradeStatus status;
    /// <summary>
    /// Gets or sets the CtStatus
    /// </summary>
    public ComponentTradeStatus Status
    {
        get { return status; }
        set
        {
            if (Status != value)
            {
                status = value;
                RaisePropertyChanged(StatusPropertyChanged);
            }
        }
    }


    private static readonly PropertyChangedEventArgs RelatedTradePropertyChanged = new PropertyChangedEventArgs("RelatedTrade");
    private Guid relatedTrade;
    /// <summary>
    /// Gets or sets the RelatedTrade
    /// </summary>
    public Guid RelatedTrade
    {
        get { return relatedTrade; }
        set
        {
            if (RelatedTrade != value)
            {
                relatedTrade = value;
                RaisePropertyChanged(RelatedTradePropertyChanged);
            }
        }
    }


    private static readonly PropertyChangedEventArgs TimePropertyChanged = new PropertyChangedEventArgs("Time");
    private DateTime time;
    /// <summary>
    /// Gets or sets the Time
    /// </summary>
    public DateTime Time
    {
        get { return time; }
        set
        {
            if (Time != value)
            {
                time = value;
                RaisePropertyChanged(TimePropertyChanged);
            }
        }
    }
}

这是我的应用程序尝试创建适配器并将其添加到集合中时生成的异常

System.NotSupportedException 未处理 Message="'CommandConverter' 无法将 'UnIfied.Module.UI.Client.Adapters.RelayCommand' 转换为 'System.String'。" Source="System" StackTrace:在 System.ComponentModel.TypeConverter.GetConvertToException(Object value, Type destinationType) at System.Windows.Input.CommandConverter.ConvertTo(ITypeDescriptorContext context, CultureInfo 文化, Object value, Type destinationType) 在 System.ComponentModel。 TypeConverter.ConvertTo(Object value, Type destinationType) at System.Windows.Controls.ContentPresenter.DefaultTemplate.DoDefaultExpansion(TextBlock textBlock, Object content, ContentPresenter container) (ETC.)

该问题是由于我的数据网格被配置为 AutoCreateColumns(即基于适配器的属性)这一事实引起的。只需将此属性切换为 false,然后一切都恢复正常。

希望这对你们有帮助!

- 布鲁诺

于 2010-12-17T13:18:56.693 回答
0

解决此问题的简单方法是使命令与动态资源绑定,以便解析器在尝试将命令转换为字符串时无法解析。

<Hyperlink Command="{DynamicResource NavigationCommand}">Navigate</Hyperlink>

在此处查看此解决方案的详细信息http://ciintelligence.blogspot.com/2011/11/wpf-copying-hyperlink-with-command.html

于 2011-11-03T16:42:20.360 回答