トップ回答者
カスタムContentControlの作り方 またはサンプルの在処を教えてください。

質問
-
System.Windows.Controls.ContentControlから派生させて独自のコントロールを作りたいのですが、そのコンテンツに名前を付けられなくて困っています。
作成してみたMyCtl.xamlとMyCtl.xaml.vbは次のようなものです。<ContentControl x:Class="MyCtl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ContentControl.Template> <ControlTemplate> <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/> </ControlTemplate> </ContentControl.Template> </ContentControl>
Partial Public Class MyCtl ''' <summary> ''' このコンストラクタがないと、コンテンツのバインディングが失敗する ''' </summary> ''' <remarks></remarks> Public Sub New() ' warning BC40054 : デザイナで生成された型 'WPFctl.MyCtl' の 'Public Sub New()' は InitializeComponent メソッドを呼び出さなければなりません。 InitializeComponent() End Sub End Class
次に、これを使用するウィンドウを次のように作りました。<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml xmlns:WPFctl="clr-namespace:WPFctl;assembly=WPFctl" x:Class="Window1" Title="Window1"> <Grid> <WPFctl:MyCtl> <Button Content="うさぎ" /> </WPFctl:MyCtl> </Grid> </Window>
質問
・そもそもこのようにしてControlTemplateを作成するのは正しい手順なのでしょうか?
・うさぎに名前(x:Name)をつけるとerror MC3093となりますが、解決策をおしえてください。(コンテンツ内で要素同士をバインディングさせるときに必要)- 編集済み もももん 2009年6月28日 10:58 InitializeComponentを追加しました
回答
-
派生してコントロールを作成すると言う事は、カスタムコントロールって事ですね。
カスタムコントロールを作りたい場合には、
新しいプロジェクトの「WPFカスタムコントロールライブラリ」で
作ったプロジェクトを参考にするのが良いと思います。
新しくプロジェクトを作成して、CustomControl1が、Controlから派生となっていますが、
これをContentControlから派生するように変えてあげてください。
(CustomControl1が参照するxamlは、Generic.xaml に書かれていると思います。)
調べごとのきっかけにでもなれば幸いです。
Sorry, I am not good at English.- 回答としてマーク もももん 2009年6月29日 15:58
-
このページがいいですね。
http://msdn.microsoft.com/ja-jp/library/ms745025(VS.80).aspx
Control や FrameworkElement の派生である ContentControl でも Control や FrameworkElement と同じようにカスタムコントロールは作れます。
ただし上記ページに書いてあるとおり 質問にあった ContentControl で始まるようなXAMLはかけません、ここが UserControl と違うところです。
Style や Template として XAMLを適用してやる必要があります。
その置き場所は FC-Shiro さんの書かれているように Generic.xaml がいいと思います。
えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12- 回答としてマーク もももん 2009年6月29日 15:58
-
すべての問題が解決しました。ここに記録しておきます。
- (VisualStudio2008 では?)新しいプロジェクトを Visual Basic または Visual C# の下の階層にある WPF カスタム コントロール ライブラリ で作成する。
!!!WPF カスタム コントロール ライブラリ!!!
!!!WPF カスタム コントロール ライブラリ!!! - 派生元を ContentControl に変える
- テンプレートを編集して ContentPresenter Content="{TemplateBinding ContentControl.Content}" を書き込む
FC-Shiro 氏の回答がすべてなのですが、プロジェクトを [クラスライブラリ] からの発展でこしらえていたので全く気づきませんでした。プロジェクトファイルを調べると両者はそれなりに違っています。そして決定的に違うのは、次のコードがエラーを引き起こすことです。
Shared Sub New() ' このコードはビルドできるがプロジェクトの種類が適切でないと実行時にエラーとなる ' (ResourceDictionary インスタンスを再初期化することはできません。) DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomControl1), _ New FrameworkPropertyMetadata(GetType(CustomControl1))) End Sub
そもそも既存の[クラスライブラリ]プロジェクトにWPFの機能を追加しようとしたのがよろしくないらしい。
- 回答としてマーク もももん 2009年6月29日 16:36
- (VisualStudio2008 では?)新しいプロジェクトを Visual Basic または Visual C# の下の階層にある WPF カスタム コントロール ライブラリ で作成する。
すべての返信
-
これを追加したら、うさぎからWindowへのバインディングはうまくいくようになった。
こんなに大袈裟なことをしなければならないのだろうか。
.NET Reflectorで例えばLabelを覗いてみてもこんなメンドクサイことはやっていない。
Partial Public Class MyCtl Implements INameScope Private ReadOnly _Names As Dictionary(Of String, Object) = New Dictionary(Of String, Object)() Public Function FindNameX(ByVal name As String) As Object Implements INameScope.FindName Try Dim FE As FrameworkElement = TryCast(MyBase.Parent, FrameworkElement) If FE IsNot Nothing Then Return FE.FindName(name) Else Return _Names.Item(name) End If Catch ex As Exception Debug.WriteLine(String.Format("{0}", ex.Message)) Return Nothing End Try End Function Public Sub RegisterNameX(ByVal name As String, ByVal scopedElement As Object) Implements INameScope.RegisterName Try Dim FE As FrameworkElement = TryCast(MyBase.Parent, FrameworkElement) If FE IsNot Nothing Then FE.RegisterName(name, scopedElement) Else _Names.Add(name, scopedElement) End If Catch ex As Exception Debug.WriteLine(String.Format("{0}", ex.Message)) End Try End Sub Public Sub UnregisterNameX(ByVal name As String) Implements INameScope.UnregisterName Try Dim FE As FrameworkElement = TryCast(MyBase.Parent, FrameworkElement) If FE IsNot Nothing Then FE.UnregisterName(name) Else _Names.Remove(name) End If Catch ex As Exception Debug.WriteLine(String.Format("{0}", ex.Message)) End Try End Sub End Class
-
派生してコントロールを作成すると言う事は、カスタムコントロールって事ですね。
カスタムコントロールを作りたい場合には、
新しいプロジェクトの「WPFカスタムコントロールライブラリ」で
作ったプロジェクトを参考にするのが良いと思います。
新しくプロジェクトを作成して、CustomControl1が、Controlから派生となっていますが、
これをContentControlから派生するように変えてあげてください。
(CustomControl1が参照するxamlは、Generic.xaml に書かれていると思います。)
調べごとのきっかけにでもなれば幸いです。
Sorry, I am not good at English.- 回答としてマーク もももん 2009年6月29日 15:58
-
このページがいいですね。
http://msdn.microsoft.com/ja-jp/library/ms745025(VS.80).aspx
Control や FrameworkElement の派生である ContentControl でも Control や FrameworkElement と同じようにカスタムコントロールは作れます。
ただし上記ページに書いてあるとおり 質問にあった ContentControl で始まるようなXAMLはかけません、ここが UserControl と違うところです。
Style や Template として XAMLを適用してやる必要があります。
その置き場所は FC-Shiro さんの書かれているように Generic.xaml がいいと思います。
えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12- 回答としてマーク もももん 2009年6月29日 15:58
-
お二方 ご助言ありがどうございます。
えムナウ氏が示された資料は当然のごとく読んでおりました。
ここで資料の一部を引用します。
外部のコントロール ライブラリ 最後の手順は、NumericUpDown コントロールをその独自のアセンブリにパッケージ化して、簡単に再利用できるようにすることです。 テーマ ファイルの作成 NumericUpDown クラスをライブラリ アセンブリに移動したら、スタイル定義を移動する必要があります。最初に、すべてのテーマ ファイルを格納するための "themes" フォルダを作成する必要があります。次に、generic.xaml という名前のファイルを作成します。このファイルは、このアセンブリのすべてのリソース検索のフォールバックとして機能します。
generic.xaml についてはこのあたりで登場しますが、[最後の手順は]の後で、[スタイル定義を移動する必要があります。]とあり、それが要請する結果として、generic.xamlが登場しています。
私のコードがスタイルのことに注意を払っていないことが見て取れると思いますが、
そんなことは後回しで構わない
という解釈でした。
が、その遥か以前にある
テンプレートへの移動 基本クラスを更新したら、コントロールのコンテンツをテンプレートに移動する必要があります。テンプレートはスタイルで定義され、アプリケーション内の多くの場所に置くことができます。この例では、アプリケーション リソースに置きます。
ここを読み落としていたようです。
結果は後ほど。 -
すべての問題が解決しました。ここに記録しておきます。
- (VisualStudio2008 では?)新しいプロジェクトを Visual Basic または Visual C# の下の階層にある WPF カスタム コントロール ライブラリ で作成する。
!!!WPF カスタム コントロール ライブラリ!!!
!!!WPF カスタム コントロール ライブラリ!!! - 派生元を ContentControl に変える
- テンプレートを編集して ContentPresenter Content="{TemplateBinding ContentControl.Content}" を書き込む
FC-Shiro 氏の回答がすべてなのですが、プロジェクトを [クラスライブラリ] からの発展でこしらえていたので全く気づきませんでした。プロジェクトファイルを調べると両者はそれなりに違っています。そして決定的に違うのは、次のコードがエラーを引き起こすことです。
Shared Sub New() ' このコードはビルドできるがプロジェクトの種類が適切でないと実行時にエラーとなる ' (ResourceDictionary インスタンスを再初期化することはできません。) DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomControl1), _ New FrameworkPropertyMetadata(GetType(CustomControl1))) End Sub
そもそも既存の[クラスライブラリ]プロジェクトにWPFの機能を追加しようとしたのがよろしくないらしい。
- 回答としてマーク もももん 2009年6月29日 16:36
- (VisualStudio2008 では?)新しいプロジェクトを Visual Basic または Visual C# の下の階層にある WPF カスタム コントロール ライブラリ で作成する。