のアーカイブ

"TypeConverterが"タグ付きの投稿

Silverlightコントロールのプロパティ値エディタ

2009年4月3日 コメントは

はじめ

これは、デザインタイムの実装の ​​変更に関するシリーズの一部であるSilverlightツールキット 2009年3月リリース この記事は、プロパティ値の編集者と型コンバータを使用してSilverlightコントロールのプロパティの編集体験を向上させる方法について説明します。 私はWPF / Silverlightのデザイナの機能拡張フレームワークの全体的なプロパティ編集アーキテクチャを記述すると起動します、その後で例を使用してSilverlightツールキット 2009年3月リリース 、それがどのように行われるか示すために、およびSilverlightのデザインタイム開発のユニークな課題/トリック。

プロパティ編集アーキテクチャ

視覚的にオブジェクトのプロパティを編集すると、デザイナーの重要な部分です。 設計者は通常、良い編集ユーザーインターフェイスを提供するために、はるかに少ないカスタム型のプロパティを(構造体、クラスまたはインタフェース)、レンダリングする方法がわからない。 コントロールの開発者は、通常提供する必要がTypeConverterがPropertyValueEditorレンダリング/カスタムタイプのプロパティの編集UIとXAMLのシリアル化を提供するために、、またはその両方を。

インラインエディタ、拡張エディタ、およびダイアログエディタ、クラスによって実装されている各:デザイナの機能拡張フレームワークは、プロパティ値エディタの3つのタイプを定義しています。

PropertyValueEditor class diagram

これらのエディタの編集のUIのように定義されているDataTemplateを 編集中のプロパティは、エディタにさらされているのDataContextのPropertyValueタイプ。 エディタUIは通常の3つのプロパティのいずれかを経由して編集されて基本的なプロパティにバインドするにPropertyValueStringValue 、またはコレクション

PropertyValueEditor

PropertyValueEditorはによって定義される単一のインラインエディタを保持InlineEditorTemplateのプロパティを。 インラインエディタは、プロパティウィンドウ内に表示されます。 以下の簡単な例ですInlineEditorTemplateプロパティを表示および編集するテキストボックス(TextBox)コントロールを使用しています。

  x:Key ="TextBoxEditor" > <DataTemplate X:キー ="TextBoxEditor">
     Text ="{Binding Path=Value}" /> <TextBox テキスト ="{バインディングパス=値}"/>
 > </ DataTemplateを > 

ExtendedPropertyValueEditor

ExtendedPropertyValueEditorはから継承インラインエディタの2つのエディタがあるPropertyValueEditor 、とで定義されている追加の拡張エディタExtendedEditorTemplateのプロパティを。 拡張エディタは通常、インラインエディタでポップアップ表示される 以下の簡単な例です。インラインエディタはボタンであり、クリックしたときに、それは基本的なプロパティを表示および編集するためのスライダーを使用する拡張エディタをポップアップ表示されます。
  x:Key ="inlineEditor" > <DataTemplate X:キー ="inlineEditor">
     Content ="..." Command ="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}" /> < ボタンの コンテンツ ="..." コマンド ="{x:StaticのPropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}"/>
 > </ DataTemplateを >

     x:Name ="slider" Value ="{Binding Path=Value}" /> < スライダー X:名前 ="スライダー" の値 ="{バインディングパス=値}"/>
 > </ DataTemplateを > 

DialogPropertyValueEditor

DialogPropertyValueEditorは、あまりにも2つのエディタを持っています。から継承されたインラインエディタPropertyValueEditor 、とで定義されている追加のダイアログエディタDialogEditorTemplateのプロパティを。 ダイアログエディタは、通常は経由インラインエディタによって開かれたPropertyValueEditorCommandsShowDialogEditorのコマンド。 下の単純な例は、次のとおりです。

 x:Key ="inlineEditor" > < Button Content ="..." Command ="{x:Static <DataTemplate X:キー ="inlineEditor"> < ボタンの コンテンツ ="..." コマンド ="{x:Staticの  ="Center" HorizontalAlignment ="Right" Margin ="0,0,4,4" /> < TextBox Text ="{Binding 名前:" は、VerticalAlignment ="センターは、右"= HorizontalAlignmentを "" マージン ="0,0,4,4"/> <TextBox テキスト ="{バインディング 

カスタムプロパティエディタを実装する

Silverlightコントロールのカスタムプロパティエディタを実装するには:

  • カスタムプロパティエディタクラスを実装する
  • のようなもの、AddCustomAttributesの呼び出しを介してSilverlightコントロールのプロパティを持つカスタムプロパティエディタを関連付ける

    正しく実装されたプロパティの値のエディタには、次の要件を満たしている必要があります。

  • プロパティ値エディタはインラインエディタと拡張エディタ部は独立して使用できるように設計されている必要があります。
  • プロパティ値エディタは状態を格納してはいけません。 プロパティ値の編集者はステートレスである、ホスト実装によってキャッシュされるかもしれない、と複数のプロパティ値で再使用することができます。
  • プロパティ値エディタは、1つの値エディタ部(ビュー/インライン/拡張)は、コントロールが所定の時間にアクティブであると仮定してはいけません。 たとえば、ダイアログボックスは表示部、インライン部、および同時にアクティブな拡張UIの部分を持つことができます。
  • プロパティ値エディタの一部として実装さコントロールは、状態を格納してはいけません。 値エディタの一部として実装さコントロールは、それが1つのプロパティ値にバインドされると想定しないでください。 コントロールは、異なるプロパティ値を変更するためにリサイクルされることがあります。 データモデルが更新されている場合にキャッシュされる情報がフラッシュされるべきである。
  • プロパティ値エディタの一部として実装さコントロールは、ホストまたは親コントロールについての仮定をしてはいけません。 使用すべき唯一の通信メカニズムはPropertyValueはの方法により、データモデル、 DataContextは 、コマンドの標準セット。

    WPFとSilverlightのアセンブリの両方を参照

    設計アセンブリは.NET / WPFアセンブリは、Visual Studioまたはブレンドによってロードされますが、それは通常のSilverlightのアセンブリを(少なくともそれがために、設計時の機能を提供Silverlightコントロールのアセンブリ)を参照する必要がありますされています。 これは設計のアセンブリプロジェクトの参照のあいまいさを作成することができます:それは時々のSilverlightのWPFとSystem.Windows.dllの両方PresentationFramework.dllでSystem.Windows.FrameworkElementを言って、WPFとSilverlightの両方に同じ完全修飾型名を参照する必要があります。 Visual Studioでの混乱を回避するには、使用することができますexternエイリアスを WPFとSilverlightの参照を区別する。

    例えば、でSilverlight 3のツールキットでControls.DataVisualization.Toolkit.Designプロジェクトの下にスクリーンショットを参照してください2009年3月リリース

    image

    • プロジェクトのPresentationFrameworkとSystem.Windows両方の参照が、System.Windowsは、Silverlightエイリアス、代わりにデフォルトのグローバルエイリアスの下です。 。Silverlightのエイリアスの下のSystem.Windows名前の参照は、以下のようにcsprojファイルに永続化されます。
        Include ="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL" > < 参考 含める ="System.Windows、バージョン= 2.0.5.0、カルチャニュートラル、PublicKeyToken = = 7cec85d7bea7798e、processorArchitecture = MSIL">
         > False </ SpecificVersion > <SpecificVersion> </ SpecificVersion> > ..\Binaries\System.Windows.dll </ HintPath > <HintPath> .. \バイナリ\ System.Windows.dll </ HintPath>
         > False </ Private > < プライベート > </ プライベート > > Silverlight </ Aliases > < エイリアス >シルバー</ エイリアス >
       > </ 参照 > 

    • ソースコードでは、我々は、追加
        externエイリアスSilverlightの。
       SSW = Silverlightを使用して ::System.Windows; 

      SSWを経由して参照、SilverlightのFrameworkElementの::FrameworkElementは。

    TextBoxEditor

    [|バー|バブル|コラム|ライン|パイ|エリア散布図]のオブジェクト型のTitleプロパティを表示および編集するためControls.DataVisualization.Toolkit.DesignプロジェクトのインラインエディタのTextBoxEditorあります以下に示すようにシリーズとChartコントロールは、。

    TextBoxEditor

    右側の[プロパティ]ウィンドウの[タイトル]フィールドに入力されたテキストは、XAMLと中央にはデザインビューに自動的に表示されます。

    TextBoxEditorの実装は、前の手順に従いました。

    TextBoxEditor.cs

    • TextBoxEditorはPropertyValueEditorのサブクラスです。 それは設定InlineEditorTemplateをそのデフォルトのコンストラクタで。 ここで私が構築する代わりにXAMLのコードを使用してDataTemplateを何とか私はXAMLを構築するために得ることができないので、、とXAMLのすべてのものは、#いずれにせよC言語で書き換えることができます。
    • それはChartMetadata.csでChart.Title(参照に関連付けられているのSilverlight Toolkitの設計時の機能の実装 * Metadata.csファイルの詳細については、)

    TextBoxEditor.cs:

     / /(C)著作権Microsoft Corporationの。/ /このソースは、Microsoftパブリックライセンス(Ms - PL)の対象となる。/ /詳細については、http://go.microsoft.com/fwlink/?LinkID=131993を参照してください。/ /他のすべての権利  TextBoxEditor : PropertyValueEditor { /// <summary> /// Preserve the constructor prototype from PropertyValueEditor. /// </summary> /// <param name="inlineEditorTemplate">Inline editor template.</param> public TextBoxEditor(DataTemplate inlineEditorTemplate) : base (inlineEditorTemplate) { } /// <summary> /// Default constructor builds the default TextBox inline editor template. /// </summary> public TextBoxEditor() { FrameworkElementFactory textBox = new FrameworkElementFactory( typeof (TextBox)); Binding binding = new Binding(); binding.Path = new PropertyPath( "Value" ); binding.Mode = BindingMode.TwoWay; textBox.SetBinding(TextBox.TextProperty, binding); DataTemplate dt = new DataTemplate(); dt.VisualTree = textBox; InlineEditorTemplate = dt; } } } 。{/ / / <summary> / / /単純なTextBoxのインラインエディタ/ / / </概要> 公共部分クラス TextBoxEditor:。PropertyValueEditor {/ / / <summary> / / / PropertyValueEditorからコンストラクタのプロトタイプを保持/ / / </概要> / / / <param name="inlineEditorTemplate">インラインエディタのテンプレート</ PARAM> 公共 TextBoxEditor(DataTemplateをinlineEditorTemplate): ベース (inlineEditorTemplate){} / / / <summary> / / /デフォルトのコンストラクタは、デフォルトのテキストボックス(TextBox)インラインを構築。エディタテンプレート/ / / </概要> 公共 TextBoxEditor(){FrameworkElementFactoryテキストボックス= 新しいFrameworkElementFactory(typeof演算 (テキストボックス));バインディングバインディング= 新しいBinding(); binding.Path = 新しい PropertyPathの("値"); binding.Mode = BindingMode.TwoWay; textBox.SetBinding(TextBox.TextProperty、結合)、DataTemplateはDT = 新しい DataTemplateを(); dt.VisualTree =テキストボックス(TextBox); InlineEditorTemplate = DT;}}} 

    CultureInfoEditor

    同僚のRJは、 BlendでのCultureInfoの既定の編集経験が無効なXAMLにつながるため、TimePicker.CultureプロパティのインラインエディタのCultureInfoEditorを書きました。 スクリーンショットの下にCultureInfoEditorはすべてのCultureInfoを表示するためにComboBoxを使用すると右のXAMLを生成します示しています。

    CultureInfoEditor

    CultureInfoEditor実際には基礎となるプロパティは、DataContextプロパティを経由してエディタに関連付けられている方法を示し、TextBoxEditorより複雑な例です。

    CultureInfoEditor.cs:

     / /(C)著作権Microsoft Corporationの。/ /このソースは、Microsoftパブリックライセンス(Ms - PL)の対象となる。/ /詳細については、http://go.microsoft.com/fwlink/?LinkID=131993を参照してください。/ /他のすべての権利  CultureInfoEditor : PropertyValueEditor { /// <summary> /// The ComboBox being used to edit the value. /// </summary> private ComboBox _owner; /// <summary> /// Preserve the constructor prototype from PropertyValueEditor. /// </summary> /// <param name="inlineEditorTemplate">Inline editor template.</param> public CultureInfoEditor(DataTemplate inlineEditorTemplate) : base (inlineEditorTemplate) { } /// <summary> /// Default constructor builds a ComboBox inline editor template. /// </summary> public CultureInfoEditor() { // not using databinding here because Silverlight does not support // the WPF CultureConverter that is used by Blend. FrameworkElementFactory comboBox = new FrameworkElementFactory( typeof (ComboBox)); comboBox.AddHandler( ComboBox.LoadedEvent, new RoutedEventHandler( (sender, e) => { _owner = (ComboBox) sender; _owner.SelectionChanged += EditorSelectionChanged; INotifyPropertyChanged data = _owner.DataContext as INotifyPropertyChanged; if (data != null ) { data.PropertyChanged += DatacontextPropertyChanged; } _owner.DataContextChanged += CultureDatacontextChanged; })); comboBox.SetValue(ComboBox.IsEditableProperty, false ); comboBox.SetValue(ComboBox.DisplayMemberPathProperty, "DisplayName" ); comboBox.SetValue(ComboBox.ItemsSourceProperty, CultureInfo.GetCultures(CultureTypes.SpecificCultures)); DataTemplate dt = new DataTemplate(); dt.VisualTree = comboBox; InlineEditorTemplate = dt; } /// <summary> /// Handles the SelectionChanged event of the owner control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> /// instance containing the event data.</param> private void EditorSelectionChanged( object sender, SelectionChangedEventArgs e) { // serialize with name. object DataContext = _owner.DataContext; DataContext .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .SetValue(DataContext, ((CultureInfo)_owner.SelectedItem).Name, new object [] { }); } /// <summary> /// Handles the PropertyChanged event of the context object. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param> private void DatacontextPropertyChanged( object sender, PropertyChangedEventArgs e) { // deserialize from name. if (e.PropertyName == "Value" ) { object value = sender .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .GetValue(sender, new object [] { }); if ( value != null ) { if ( value is string ) { CultureInfo setCulture = new CultureInfo( value .ToString()); _owner.SelectedItem = setCulture; } } } } /// <summary> /// Called when the context is changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private void CultureDatacontextChanged( object sender, DependencyPropertyChangedEventArgs e) { INotifyPropertyChanged old = e.OldValue as INotifyPropertyChanged; if (old != null ) { old.PropertyChanged -= DatacontextPropertyChanged; } INotifyPropertyChanged newDataContext = e.NewValue as INotifyPropertyChanged; if (newDataContext != null ) { newDataContext.PropertyChanged += DatacontextPropertyChanged; } } } } 。{CultureInfoの/ / / <summary> / / /編集/ / / </概要> / / / <remarks>は、現在XAMLからエディタへのバインドをサポートしていません</備考> 公的クラス CultureInfoEditor:。PropertyValueEditor {/ / /値を編集するために使用されている<summary> / / /コンボボックス/ / / </概要> プライベートコンボボックス_owner、。。/ / / <summary> / / / PropertyValueEditorからコンストラクタのプロトタイプを保持/ / / </概要> / / / <param name="inlineEditorTemplate">インラインエディタのテンプレート</ PARAM> 公共 CultureInfoEditor(DataTemplateをinlineEditorTemplate): ベース (inlineEditorTemplate){} / / / <summary> / / /デフォルトのコンストラクタは、ComboBoxのインラインエディタのテンプレートを構築します。 / / / </概要> 公共 CultureInfoEditor(){/ /シルバーブレンドによって使用される/ / WPFのCultureConverterをサポートしていないため、ここでデータバインディングを使用していない FrameworkElementFactory ComboBoxは= 新しいFrameworkElementFactory(typeof演算 (コンボボックス));。comboBox.AddHandler( ComboBox.LoadedEvent、 新しい RoutedEventHandlerは((送信者、E)=> {_owner =(コンボボックス)送信者、_owner.SelectionChanged + = EditorSelectionChanged、INotifyPropertyChangedのデータ= _owner.DataContext INotifyPropertyChangedのように 、! 場合 (データ= NULL){data.PropertyChanged + = DatacontextPropertyChanged;} _owner.DataContextChanged + = CultureDatacontextChanged;})); comboBox.SetValue(ComboBox.IsEditableProperty、  ); comboBox.SetValue(ComboBox.DisplayMemberPathProperty、"表示名"); comboBox.SetValue(ComboBox.ItemsSourceProperty、CultureInfo.GetCultures (CultureTypes.SpecificCultures))、DataTemplateはdtが= 新しい DataTemplateを(); dt.VisualTree = comboBoxの、InlineEditorTemplate = DT;} / / / <summary> / / /オーナーのコントロールのSelectionChangedイベントを処理する/ / / </要約> / / / <param name="sender">イベントのソース。</ PARAM> / / / <param name="e"> <seeはcref="System.Windows.Controls.SelectionChangedEventArgs"/> /イベントデータを格納する/ /インスタンス</ PARAM> プライベート無効 EditorSelectionChanged( オブジェクト送信者、SelectionChangedEventArgs E){/ /名前とシリアライズ オブジェクトのDataContext = _owner.DataContext;。。。DataContextのGetTypeメソッド()のGetProperty("バリュー"は、BindingFlags。。公共| BindingFlags.Instance | BindingFlags.GetProperty)SetValueを(DataContextは、((カルチャ)_owner.SelectedItem)名、 新しいオブジェクト [] {});} / / / <summary> / / /ハンドルのPropertyChangedイベント 。。 コンテキストオブジェクト。/ / / </概要> / / / <param name="sender">イベントのソース。</ PARAM> / / / <param name="e"> <CREF ="システムを参照してください。 ComponentModel.PropertyChangedEventArgs"/イベントデータを含む>インスタンス。</ PARAM> プライベート無効 DatacontextPropertyChanged( オブジェクト送信者、PropertyChangedEventArgsのe){/ /名前から逆シリアル化する if(e.PropertyName =="値"){ オブジェクトの値 =送信者。 GetTypeメソッド()のGetProperty("バリュー"、のBindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)GetValueメソッド(送信者、 新しいオブジェクト [] {});。。 するif(!値 = NULL){{(値が文字列である場合のCultureInfo setCulture = 新しいCultureInfo(値のtoString()); _owner.SelectedItem = setCulture;}}}} / / / <summary> / / /コンテキストが変更されたときに呼び出されます/ / / </概要> / / / <パラメータ名="送信者">送信者。</ PARAM> / / / <param name="e">イベントデータを格納している<see cref="System.Windows.DependencyPropertyChangedEventArgs"/>インスタンス。</ PARAM> プライベート INotifyPropertyChangedを古い= e.OldValue INotifyPropertyChangedのように {( オブジェクト送信者、DependencyPropertyChangedEventArgs E)CultureDatacontextChanged 無効 ; 場合 (古い= NULL){old.PropertyChanged -= DatacontextPropertyChanged;} INotifyPropertyChangedのnewDataContext = e.NewValue として INotifyPropertyChangedの;! 場合 (newDataContext = ヌル ){newDataContext.PropertyChanged + = DatacontextPropertyChanged;}}}} 

    ExpandableObjectConverter

    冒頭で述べたように、カスタムプロパティの値のエディタに加えて、時々あなたは良い編集の経験とXAMLのシリアル化を提供するために適切な型コンバータを使用することができます。 一例として、ColumnSeries.DependentRangeAxisです:それはIRangeAxisの型である、ブレンドはDependentRangeAxisがように[プロパティ]パネルで、読み取り専用を示していますので、それを編集する方法を知っていません。 関連付けることで、 ExpandableObjectConverterを ColumnSeries.DependentRangeAxisする。

      b.AddCustomAttributes(
         Extensions.GetMemberName <ColumnSeries>(X => x.DependentRangeAxis)、
         (ExpandableObjectConverter))); 新しい TypeConverterAttributeを(typeof演算 (ExpandableObjectConverter)));
     b.AddCustomAttributes(
         Extensions.GetMemberName <ColumnSeries>(X => x.IndependentAxis)、
         (ExpandableObjectConverter))); 新しい TypeConverterAttributeを(typeof演算 (ExpandableObjectConverter))); 

    ブレンドは、[プロパティ]パネルの[このプロパティの横に新しいボタンが表示されます。クリックしたときに、それは、プロパティの型IRangeAxisでフィルタリング[オブジェクトの選択]ダイアログを、ポップアップ表示されます:

    ExpandableObjectConverter

    結論

    一緒にブレンドし、シルバーデザイナーが直接、実際のコントロールに対して素晴らしいUIを作成できるように、それはコントロールの開発者は、全体的な制御の設計と実装の一環として、デザイナーの経験を取ることが非常に重要です。 素敵な編集UIを提供し、適切なXAMLを生成するカスタムプロパティエディタを持つことは、デザイナーの経験の重要な部分です。 うまくいけば、この記事では、カスタムプロパティエディタを作成する方法を理解するのに役立ちます。 ありがとう!