Service Manager オーサリング ツールを使用したフォームのカスタマイズと作成の概要
フォームは、ユーザーとデータベースからのオブジェクトの対話を可能にするウィンドウです。 ユーザーは、フォームを使用してオブジェクトのプロパティを表示して編集できます。 各フォームは、特定のクラスに関連付けられていて、対象クラスのインスタンスのみの情報を表示します。 フォームには複数のフィールドが含まれています。 通常、各フィールドはフォームのターゲット クラスの特定のプロパティにバインドされます。 たとえば、インシデント フォームは、インシデントのオブジェクトに関連付けられます。 そのため、インシデント フォームには、データベース内のインシデント オブジェクトに関する情報が表示されます。
Service Manager フォームは、Microsoft .NET Framework アセンブリでの Windows Presentation Foundation (WPF) フォームの実装と、Service Manager 管理パック内のフォーム定義で構成されます。 フォーム定義により、フォームが表すクラスと、その他のフォームのプロパティが指定されます。
フォームに関する主要な概念
フォームをカスタマイズする前に、以下のフォームに関する概念を把握しておく必要があります。
フォームの使用
フォーム定義を含む管理パックが Service Manager にインポートされると、フォーム定義がデータベースに格納されます。 後で、ユーザーがオブジェクトの表示を必要とする Service Manager コンソール タスクを開始すると、Service Manager は要求されたオブジェクトを表示するフォームを見つける必要があります。 Service Manager はデータベースにアクセスし、そのオブジェクトに対して定義されているフォームを検索します。 オブジェクトに対してフォームが定義されていない場合、Service Manager はオブジェクトの親オブジェクトに対して定義されているフォームを検索します。 Service Manager は、定義されたフォームが見つかるまで、オブジェクトの継承階層全体を検索し続けます。
汎用フォーム
Service Manager でオブジェクトまたはその親オブジェクトのフォームが見つからない場合、Service Manager は、そのオブジェクトの既定の ジェネリック フォーム を動的にビルドします。 汎用フォームは、システム生成フォームで、簡単なフォームの用途には十分です。 汎用フォームは、フォーム定義のないオブジェクト用にフォームをすばやく簡単に作成する方法です。
既定では、汎用フォームには、変更できない単純なレイアウトでフォームのすべてのプロパティが表示されます。 汎用フォームには、フォームの継承階層内のすべての親オブジェクトのプロパティが表示され、その動作を変更することはできません。 汎用フォームのカスタマイズには制限があります。 たとえば、汎用フォームに表示するプロパティを指定できます。ただし、汎用フォームをカスタマイズの基礎として使用することはできません。 そのオブジェクトのカスタム フォームを後で定義すると、カスタム フォームによってオブジェクトの汎用フォームが上書きされます。
汎用フォームのプロパティを非表示にする方法と、汎用フォームのその他のカスタマイズ方法については、ブログ記事「 Overview of the Forms Infrastructure and the Generic Form (フォームのインフラストラクチャと汎用フォームの概要)」を参照してください。
フォーム内のクラスの組み合わせ
場合によっては、複数のクラスから取得された情報を表示するフォームが必要となることがあります。 そのためには、 複合クラス を作成し、フォームの 1 つのフィールドをその複合クラスにバインドします。 組み合わせクラスの詳細については、「 Changes to the System Center Common Schema」を参照してください。
フォームの機能面
フォームには、以下の機能面があります。
初期化
サイズと場所
最新のステータスに更新
変更の送信
各機能面について以下のセクションで説明します。
初期化
初期化中に、フォームの拡張アプリケーション マークアップ言語 (XAML) が解析され、フォーム上のすべてのコントロールがインスタンス化されて読み込まれます。 フォームの Loaded イベントは、フォームと含まれるすべての要素が読み込まれた日時を示します。 データの読み込み操作は非同期です。 そのため、 Loaded イベントの発生時にターゲット インスタンスが使用できない場合があります。 代わりに、フォームのターゲット イベントの設定時の通知には DataContextChanged イベントを使用しなければなりません。 PropertyChanged プロパティの DataContext イベントを、 DataContextChanged イベントの代わりに使用できます。
コントロール関連のカスタムの初期化には Loaded イベントを使用し、その後、ターゲット インスタンス関連のカスタムの初期化の DataContextChanged プロパティには、 PropertyChanged または DataContext イベントを使用することを推奨します。
サイズと場所
フォームがポップアップ ウィンドウに表示される場合、その初期サイズは、フォームの Width、 Height、 MinWidth、および MinHeight プロパティに基づいて決定されます。 フォームにこれらのプロパティが設定されていない場合、フォームの初期サイズは、その内容に基づいて計算されます。
これらのプロパティは、次のように設定することが推奨されます。
フォームの理想的なサイズを明示的に指定するには、フォームの Width プロパティと Height プロパティを指定します。 これらのプロパティを Auto 値に設定することも可能です。 その場合、フォームの幅と高さがコンテンツのサイズを基に設定されます。
フォームに許容される最小サイズのウィンドウを指定するには、フォームの MinWidth プロパティと MinHeight プロパティを指定します。 ユーザーがウィンドウを指定サイズより小さいサイズに変更すると、フォームの非表示コンテンツにスクロールできるようにスクロールバーが表示されます。
フォームが Service Manager フォーム ホスト内でホストされている場合、同じ実行セッション内で同じユーザーがそのフォームを後で表示するために、最後に使用されたサイズと場所が保持されます。
最新のステータスに更新
フォームのターゲット インスタンスは、フォームに対して Refresh コマンドを実行した結果、変更される可能性があります。 このコマンドのハンドラーが、データベースから新しいデータをフェッチします。 データが到着すると、フォームの DataContext プロパティ値が新しいターゲット インスタンスに設定され、 DataContextChanged イベントが発生します。
フォームが最初に読み込まれた時に発生した DataContextChanged イベントと、 Refresh コマンドを処理するために発生したイベントを区別するには、イベントと共に渡されるイベント引数の OldValue プロパティを確認します。 フォームが初期化されたばかりの場合はこのプロパティは Null です。
変更の送信
Service Manager のフォーム ホスト ポップアップ ウィンドウには、フォームで行われた変更を送信したり、ポップアップ ウィンドウを閉じたりするためのボタンが用意されています。
ユーザーがフォームの Apply ボタンを選択すると、フォームのターゲット インスタンスがストレージ用に送信されます。 この操作は同期です。そのため、ユーザーは送信操作が完了するまでフォームを編集できません。 フォーム送信中にエラーがあった場合は、エラー メッセージが表示されます。 フォームは開いたままになり、さらに変更を加えることができます。 複数ユーザーによりフォームの別のインスタンスが同時に編集されていることもあるため、競合を防止するために、頻繁に変更を適用することが推奨されます。
ユーザーが [ OK ] ボタンを選択した場合、動作は Apply に似ていますが、フォーム送信操作が成功した場合、フォームとそのホスト ウィンドウは閉じられます。
ユーザーが Cancel ボタンを選択すると、操作の確認を求めるダイアログが表示されます。 ユーザーは Yes を選択して変更を失うか、 No を選択してフォームに戻ることができます。
フォームの一般的なガイドラインとベスト プラクティス
フォームを追加または変更することで、Service Manager の機能を拡張できます。 このセクションでは、さまざまなツールとスクリプト フォーム定義を直接使用して、Service Manager フォームを作成および使用するためのベスト プラクティスの推奨事項について説明します。
このセクションは、主に、Windows Presentation Foundation (WPF) と Microsoft Visual Studio Team System または Microsoft Expression Blend を使用して独自のカスタム フォームを構築した経験があるパートナーや顧客を対象としています。
新しいフォームの作成についての一般的なガイドラインは、次のとおりです。
- 標準コントロールを使用する
- 一般的なフォーム デザイン ガイドラインに従う
- 分離コードを避ける
- 例外処理を含める
- フォームのカスタマイズとアップグレードを配慮する
- すべてのカスタマイズ可能なコントロールに名前を付ける
- フォームをデータ ソースにバインドする
- Service Manager フォームのインフラストラクチャ検証規則、値コンバーター、およびエラー テンプレートを使用します。
- フォーム インフラストラクチャ コマンドとイベントを使用する
これらのガイドラインについては、次のセクションを参照してください。
標準コントロールを使用する
次のコントロールをフォーム上で使用できます。
- 標準コントロール。 これには、コンボ ボックスやリスト ボックスなどの .NET ライブラリ コントロールが含まれます。
- カスタム コントロール。 これには、フォームの作成者やサード パーティが作成する追加コントロールが含まれます。
ヒント
標準コントロールの使用が可能であれば、カスタム コントロールの作成は避け、フォームのユーザー エクスペリエンスの一貫性を高めるようにしてください。 カスタム コントロールの作成が必要な場合は、コントロールの表示を定義するコントロール テンプレートを使用して、視覚的な外観と動作と論理的な動作を分けます。 できれば、各 Windows テーマ用にコントロール テンプレートを分けてください。
フォームの設計に関する一般的なガイドラインに従う
フォームをデザインする際、公開されているデザイン ガイドラインを使用して、フォームがユーザーにとって使いやすく、ユーザーとの対話機能の一般的な枠組みに従っていることを確認します。
一般的な Windows デザインの詳細については、「 Windows ユーザー エクスペリエンス ガイドライン」を参照してください。
さらに:
- 情報を複数のタブに分けて、フォームをより単純に、より読みやすくします。 最初のタブで最もよく使用される情報と、後続のタブの重要度が低い情報を含めます。
- レイアウト パネルを使用して、フォーム上のコントロールを配置します。 これにより、フォームのサイズ変更とローカライズ時に正しく動作することが保証されます。
- 個々のコントロールに視覚的なプロパティを設定するのを避け、代わりにスタイルを使用してください。 これにより、スタイルを変更することで、一連のフォーム全体のすべてのコントロールの外観を変更でき、関連するフォーム間で一貫した外観が促進されます。
分離コードを回避する
分離コード とは、XAML ページがマークアップ コンパイルされる際に、マークアップ定義オブジェクトによって結合されるコードを表す用語です。 フォームでの分離コードの使用は、できる限り制限してください。 後でそのコードを変更する方が簡単なので、フォームのコードをコントロール自体に埋め込むことをお勧めします。 代わりに、Service Manager フォーム インフラストラクチャでサポートされている宣言型機能を使用して、フォームで値の変換と検証規則を定義します。
一般的なガイドラインとして、コードビハインドの使用は、WPF とフォーム インフラストラクチャ ライブラリで定義されたクラスを使用して、XAML の宣言型機能を使用して必要な機能を提供できない状況に限定する必要があります。 そのような場合でも、分離コードで実装されている機能をヘルパー ライブラリに移動して、XAML から参照することを検討してください。
例外処理を含める
フォーム内のコードに例外処理が含まれていることを確認します。これにより、作成時に作成ツールのデザイン フェーズ中と Service Manager コンソールの両方でフォームを読み込むことができます。
フォームのカスタマイズとアップグレードを検討する
新しいフォームを設計するときは、そのフォームへの今後のカスタマイズとアップグレードを検討する必要があります。 カスタマイズを維持しながらフォームをカスタマイズおよびアップグレードできるようにするには、このセクションで前述したガイドラインとヒントに従い、次のガイドラインに従ってください。
フォームの設計中は、将来のカスタマイズとアップグレードを早い段階で検討してください。 フォームは将来のバージョンで進化する可能性が高く、ユーザーが元のフォームにカスタマイズを維持しながら、フォームの新しいバージョンにアップグレードする方法を検討することが重要です。 たとえば、元のフォームのカスタマイズにユーザーが多くの労力を注いだ後に、更新されたバージョンを提供する可能性があります。 ユーザーは、バージョンがアップグレードされてもカスタマイズをそのまま使用できることを期待します。
フォームの各コントロールに一意の名前を付け、コントロールをカスタマイズできるようにしてください。 フォームのカスタマイズは、特定のコントロール、あるいは特定のコントロールのセットをターゲットにする操作のセットとして保存されます。 ターゲット コントロールは名前によって参照されるため、フォームのバージョン間でコントロール名を保持することが重要です。 コントロールに名前がない場合、フォーム カスタマイズ エディターによって名前が生成されますが、生成された名前はフォームの異なるバージョン間で保持されません。
フォームの異なるバージョン間でコントロール名が不変であることを確認します。 これによって、前のバージョンの特定のコントロールに対するカスタマイズが、フォームの新しいバージョンの同じコントロールに対しても確実に適用できます。
可能であれば、フォームをアップグレードする際、コントロールを同じタブの別の場所に移動することは避けてください。 一般的なユーザーのカスタマイズに、フォームのコントロールを別の場所に移動することがあります。 フォームの新しいバージョンでコントロールの場所を変更すると、新しいコントロールの場所が、ユーザーが再配置したコントロールと重複する可能性があります。
可能であれば、既存のフォームへの更新を設計するときに、タブ間でコントロールを移動しないようにします。 コントロールは、名前と、コントロールが配置されているタブの両方で識別されます。 新しいバージョンのフォームでコントロールをあるタブから別のタブに移動すると、カスタマイズされた部分でターゲット コントロールを識別できなくなり、ユーザーがコントロールに対して行ったカスタマイズが壊れる可能性があります。
フォームへの更新に新しいコントロールが含まれている場合は、新しいコントロールを新しいタブに追加することを検討してください。これは、既存のタブとコントロールに対するユーザーのカスタマイズを妨げることを回避する最も安全な方法です。
コントロールのバインド方法に注意してください。 読み取り専用のコントロールは、一方向バインドのみ使用します。
すべてのカスタマイズ可能なコントロールに名前を付けます
コントロールがバインドされているデータ、あるいはコントロールが何をするかが、コントロール名でわかるようにしてください。
フォームをデータ ソースにバインドする
フォームの主な目的は、Service Manager データベースから 1 つのオブジェクトを視覚化することです。 このオブジェクトとは「 target instance」と呼ばれ、フォームの DataContext プロパティ ( FrameworkElement クラスから継承) により常に指定されます。
重要
フォームの DataContext プロパティは変更しないでください。 フォームのホスト環境は、このプロパティを使用してフォームのターゲット インスタンスを識別します。
Service Manager データ モデルでは、ターゲット インスタンスは BindableDataItem オブジェクトとして表されます。 このクラスは、基礎となるソフトウェア開発キット (SDK) オブジェクトを集計して、プロパティ名をパラメーターとして取得するインデクサーを通じてそのプロパティを公開します。
BindableDataItem クラスはまた、 ICustomTypeDescriptorも実装し、WPF バインディングのデータ ソースとして BindableDataItem クラスを使用できるようにします。 次の例では、ターゲット インスタンス プロパティを Text コントロールの TextBox プロパティにバインドしています。
<TextBox Name="textBoxDescription" Text="{Binding Path=Summary}"/>
ターゲット インスタンスはフォームの DataContext として設定され、フォーム上のすべてのコントロールの既定の Source として機能するため、バインドのSourceを指定する必要はありません。
フォーム上のコントロールはターゲット インスタンス以外のデータ ソースにバインドでき、フォーム インフラストラクチャ ライブラリには、バインディングを暗黙的に実行する多くのコントロールが含まれています。 たとえば、インスタンス ピッカー コントロールは、選択するインスタンスのコレクションを提供するデータソースにバインドされます。 ObjectDataProvider および XmlDataProvider クラスを使用して、宣言によって追加のデータ ソースを定義することもできます。
フォーム インフラストラクチャは、ターゲット インスタンスを、フォームの唯一の読み取り/書き込みデータ ソースとして扱います。 そのため、 Submit コマンドの実装は、ターゲット インスタンスに行われた変更のみを保存します。 フォームの他のデータ ソースは、読み取り専用として扱われます。
Service Manager フォームのインフラストラクチャ検証ルール、値コンバーター、およびエラー テンプレートを使用する
フォームでフォーム インフラストラクチャ検証規則を使用して、無効なデータ入力を指定することをお勧めします。 WPF バインド インフラストラクチャは、一方向または双方向のいずれかでデータ ソースにバインドされるコントロールのプロパティの検証をサポートします。 バインド オブジェクトは、任意の数の ValidationRule オブジェクトを含むことができる ValidationRules コレクションを持ちます。 データがコントロールからデータ ソースへプッシュされると、その値を検証するために ValidationRule オブジェクトが呼び出されます。
フォーム インフラストラクチャ ライブラリには、最も一般的なケースを処理する多くの検証規則が含まれています。 フォーム インフラストラクチャは検証規則を利用して、フォームのコンテンツを送信して格納できるものかどうかを決定します。 たとえば、フォームに検証エラーがあるコントロールがある場合、フォームの Submit ボタンを無効にすることができます。
フォーム インフラストラクチャ ライブラリにあるカスタム エラー テンプレートの使用をお勧めします。 コントロールに検証エラーがあれば、既定ではそのコントロールに赤い枠線が付いて表示されます。 WPF を使用すれば、どのコントロールにも設定できる Validation.ErrorTemplate プロパティを通じて、カスタム エラー表示を定義できます。 Service Manager フォーム インフラストラクチャ ライブラリには、WPF の赤い境界線ではなくエラー アイコンが表示されるカスタム エラー テンプレートが含まれています。 さらに、マウスでエラー アイコンをポイントすると、エラー メッセージがカスタム ヒントに表示されます。 このエラー メッセージは、コントロールのデータが検証できなかった理由を表示します。
次の例は、XAML でエラー テンプレートを参照する方法を示したものです。
<TextBox Text="{Binding SomeProperty}"
scwpf:Validation.ValueRequired="True"
Validation.ErrorTemplate="{DynamicResource {ComponentResourceKey {x:Type scwpf:Validation}, InvalidDataErrorTemplate}}"/>
組み込みの検証規則で必要な検証ロジックが提供されない場合は、そのロジックを表すカスタム検証規則を作成することをお勧めします。 これによって、一般的な検証処理メカニズム内で、標準検証ロジックとカスタム検証ロジックの共存が可能になります。
検証規則のメカニズムが特定のシナリオに適していない場合は、代わりに FormEvents.PreviewSubmitEvent を処理し、そこから検証を実行する必要があります。
次のサンプル コードは、カスタム検証に使用できるパターンの例です。
void MyForm_Loaded(object sender, RoutedEventArgs e)
{
// hook to handle form events
this.AddHandler(
FormEvents.PreviewSubmitEvent,
new EventHandler<PreviewFormCommandEventArgs>(this.OnPreviewSubmit));
}
private void OnPreviewSubmit(object sender, PreviewFormCommandEventArgs e)
{
string errorMessage;
bool result = this.DoVerify(out errorMessage);
if (!result)
{
// cancel Submit operation
e.Cancel = true;
// display error message
MessageBox.Show(errorMessage);
}
}
internal bool DoVerify(out string errorMessage)
{
// Do custom verification and return true to indicate that
// validation check has passed; otherwise return false and
// populate errorMessage argument
}
フォーム インフラストラクチャのコマンドとイベントを使用する
フォーム インフラストラクチャには、フォームで実行できる多くのコマンドが公開されています。 コマンドには、次が含まれます。
FormsCommand.Submitは、フォームのターゲット インスタンスを保存します。
FormsCommand.SubmitAndCloseは、フォームのターゲット インスタンスを保存し、フォームを閉じます。
FormsCommand.Refreshは、フォームのターゲット インスタンスに対してクエリを繰り返します。
FormCommands.Cancel。すべての変更を破棄し、フォームを閉じます。
これらの各コマンドは、コマンド実行の前後で発生するイベントでまとめられます。
コマンドの前に、次のイベントが発生します。
FormEvents.PreviewSubmit コマンドの前に FormCommand.Submit イベントが発生し、 FormEvents.Submitted コマンドの後に FormCommand.Submit イベントが発生します。
FormEvents.PreviewRefresh コマンドの前に FormCommands.Refresh イベントが発生し、 FormCommand.Refreshed コマンドの後に FormCommand.Submit コマンドが発生します。
FormEvents.PreviewCancel コマンドの前に FormCommands.Cancel イベントが発生し、 FormCommand.Canceled コマンドの後に FormCommand.Cancel イベントが発生します。
プレビュー イベントは PreviewFormCommandEventArgs オブジェクトを渡します。 このオブジェクトには、変更可能な Cancel プロパティが含まれます。このプロパティに trueが設定されているときは、対応するコマンドの実行を防ぎます。
コマンド後のイベントは FormCommandExecutedEventArgs オブジェクトを渡します。 このオブジェクトには、コマンドの実行が正常に行われたか、取り消されたか、あるいはエラーが発生したかを示す Result プロパティが含まれます。 エラーの場合、 FormCommandExecutedEventArgs オブジェクトの Error プロパティが、エラーについての情報を提供する例外を参照します。
プログラムと宣言の両方で、フォーム コマンドを有効、無効、および実行できます。
フォーム コマンドをプログラムで有効にするには、フォームと関連コマンドの間に CommandBinding を確立します。
次の例では、フォームと Refresh コマンドの間にコマンドのバインドが確立され、2 つのハンドラーがこのコマンドに定義されます。 最初のハンドラーは Refresh コマンドが実行できるかどうかを返し、2 番目のハンドラーが実際の Refresh コマンドの実装を含んでいます。
public class MyForm : UserControl
{
public MyForm()
{
// do standard initialization
// establish CommandBinding for Refresh command
this.CommandBindings.Add(
new CommandBinding(FormCommands.Refresh, this.ExecuteRefresh, this.CanExecuteRefresh));
}
private void CanExecuteRefresh(
object sender,
CanExecuteRoutedEventArgs e)
{
// put your logic that determines whether Refresh
// can be executed here
bool canExecute = true;
BindableDataItem dataItem = this.DataContext as BindableDataItem;
if (dataItem)
{
canExecute = dataItem["Status"] != "New";
}
e.CanExecute = canExecute;
}
private void ExecuteRefresh(
object sender,
ExecutedRoutedEventArgs e)
{
// here is placeholder for the code that has do be
// executed upon running Refresh command
}
}
フォーム コマンドのハンドラーを宣言で定義することもできます。 これは、 RoutedCommandTrigger を使う Ruleオブジェクトを使用することで可能です。 次のサンプル コードは、ハンドラーを宣言で定義する方法を示したものです。
<scwpf:BusinessLogic.Rules>
<scwpf:RuleCollection>
<scwpf:Rule>
<scwpf:Rule.Triggers>
<scwpf:RoutedCommandTrigger
RoutedCommand="{x:Static scwpf:FormCommands.Refresh}"/>
</scwpf:Rule.Triggers>
<scwpf:Rule.Conditions>
<scwpf:PropertyMatchCondition
Binding="{Binding Status}"
Value="New"
Operation="NotEquals" />
</scwpf:Rule.Conditions>
<!-- Use RuleAction objects to define the logic that executed
upon running Refresh command; this can be left empty -->
</scwpf:Rule>
</scwpf:RuleCollection>
</scwpf:BusinessLogic.Rules>