代码之家  ›  专栏  ›  技术社区  ›  Walt Stoneburner

CommandConverter-有效异常或.NET错误

  •  1
  • Walt Stoneburner  · 技术社区  · 16 年前

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

    我所做的是相当直截了当的,正如 MSDN ICommand 文档:

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

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

    那部分有用。

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

    NET framework立即抛出一个System.NotSupportedException,其中包含上面的异常详细信息。堆栈跟踪显示:


    在System.Windows.Input.CommandConverter.ConvertTo(ITypeDescriptorContext上下文,CultureInfo区域性,对象值,类型destinationType)

    Red Gate's free .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错误,如果是的话,有解决方法吗?

    谢谢你的帮助。

    5 回复  |  直到 16 年前
        1
  •  1
  •   David Nelson    16 年前

    直觉上这感觉不对;不管命令做什么,复制超链接都应该复制文本。但是,您可以通过为命令类实现自己的TypeConverter来解决这个问题( How to Implement a Type Converter

        2
  •  1
  •   Walt Stoneburner    16 年前

    关于ICommand的一个奇妙的描述就在这里 blog entry by SkySigal ,尽管我需要 Google's Cache 因为当时的博客配置问题。不幸的是,本文的结尾讨论了这个问题,但它的措辞有点模棱两可,即ICommand应该是静态的还是非静态的。

    an article on dotnet mania talking about how copying a hyperlink with a custom command will crash 申请书。

    似乎这个bug至少从2007年就出现在.NET中了,问题是代码显式地检查“已知命令”,正如上面的Reflector分析所示。

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

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

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

    在myns命名空间的HyperlinkHelper类中使用一些支持代码作为名为Command的属性。这是聪明的诡计,应该是可耻的没有必要的。

    Eric Burke

        3
  •  1
  •   Henrik    13 年前

    我的头也撞了好一阵子。我本想把这句话作为对沃尔特·斯通伯纳的回答的一个评论,但目前看来,要做到这一点,首先需要更多的要点。

    不管怎样。由于原始解决方案的链接似乎已断开,我在谷歌上做了更多的搜索。我现在运行的是.NETFramework版本4,这个问题似乎还没有解决(!)

    有一个bug问题发布到微软,再加上这是一个浪费的解决办法,我认为这是一个类似的解决方案所描述的沃尔特Stoneburner。您只需在复制粘贴时使用另一个创建的依赖项属性而不是麻烦的“Command”属性,其余的由helper类处理。你可以从这里下载一个zip,按“显示链接”访问它。感谢Bob Bao发布:

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

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

        4
  •  0
  •   Bruno    15 年前

    我只是在一个小的POC中遇到了这个问题,尽管我把它应用到了一个更大的项目中,我设法找到了原因。不知道这是否有帮助,但这里是他的背景和解决方案。

    视图:

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

    VIEWMODEL

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

    适配器(然后由datagrid中的一行表示)。中继命令是一个基本的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.ComponentModel.TypeConverter.GetConvertToException(对象值,类型destinationType) 在System.ComponentModel.TypeConverter.ConvertTo(对象值,类型destinationType) 位于System.Windows.Controls.ContentPresenter.DefaultTemplate.DoDefaultExpansion(TextBlock TextBlock,Object content,ContentPresenter container) (等)

    希望这对你们有帮助!

        5
  •  0
  •   Syed Bashar    14 年前

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

    http://ciintelligence.blogspot.com/2011/11/wpf-copying-hyperlink-with-command.html