UserControl 的内容属性内的命名控件在运行时始终显示为空

Posted

技术标签:

【中文标题】UserControl 的内容属性内的命名控件在运行时始终显示为空【英文标题】:Named Controls inside content property of UserControl always appear null at run time 【发布时间】:2011-08-05 23:07:36 【问题描述】:

我创建了一个用户控件,可重复用于不同的目的。我已经定义了一个包含 UIElement 的依赖属性,我将其呈现为用户控件中某个区域的内容。

我注意到当我使用这个控件时。并为 content 属性中的元素命名,它们在运行时总是显示为 null。

MyContainer.xaml:

<UserControl x:Class="BSSApp.UI.Tests.MyContainer" x:Name="userControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Border x:Name="LayoutRoot" Background="Green" CornerRadius="20" BorderBrush="#005500" BorderThickness="10">
        <ContentPresenter Content="Binding ElementName=userControl, Path=MyContent" Margin="20"/>

    </Border>
</UserControl>

MyContainer.xaml.cs

namespace BSSApp.UI.Tests

    public partial class MyContainer : UserControl
    
        public static readonly DependencyProperty MyContentProperty =
                    DependencyProperty.Register("MyContent",
                                                typeof(UIElement),
                                                typeof(MyContainer),
                                                new PropertyMetadata(new Grid()));

        public UIElement MyContent
        
            get
            
                return (UIElement)GetValue(MyContentProperty);
            
            set
            
                SetValue(MyContentProperty, value);
            
        


        public MyContainer()
        
            InitializeComponent();
        
    

以及使用类: UserControlContentBug.xaml:

<UserControl x:Class="BSSApp.UI.Tests.UserControlContentBug"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:t="clr-namespace:BSSApp.UI.Tests"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <t:MyContainer>
            <t:MyContainer.MyContent>
                <Button Name="btnWithName" Click="btnWithName_Click">Click Me</Button>
            </t:MyContainer.MyContent>
        </t:MyContainer>
    </Grid>
</UserControl>

以及后面的代码: UserControlContentBug.xaml.cs

 namespace BSSApp.UI.Tests
 
     public partial class UserControlContentBug : UserControl
     
         public UserControlContentBug()
         
             InitializeComponent();
         

         private void btnWithName_Click(object sender, RoutedEventArgs e)
         
             // this throws an exception
             btnWithName.Tag=2;
         
     
  

所以有一个按钮的声明,名为“btnWithName”。该变量确实是在后面的代码中声明的,但它保持为空...仅当按钮在另一个用户控件的属性中声明时才会发生。

有人知道怎么解决吗?

谢谢

【问题讨论】:

【参考方案1】:

我自己也遇到了这个问题。生成的部分类(例如,obj\UserControlContentBug.xaml.g.cs)定义了 XAML 中代码可访问的命名元素,使用 FrameworkElement.FindName() 进行这些分配。

根据 MSDN:

FindName 等运行时 API 正在处理对象树。这些对象被加载到整个 Silverlight 插件的内容区域和 CLR 运行时引擎中。从模板或运行时加载的 XAML 创建对象树的一部分时,XAML 名称范围通常与该对象树的整体不连续。结果是对象树中可能有一个给定的 FindName 调用找不到的命名对象。

由于 MyContainer 将其自己的名称范围边界建立为 UserControl,因此从 UserControlContentBug 调用 FindName() 将不会探索对象树的该部分(其中 btnWithName 已定义)。

可能有几种不同的方法可以解决这个问题,但对我来说最简单的方法是:

    为 MyContainer 命名 (x:Name="myContainer")。

    从 Button (btnWithName) 中删除名称,这样在生成的 C# 代码中就不会保留多余的、始终为空的同名变量。

    在代码隐藏中创建对 Button 的引用。

像这样:

namespace BSSApp.UI.Tests

    public partial class UserControlContentBug : UserControl
    
        private Button btnWithName;

        public UserControlContentBug()
        
            Loaded += UserControlContentBugLoaded;
            InitializeComponent();
        

        private void UserControlContentBugLoaded(object sender, System.Windows.RoutedEventArgs e)
        
            btnWithName = myContainer.MyContent as Button;
        

        private void btnWithName_Click(object sender, RoutedEventArgs e)
        
            // this throws an exception, not so much
            btnWithName.Tag=2;
        
    

希望这会有所帮助。

【讨论】:

【参考方案2】:

尝试在按钮上使用 x:Name 而不是 Name。

【讨论】:

我用 Name 和 x:Name 都试过了 - 但我得到了相同的结果。

以上是关于UserControl 的内容属性内的命名控件在运行时始终显示为空的主要内容,如果未能解决你的问题,请参考以下文章

WPF怎么修改在窗体里引用的UserControl中元素的值?

如何在 DataGridTemplateColumn 内的控件上双向绑定属性?

在 UserControl WPF MVVM caliburn 内的 UserControl 之间切换

如何绑定到WPF中的usercontrol内的控件?

WPF ----在UserControl的xaml里绑定依赖属性

无法在WPF中创建继承的usercontrol,本地命名空间中的基本控件“不存在”