WPF デザイナ読み込みエラーのトラブルシューティング

更新 : 2007 年 11 月

Windows Presentation Foundation (WPF) Designer for Visual Studio には、XAML を描画する洗練された拡張可能なビジュアル デザイナが含まれています。XAML ファイルがデザイナに読み込まれない場合に、問題の原因を調べるために実行できる手順がいくつかあります。ここでは、WPF デザイナ の読み込みエラーをトラブルシューティングするために役立つヒントとテクニックについて説明します。

メモ :

ここで説明するテクニックの多くは、Expression Blend にも適用されます。

トラブルシューティングの手順

次の手順は、WPF デザイナ の読み込みエラーをトラブルシューティングするために役立ちます。

  1. 出力された例外メッセージに目を通します。

    言うまでもないことですが、例外が発生したときにはメッセージをよく読んでください。問題のすばやい診断に役立つことがあります。詳細については、「WPF デザイナでのエラーのデバッグと解釈」を参照してください。

  2. 問題が実装内にあるかどうかを確認します。

    アプリケーションをビルドおよび実行して、問題が実装だけに起因しているのか、WPF デザイナ との対話に起因しているのかを特定します。アプリケーションをビルドおよび実行すると、実装が原因でデザイン時エラーが発生する可能性があります。

  3. Visual Studio デバッガを使用して、デザイン時にコードをステップ実行します。詳細については、「チュートリアル : デザイン時の WPF カスタム コントロールのデバッグ」を参照してください。

  4. 問題が読み込みエラーなのかどうかを確認します。

    デザイン ビューで例外が発生して読み込みに失敗した場合、問題は読み込みエラーである可能性が高くなります。デザイン時に読み込まれるカスタム コードがあり、例外または読み込みエラーがデザイン時に発生する場合は、このトピックの「デザイン時のコードの作成」を参照してください。リソースの操作中にそのリソースが読み込まれないようである場合は、このトピックの「デザイン時の UserControl とカスタム コントロール リソース」を参照してください。

  5. デザイン時に読み込まれるコードを校閲します。

    デザイン時にも実行されるコードを作成する方法には 2 つあります。1 つは、クラスへの入力パラメータをチェックすることにより、防御されたコードを作成する方法です。もう 1 つは、GetIsInDesignMode メソッドを呼び出して、デザイン モードがアクティブかどうかをチェックする方法です。詳細については、このトピックの「デザイン時のコードの作成」を参照してください。

  6. コードの他の領域を校閲します。

    WPF デザイナ を操作するときのプログラミング上のヒントについては、このトピックの「プログラミングのヒント」を参照してください。堅牢なコードを作成するテクニックについては、このトピックの「プログラミングのベスト プラクティス」を参照してください。

  7. 問題がまだ解決されない場合は、MSDN の WPF Designer フォーラム を利用して、WPF デザイナ を使用する他の開発者と連絡をとることができます。懸念される問題点や提案がある場合は、Visual Studio and .NET Framework Feedback サイトへ投稿してください。

デザイン時のコードの作成

コードがデザイン時および実行時に動作することを確認します。コードをデザイン時に実行する場合、Application.Current が常にアプリケーションであるとは限りません。たとえば、Expression Blend の使用時は、Current は Expression Blend です。デザイン時には、MainWindow はアプリケーションのメイン ウィンドウではありません。デザイン時にカスタム コントロールでエラーを発生させる一般的な操作を次に示します。

デザイン時のコードを作成する方法には 2 つあります。1 つは、値コンバータなどのクラスへの入力パラメータをチェックすることにより、防御されたコードを作成する方法です。もう 1 つは、GetIsInDesignMode メソッドを呼び出して、デザイン モードがアクティブかどうかをチェックする方法です。

実装の入力パラメータをチェックすることが必要な理由は、デザイン環境によって実行時環境とは異なる種類の入力が提供されるためです。

一般にスタイル セレクタと値コンバータは、これらの方法のいずれかでデザイン時に正常に動作する必要があります。

値コンバータ

カスタムの IValueConverter 実装では、Convert メソッドの最初のパラメータをチェックして、null または予期される型かどうかを確認する必要があります。次の XAML は、値コンバータが正しく実装されていない場合にデザイン時にエラーになる Application.Current へのバインディングを示しています。

<ComboBox.IsEnabled>
    <MultiBinding Converter="{StaticResource specialFeaturesConverter}">
        <Binding Path="CurrentUser.Rating" Source="{x:Static Application.Current}"/>
        <Binding Path="CurrentUser.MemberSince" Source="{x:Static Application.Current}"/>
    </MultiBinding>
</ComboBox.IsEnabled>

Application.Current がアプリケーションではなくデザイナ アプリケーションを参照するため、このバインディングはデザイン時に例外を発生させます。この例外の発生を回避するには、値コンバータで入力パラメータをチェックするか、デザイン モードになっているかどうかをチェックする必要があります。

2 つの入力パラメータが特定のビジネス ロジックを満たす場合に true を返す値コンバータで入力パラメータをチェックする方法を次のコード例に示します。

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    // Check the values array for correct parameters.
    // Designers may send null or unexpected values.
    if (values == null || values.Length < 2) return false;
    if (!(values[0] is int)) return false;
    if (!(values[1] is DateTime)) return false;

    int rating = (int)values[0];
    DateTime date = (DateTime)values[1];

    // If the user has a good rating (10+) and has been a member for 
    // more than a year, special features are available.
    if((rating >= 10) && 
        (date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
    {
        return true;
    }
    return false;
}

2 番目の方法は、デザイン モードがアクティブかどうかをチェックするデザイン時のコードを作成することです。前に示したパラメータのチェックの代わりにデザイン モードのチェックを行う方法を次のコード例に示します。

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    // Check for design mode. 
    if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) 
    {
        return false;
    }

    int rating = (int)values[0];
    DateTime date = (DateTime)values[1];

    // If the user has a good rating (10+) and has been a member for 
    // more than a year, special features are available.
    if((rating >= 10) && 
        (date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
    {
        return true;
    }
    return false;
}

スタイル セレクタ

カスタムのスタイル セレクタもデザイン モードで動作するように実装する必要があります。次の XAML は、DataTemplate として返されるリソースを特定するために、Application.MainWindow を実行時に使用するカスタムのテンプレート セレクタを示しています。このリソースをデザイン時に使用できない可能性があるため、SelectTemplate オーバーライドはデザイン時に null を返します。

<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
<ListBox Width="400" Margin="10"
    ItemsSource="{Binding Source={StaticResource myTodoList}}"
    ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
    HorizontalContentAlignment="Stretch" 
    IsSynchronizedWithCurrentItem="True"/>

スタイル セレクタの実装例を次のコードに示します。

public class TaskListDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(
        object item, 
        DependencyObject container)
    {
        if (item != null && item is Task)
        {
            Task taskitem = item as Task;
            Window window = Application.Current.MainWindow;

            // To run in design mode, either test for the correct window class
            // or test for design mode.
            if (window.GetType() == typeof(Window1))
            // Or check for design mode: 
            //if (!DesignerProperties.GetIsInDesignMode(window))
            {
                if (taskitem.Priority == 1)
                return window.FindResource("importantTaskTemplate") as DataTemplate;
                else
                return window.FindResource("myTaskTemplate") as DataTemplate;
            }
        }
        return null;
    }
}

デザイン時の UserControl とカスタム コントロール リソース

既定で、実行時に使用できる UserControl やカスタム コントロール リソースがデザイン時に使用できない可能性があります。カスタム コントロールとユーザー コントロールをデザイン画面上の Page または Window に追加すると、コントロールのインスタンスが作成されます。App.xaml 内のリソースは、ページまたはウィンドウに読み込まれた UserControl やカスタム コントロール インスタンスでは使用できません。

リソースをデザイン時に使用するには、リソースを単独のリソース ディクショナリに組み込み、このディクショナリを App.xaml とコントロールの XAML に含めます。すべての StaticResource 参照を DynamicResource 参照に変更します。リソースをデザイン時に使用可能にするためにリソース ディクショナリを共有する方法のコード例を次に示します。

UserControl1.xaml

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

App.xaml

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Pack URI 構文

アプリケーション相対リソース参照は使用しないでください。以下のコード例に示されるアプリケーションに基づく構文は、デザイン時にエラーを発生させる可能性があります。

<Image Name="image1" Source="pack://application:,,,/Image1.bmp" />

このような参照は、DLL ではなくアプリケーションに対して相対的です。DLL 内でアプリケーション相対リソース参照を使用すると、DLL は親アプリケーション内のリソースに依存するようになります。この手法は脆弱で、デザイン時に確実に動作するとは限りません。

アプリケーション相対リソース参照を使用する代わりに、DLL 自体にリソースを追加して、コンポーネントに基づくリソース参照を使用してください。詳細については、「Windows Presentation Foundation におけるパッケージの URI」を参照してください。

以下のコード例に示されるコンポーネントに基づく構文は、推奨される手法です。

<Image Name="image1" Source="/TestHostApp;component/Image1.bmp" />
<Image Name="image1" Source="pack://application:,,,/TestHostApp;component/Image1

プログラミングのヒント

WPF デザイナ を操作するときのプログラミングのヒントを次に示します。

プログラミングのベスト プラクティス

ここでは、WPF デザイナ 用の堅牢なコードを作成するためのプログラミングのベスト プラクティスをいくつか示します。

  • 編集スコープは、using ステートメントまたは try/finally ブロックで常にラップします。例外が発生すると、Dispose 呼び出し内で変更が中止されます。詳細については、「ModelEditingScope」を参照してください。

  • 1 つのコンテナから別のコンテナにコントロールを移動するには ModelEditingScope を使用します。これを行わない場合、例外が発生します。

  • WPF および WPF デザイナ では、プロパティ値をクリアする目的で既定値に設定しないでください。Height などに NaN 値を設定する場合は、NaN を代入するのではなく ClearValue メソッドを呼び出します。

  • プロパティの値を取得するには、プロパティの計算値を使用します。つまり、ModelItemGetCurrentValue メソッドではなく ComputedValue プロパティを使用する必要があります。GetCurrentValue メソッドは、バインディングやその他の式が XAML に保存された場合にこれらを返すため、キャスト例外が発生することがあります。

参照

その他の技術情報

WPF デザイナでのエラーのデバッグと解釈

XAML とコードのチュートリアル

基本的な機能拡張という概念

WPF デザイナの機能拡張について