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

ICommandの素晴らしい説明は、SkySigalによるこのブログエントリにありますが、当時のブログ構成の問題のためにGoogleのキャッシュが必要でした。残念ながら、この問題に対処している記事の最後は、ICommandを静的にするか非静的にするかについての文言が少しあいまいです。

ただし、カスタムコマンドを使用してハイパーリンクをコピーするとアプリケーションがクラッシュする方法について説明しているdotnetmaniaに関する記事がありました。

このバグは、少なくとも2007年から.NETに存在しているようです。問題は、上記のReflector分析が示したように、コードが「既知のコマンド」を明示的にチェックしていることです。

.NETは、コマンドをその親オブジェクトと一緒にシリアル化する必要があり、そこで問題が発生します。この記事の解決策には、コマンドと同じことを行うシリアル化プロセスによって無視されるヘルパーオブジェクトの作成が含まれます。

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

になります

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

myns名前空間のHyperlinkHelperクラス内にCommandという名前のプロパティとしていくつかのバッキングコードがあります。それは巧妙な策略であり、恥ずべきことに不必要であるべきです。

これを理解してくれたEricBurkeに敬意を表します。

于 2010-07-16T19:18:01.283 に答える
1

直感的に、これは間違っていると感じます。ハイパーリンクをコピーすると、コマンドの動作に関係なく、テキストがコピーされます。ただし、コマンド クラスに独自の TypeConverter を実装することで、この問題を回避できます ( How to Implement a Type Converter を参照)。CanConvertTo を除いて、CommandConverter にデリゲートします。そのメソッドから false を返し、コマンドを文字列に変換できないことをフレームワークに伝えます (または、CanConvertTo を CommandConverter にもデリゲートし、ConvertTo から代表的な文字列を返します)。

于 2010-07-09T18:53:45.237 に答える
1

また、かなり長い間、これに頭をぶつけました。私はこれをウォルト・ストーンバーナーの回答へのコメントとして追加したでしょうが、現在それを行うには最初にもっとポイントが必要なようです.

ともかく。元のソリューションへのリンクが壊れているように見えるので、さらにグーグルで調べました。私は現在 .Net Framework バージョン 4 を実行していますが、これはまだ解決されていないようです (!)

Microsoft に投稿されたバグの問題があり、それに加えて回避策が費やされていますが、Walt Stoneburner が説明したのと同様の解決策だと思います。コピペで厄介な「コマンド」プロパティの代わりに、別の作成された依存関係プロパティを使用するだけで、残りはヘルパー クラスによって処理されます。ここから zip をダウンロードできます。[リンクを表示] をクリックしてアクセスします。投稿してくれたBob Baoに感謝します:

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

この問題を解決する簡単な方法は、コマンドを動的リソースにバインドして、コマンドを文字列に変換しようとしているときにパーサーが解決しないようにすることです。

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

ここでこのソリューションの詳細を確認してくださいhttp://ciintelligence.blogspot.com/2011/11/wpf-copying-hyperlink-with-command.html

于 2011-11-03T16:42:20.360 に答える
0

小さなPOCでこの問題に遭遇しましたが、より大きなプロジェクトで動作するようになり、その理由を見つけることができました。それが役立つかどうかはわかりませんが、ここで彼のコンテキストと解決策を示します。

この問題は、itemSource がビューモデルで公開されているコレクションにバインドされている Xceed データグリッドのボタンにコマンドをバインドしようとしたときに発生しました。

景色 :

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

THE ADAPTER (データグリッドの行で表されます)。Relay コマンドは、基本的な 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);
            }
        }
    }
}

そして、これは私のアプリがアダプターを作成してコレクションに追加しようとするとすぐに生成される例外です

Message="'CommandConverter' は 'UnIfied.Module.UI.Client.Adapters.RelayCommand' を 'System.String' に変換できません。" Source="System" StackTrace: System.ComponentModel.TypeConverter.GetConvertToException (オブジェクト値、型 destinationType) で System.Windows.Input.CommandConverter.ConvertTo (ITypeDescriptorContext コンテキスト、CultureInfo カルチャ、オブジェクト値、型 destinationType) で System.ComponentModel. TypeConverter.ConvertTo(Object value, Type destinationType) at System.Windows.Controls.ContentPresenter.DefaultTemplate.DoDefaultExpansion(TextBlock textBlock, オブジェクト コンテンツ, ContentPresenter コンテナー) (その他)

この問題は、データグリッドが AutoCreateColumns に構成されている (つまり、アダプターのプロパティに基づいている) ために発生しました。このプロパティを false に切り替えるだけで、すべてが再びまっすぐになりました。

これが皆さんの助けになることを願っています!

――ブルーノ

于 2010-12-17T13:18:56.693 に答える