2009-04-15 46 views
73

添付のコマンドとロジックを使用して、同じ方法で常に再利用される一連のコントロールがあります。すべての共通コントロールとロジックを保持するユーザーコントロールを作成することにしました。NAMEDコンテンツを含むWPF UserControlを作成する方法

ただし、名前を付けることができるコンテンツを保持できるコントロールも必要です。私は次のように試しました:

<UserControl.ContentTemplate> 
    <DataTemplate> 
     <Button>a reused button</Button> 
     <ContentPresenter Content="{TemplateBinding Content}"/> 
     <Button>a reused button</Button> 
    </DataTemplate> 
</UserControl.ContentTemplate> 

しかし、ユーザーコントロール内に配置されたコンテンツの名前は付けられません。私はbuttonNameを削除すると、それはコンパイル

Cannot set Name attribute value 'buttonName' on element 'Button'. 'Button' is under the scope of element 'UserControl1', which already had a name registered when it was defined in another scope.

、しかし私は名前のことができるようにする必要があります:私は次のエラーが表示さ

<lib:UserControl1> 
    <Button Name="buttonName">content</Button> 
</lib:UserControl1> 

:たとえば、私は、次のようにコントロールを使用する場合コンテンツ。どうすればこれを達成できますか?

+0

これは偶然のことです。私はちょうどこの質問をしようとしていた!私は同じ問題を抱えています。一般的なUIパターンをUserControlに分解するが、コンテンツUIを名前で参照したい。 – mackenir

+1

ResourceDictionaryの方法はどうですか?その中にDataTemplateを定義します。または、BasedOnキーワードを使用してコントロールを継承します。 WPFでコードビハインドUIを実行する前に私が従うちょうどいくつかのパス... –

+2

この男は[解決策]を見つけました(http://blog.bluecog.co.nz/archives/2007/08/27/wpf-cannot -set-name-attribute /)を使用して、カスタムコントロールのXAMLファイルを削除し、カスタムコントロールのUIをプログラムでビルドします。この[ブログの投稿](http://rrelyea.spaces.live.com/Blog/cns!167AD7A5AB58D5FE!2130.entry?wa=wsignin1.0&sa=752255111)は、この件についてもっと言いたいことがあります。 – mackenir

答えて

16

XAMLを使用するとこれは不可能と思われます。カスタムコントロールは、私が必要とするすべてのコントロールを実際に持っていても、ちょっとしたロジックと一緒にグループ化し、名前付きのコンテンツを許可する必要があるときに、過剰なものに見えます。

mackenirのJD's blogの解決策には、最良の妥協点があるようです。

protected override void OnInitialized(EventArgs e) 
    { 
     base.OnInitialized(e); 

     var grid = new Grid(); 
     var content = new ContentPresenter 
          { 
           Content = Content 
          }; 

     var userControl = new UserControlDefinedInXAML(); 
     userControl.aStackPanel.Children.Add(content); 

     grid.Children.Add(userControl); 
     Content = grid;   
    } 

私の例では、私はXAMLを使用して、任意の通常のユーザーコントロールのように定義されUserControlDefinedInXAMLと呼ばれるユーザーコントロールを作成した上で、次のようにまだXAMLで定義されるコントロールは可能性ができるように、JDのソリューションを拡張する方法。私のUserControlDefinedInXAMLにはStackPanelという名前のコンテンツを表示させるためのaStackPanelがあります。

+0

私は、コンテンツを再親子化するこのメカニズムを使用するときにデータバインディングの問題が発生することを発見しました。データバインディングは正しく設定されているように見えますが、データソースからのコントロールの初期設定は正しく機能しません。私は問題がコンテンツプレゼンターの直接の子ではないコントロールに限られていると思います。 – mackenir

+0

これ以降はこれを試す機会はありませんでしたが、私はUserControlDefinedInXAML(上記の例)で定義されたコントロールまたはこれまでContentPresenterに追加されたコントロールのいずれかにデータバインディングすることに問題はありませんでした。私はコードを介してデータバインディングを行っていますが(XAMLではなく、違いがあるかどうかはわかりません)。 – Ryan

+0

それは違いがあるようです。あなたが説明したケースでデータバインディングにXAMLを使用しようとしましたが、動作しません。しかし、コードで設定すると、うまくいきます! – Ryan

31

答えは、それを行うためにUserControlを使用しないことです。

その後、内容を指定するスタイルを使用ContentControlに

public class MyFunkyControl : ContentControl 
{ 
    public static readonly DependencyProperty HeadingProperty = 
     DependencyProperty.Register("Heading", typeof(string), 
     typeof(HeadingContainer), new PropertyMetadata(HeadingChanged)); 

    private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ((HeadingContainer) d).Heading = e.NewValue as string; 
    } 

    public string Heading { get; set; } 
} 

を拡張するクラスを作成します

<Style TargetType="control:MyFunkyControl"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="control:MyFunkyContainer"> 
       <Grid> 
        <ContentControl Content="{TemplateBinding Content}"/> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

、最終的には - それを

<control:MyFunkyControl Heading="Some heading!">    
    <Label Name="WithAName">Some cool content</Label> 
</control:MyFunkyControl> 
+2

デザイナを使用して通常のUserControlでControlTemplateを完成させ、それを関連付けられたコントロールテンプレートでスタイルに変換できるので、これは実際に最も快適なソリューションです。 –

+1

ええと、私はこのアプローチを適用しようとしたため、私の場合はまだこの悪名高いエラーを取得するので、それはあなたのために動作する少し奇妙です。 – greenoldman

+1

私と同じです:効果はありません。 :( – Badiboy

3

I」は別の方法を使用します使用したのは、を設定するだけですLoadedイベントの3210プロパティ。

私の場合、私はコードビハインドで作成したくないかなり複雑なコントロールを持っていました。特定の動作のための特定の名前のオプションのコントロールを探しました。 DataTemplateの名前私はLoadedイベントでもそれを行うことができると考えました。

private void Button_Loaded(object sender, RoutedEventArgs e) 
{ 
    Button b = sender as Button; 
    b.Name = "buttonName"; 
} 
+2

これを行うと、名前の付いたバインディングは動作しません。 – Tower

0

私は私が取得する必要があり、各要素のための余分なプロパティを作成することを選択しました:

public FrameworkElement First 
    { 
     get 
     { 
      if (Controls.Count > 0) 
      { 
       return Controls[0]; 
      } 
      return null; 
     } 
    } 

これはXAMLで子要素にアクセスするために私を可能にします:たまに

<TextBlock Text="{Binding First.SelectedItem, ElementName=Taxcode}"/> 
3

C#の要素を参照するだけでよいでしょう。ユースケースに応じて、x:Nameの代わりにx:Uidを設定し、Get object by its Uid in WPFのようなUidファインダメソッドを呼び出すことで要素にアクセスできます。

0

あなたはユーザーコントロール内にセット名に、このヘルパーを使用することができます:あなたはプロパティによって制御セットの背後

using System; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Media; 
namespace UI.Helpers 
{ 
    public class UserControlNameHelper 
    { 
     public static string GetName(DependencyObject d) 
     { 
      return (string)d.GetValue(UserControlNameHelper.NameProperty); 
     } 

     public static void SetName(DependencyObject d, string val) 
     { 
      d.SetValue(UserControlNameHelper.NameProperty, val); 
     } 

     public static readonly DependencyProperty NameProperty = 
      DependencyProperty.RegisterAttached("Name", 
       typeof(string), 
       typeof(UserControlNameHelper), 
       new FrameworkPropertyMetadata("", 
        FrameworkPropertyMetadataOptions.None, 
        (d, e) => 
        { 
         if (!string.IsNullOrEmpty((string)e.NewValue)) 
         { 
          string[] names = e.NewValue.ToString().Split(new char[] { ',' }); 

          if (d is FrameworkElement) 
          { 
           ((FrameworkElement)d).Name = names[0]; 
           Type t = Type.GetType(names[1]); 
           if (t == null) 
            return; 
           var parent = FindVisualParent(d, t); 
           if (parent == null) 
            return; 
           var p = parent.GetType().GetProperty(names[0], BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty); 
           p.SetValue(parent, d, null); 
          } 
         } 
        })); 

     public static DependencyObject FindVisualParent(DependencyObject child, Type t) 
     { 
      // get parent item 
      DependencyObject parentObject = VisualTreeHelper.GetParent(child); 

      // we’ve reached the end of the tree 
      if (parentObject == null) 
      { 
       var p = ((FrameworkElement)child).Parent; 
       if (p == null) 
        return null; 
       parentObject = p; 
      } 

      // check if the parent matches the type we’re looking for 
      DependencyObject parent = parentObject.GetType() == t ? parentObject : null; 
      if (parent != null) 
      { 
       return parent; 
      } 
      else 
      { 
       // use recursion to proceed with next level 
       return FindVisualParent(parentObject, t); 
      } 
     } 
    } 
} 

とあなたのウィンドウやコントロールコード:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

    } 

    public Button BtnOK { get; set; } 
} 

あなたのウィンドウのXAML:

<Window x:Class="user_Control_Name.MainWindow" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:test="clr-namespace:user_Control_Name" 
      xmlns:helper="clr-namespace:UI.Helpers" x:Name="mainWindow" 
      Title="MainWindow" Height="350" Width="525"> 
     <Grid> 
      <test:TestUserControl> 
       <Button helper:UserControlNameHelper.Name="BtnOK,user_Control_Name.MainWindow"/> 
      </test:TestUserControl> 
      <TextBlock Text="{Binding ElementName=mainWindow,Path=BtnOK.Name}"/> 
     </Grid> 
    </Window> 

UserControlNameHelperコントロール名を取得し、コントロールをPに設定するクラス名を取得します。ルパンティ背後

0
<Popup> 
    <TextBox Loaded="BlahTextBox_Loaded" /> 
</Popup> 

コード:

public TextBox BlahTextBox { get; set; } 
private void BlahTextBox_Loaded(object sender, RoutedEventArgs e) 
{ 
    BlahTextBox = sender as TextBox; 
} 

Microsoftは仮に話すこの問題を解決するだけでなく、壊れた視覚的な木と他のすべてなどするための真の解決策は次のようになります。

0

さらに別の回避策:要素をRelativeSourceと参照してください。

0

多くの名前付きコントロールを配置するときにTabControlを使用して同じ問題が発生しました。

私の回避策は、すべてのコントロールがタブページに表示されるコントロールテンプレートを使用することでした。テンプレート内では、Nameプロパティを使用できます。また、同じテンプレート内の他のコントロールから名前付きコントロールのプロパティにデータをバインドすることもできます。あなたはビジュアルツリーを使用する必要があります後ろ

<Control Template="{StaticResource MyControlTemplate}"/> 

をコードからテンプレートの中にあるものをという名前のコントロールへのアクセス:

のTabItemコントロールの内容として、簡単なコントロールを使用し、それに応じたControlTemplateを設定します。

関連する問題