2009-04-15 28 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> 

但是,似乎放置在用戶控件內的任何內容都無法命名。例如,如果我使用以下方式控制:

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

我收到以下錯誤:

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.

如果我刪除BUTTONNAME,然後再編譯,但是我需要能夠名稱內容。我怎樣才能做到這一點?

+0

這是一個巧合。我正要問這個問題!我也有同樣的問題。將通用UI模式分解爲UserControl,但希望通過名稱引用內容UI。 – mackenir 2009-04-15 16:11:50

+1

爲什麼不使用ResourceDictionary方法?在其中定義DataTemplate。或者使用BasedOn關鍵字繼承該控件。只是我在做WPF代碼隱藏UI之前要遵循的一些路徑... – 2011-07-22 22:37:34

+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 2009-04-15 16:43:08

回答

16

看來這是不可能的,當使用XAML時。當我實際擁有我需要的所有控件時,自定義控件似乎是一種矯枉過正,但只需要用一些邏輯將它們組合在一起,並允許指定內容。

馬克尼爾認爲,JD's blog的解決方案似乎有最好的折中。延長JD的溶液A的方法,以允許仍然在XAML來定義控制可以如下:

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;   
    } 

在我的例子以上我已經創建稱爲UserControlDefinedInXAML的用戶控制其是定義像使用XAML任何正常用戶控件。在我的UserControlDefinedInXAML中,我有一個名爲aStackPanel的StackPanel,其中我想讓我的命名內容出現。

+0

我一直在發現,當使用這種重新父母內容的機制時,我會遇到數據綁定問題。數據綁定似乎設置正確,但數據源中控件的初始填充無法正常工作。我認爲這個問題僅限於不是內容提供者的直接子女的控制。 – mackenir 2009-04-24 08:36:03

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> 
3

另一種選擇我我使用的是隻設置在Loaded事件中的3210財產。

在我的情況下,我有一個相當複雜的控制,我不想在代碼隱藏中創建,它尋找一個特定名稱的可選控件,因爲我注意到我可以設置在DataTemplate的名字我想我也可以在Loaded事件中做到這一點。

private void Button_Loaded(object sender, RoutedEventArgs e) 
{ 
    Button b = sender as Button; 
    b.Name = "buttonName"; 
} 
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:Uid而不是x:Name,並通過調用像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 roperty。

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

後面的代碼:

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

真正的解決方案將是微軟來解決這個問題,以及所有與打破視覺樹木等假設說別人。

0

又一個解決方法:引用元素爲RelativeSource

0

我在使用TabControl放置一堆命名控件時遇到了同樣的問題。

我的解決方法是使用一個控制模板,其中包含我要顯示在標籤頁中的所有控件。在模板內部,您可以使用Name屬性,也可以將數據綁定到至少在同一模板內的其他控件的指定控件的屬性。

由於TabItem的控制的內容,用一個簡單的控制,並設置相應的控件模板:

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

訪問來自代碼模板中的命名控制背後,你會需要使用可視化樹。

相關問題