CommandConverter - допустимое исключение или ошибка .NET - PullRequest
1 голос
/ 08 июля 2010

Я получаю исключение в своем приложении на C # / .NET, которое гласит:

«CommandCoverter» не может преобразовать «MyNamespace.MyDerivedFromICommandSubclass» в «System.String».

То, что я делаю, довольно просто, как описано в документации MSDN ICommand :

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

У меня есть FlowDocument с Гиперссылкой . Гиперссылке разрешено иметь свойство Command , которое я установил в свою производную ICommand, чтобы при щелчке по ссылке выполнялось мое настраиваемое действие.

Эта часть работает.

Вот где я попадаю в беду: если я выберу гиперссылку и щелкну правой кнопкой мыши Копировать (или нажму Control-C).

Мгновенно .NET Framework генерирует исключение System.NotSupportedException с подробностями исключения выше. Трассировка стека показывает:

at System.ComponentModel.TypeConverter.GetConvertToException (значение объекта, тип destinationType)
в System.Windows.Input.CommandConverter.ConvertTo (контекст ITypeDescriptorContext, культура CultureInfo, значение объекта, тип destinationType)

В этот момент я прибегнул к бесплатному .NET Reflector от Red Gate и посмотрел исходный код на 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 ]

1 голос
/ 15 марта 2013

Также долго бился об эту голову. Я бы добавил это в качестве комментария к ответу Уолта Стоунбернера, но мне кажется, что сначала нужно больше очков, чтобы сделать это в настоящее время.

В любом случае. Поскольку ссылка на исходное решение, похоже, не работает, я сделал еще один поиск в Google. В настоящее время я использую .Net Framework версии 4, и это все еще кажется очень неразрешенным (!)

Существует проблема с ошибкой, опубликованная в Microsoft, и вместе с тем, что это обходной путь, я думаю, это аналогичное решение, описанное Уолтом Стоунбернером. Вы просто используете другое свойство созданной зависимости вместо хлопотного свойства 'Command' при копировании-вставке, остальное обрабатывается вспомогательным классом. Вы можете скачать почтовый индекс здесь, нажмите «Показать ссылку», чтобы получить к нему доступ. Спасибо Бобу Бао за публикацию:

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

Microsoft, похоже, выпустила решение, которое называется «HyperlinkHelper». По какой-то неловкой причине они, кажется, решили распространять его в какой-то команде dll (?). Документацию по ней можно найти по ссылке ниже. Если вам повезло использовать сервер Team Foundation, вы можете даже использовать класс напрямую. В противном случае я бы порекомендовал повторно использовать решение, представленное выше.

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

1 голос
/ 16 июля 2010

Фантастическое описание ICommand содержится в этой записи в блоге SkySigal , хотя мне понадобился Кэш Google из-за проблем с настройкой блога в то время.К сожалению, конец статьи, где рассматривается эта проблема, несколько двусмысленен в формулировке о том, должна ли ICommand быть статической или нестатической.

Оказывается, однако, была статья оdotnet mania говорит о том, как копирование гиперссылки с помощью пользовательской команды приведет к сбою приложения.

Похоже, что эта ошибка существует в .NET по крайней мере с 2007 года, и проблема заключается в том, что код явно проверяет наличие«известные команды», как показал вышеупомянутый анализ Reflector.

.NET хочет сериализовать команду вместе с ее родительским объектом, и именно здесь возникает проблема. Решение статьи включает создание вспомогательного объекта, которыйигнорируется процессом сериализации, который делает то же самое, что и команда.

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

становится

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

с некоторым вспомогательным кодом внутри класса HyperlinkHelper пространства имен myns в качестве свойства с именем Command,Это хитрая хитрость, и она должна быть постыдно ненужной.

Снимаю шляпу перед Эриком Бёрком , чтобы выяснить это.

1 голос
/ 09 июля 2010

Интуитивно это кажется неправильным; Копирование гиперссылки должно копировать текст независимо от того, что делает команда. Однако вы можете обойти эту проблему, реализовав свой собственный TypeConverter для своего командного класса ( Как реализовать преобразователь типов ). Пусть он делегирует CommandConverter, за исключением CanConvertTo: верните false из этого метода, чтобы сообщить платформе, что ваша команда не может быть преобразована в строку (или также делегируйте CanConvertTo в CommandConverter, а затем верните представительную строку из ConvertTo.

0 голосов
/ 03 ноября 2011

Простой способ решить эту проблему - связать команду с динамическим ресурсом, чтобы синтаксический анализатор не разрешался при попытке преобразовать команду в строку.

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

Проверьте подробности этого решения здесь http://ciintelligence.blogspot.com/2011/11/wpf-copying-hyperlink-with-command.html

0 голосов
/ 17 декабря 2010

Я только что столкнулся с этой проблемой, хотя в небольшом POC, хотя я заставил его работать в более крупном проекте, и мне удалось выяснить, почему.Не знаю, может ли это помочь, но здесь его контекст и решение.

Проблема возникла при попытке привязать команду к кнопке в сетке данных Xceed, которой itemSource был привязан для коллекции, представленной вmy 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 не обрабатывалосьСообщение = "'CommandConverter' не может преобразовать 'UnIfied.Module.UI.Client.Adapters.RelayCommand' в 'System.String'."Source = "System" StackTrace: at System.ComponentModel.TypeConverter.GetConvertToException (значение объекта, тип destinationType) в System.Windows.Input.CommandConverter.ConvertTo (контекст ITypeDescriptorContext, культура CultureInfo, значение объекта, тип destinationTypeM).TypeConverter.ConvertTo (значение объекта, тип destinationType) в System.Windows.Controls.ContentPresenter.DefaultTemplate.DoDefaultExpansion (TextBlock textBlock, содержимое объекта, контейнер ContentPresenter) (ETC.)

Проблемабыло вызвано тем, что моя сетка данных была настроена на AutoCreateColumns (то есть на основе свойств адаптера).Просто переключили это свойство на false, а затем все снова пошло прямо.

Надеюсь, это поможет вам, ребята!

- Бруно

...