WPF:无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内,在另一范围内定义它时,已注册了名称。

最近在改一段 XAML 代码时,我发现无论如何给一个控件添加 Name 或者 x:Name 属性时都会出现编译错误:无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内,在另一范围内定义它时,已注册了名称。


编译错误

编译时,出现错误:

无法对元素“XXX”设置 Name 特性值“YYY”。“XXX”在元素“ZZZ”的范围内,在另一范围内定义它时,已注册了名称。

MC3093: Cannot set Name attribute value ‘X’ on element ‘Y’. ‘Y’ is under the scope of element ‘Z’, which already had a name registered when it was defined in another scope.

这里的 XXX 是元素的类型,YYY 是指定的名称的值,ZZZ 是父容器的名称。

我把出现错误的 XAML 简化后大约是这样的,XXXTextBoxYYYRenameTextBox,而 ZZZwalterlv:Foo

<walterlv:Foo Background="White">
    <StackPanel Orientation="Horizontal" Focusable="False">
        <TextBlock Text="名称:" />
        <TextBox x:Name="RenameTextBox" />
    </StackPanel>
</walterlv:Foo>

小心用户控件

出现此问题的最大原因在那个 walterlv:Foo 上。实际上,这是一个用户控件,也就是继承自 UserControl 的大家通常用来写界面的东西。

<UserControl x:Class="Walterlv.Foo"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Style>
        <!-- 省略 -->
    </UserControl.Style>
</UserControl>

别问我为什么会有以上这样诡异的代码。我也不知道,这只是偶然发现的代码,我简化后拿到博客中。

于是需要提醒大家注意:

  1. 在 WPF 里,拥有直接的 XAML 文件的始终应该作为最终用户界面,不应该当作控件使用(不要试图在其他地方使用时还设置其 Content 属性);
  2. 如果你确实希望做控件,请继承自 CustomControl 然后在 /Themes/Generic.xaml 里写样式。

至于以上 XAML 代码中我看到用的是 <UserControl.Style> 来写样式,是因为踩到了当控件用的另一个坑:

所有在控件的 XAML 中设置的 Content 属性都将被使用时覆盖。

解决方法

当然是考虑将以上诡异的用户控件定义方式改为正统的 CustomControl 啦!将 <UserControl.Style> 里定义的所有样式全部改到 /Themes/Generic.xaml 文件中。

创建自定义控件

如果你不清楚如何编写一个自定义控件,那么请直接在 Visual Studio 中基于 WPF 自定义控件创建文件,你会发现 Visual Studio 为你写好了注释。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Walterlv.Demo
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Walterlv.Demo"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Walterlv.Demo;assembly=Walterlv.Demo"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目,然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:Foo/>
    ///
    /// </summary>
    public class Foo : Control
    {
        static Foo()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Foo), new FrameworkPropertyMetadata(typeof(Foo)));
        }
    }
}

/Themes/Generic.xaml 文件:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Walterlv.Demo">


    <Style TargetType="{x:Type local:Foo}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Foo}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/cannot-set-name-attribute-value-on-element-using-wpf.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,或者前往 CSDN 关注我的主页

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])