2009-04-15 75 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}"/> 

访问来自代码模板中的命名控制背后,你会需要使用可视化树。

相关问题