在代码隐藏中指定 UserControl 的 .XAML 部分

Posted

技术标签:

【中文标题】在代码隐藏中指定 UserControl 的 .XAML 部分【英文标题】:Specify .XAML part of UserControl in code-behind 【发布时间】:2015-01-26 20:13:00 【问题描述】:

我编写了自定义TreeView 控件。

XAML:

<TreeView x:Class="EArchiveMaster.View.MyTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TreeView.ItemContainerStyle>
        <Style TargetType="x:Type TreeViewItem">
            <Setter Property="IsExpanded" Value="Binding IsExpanded, Mode=TwoWay" />
            <Setter Property="IsSelected" Value="Binding IsSelected, Mode=TwoWay" />
            <EventSetter Event="LostFocus" Handler="EventSetter_OnHandler" />
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

.cs

public partial class MyTreeView

    public event Action SomeItemLostFocus;

    public MyTreeView()
    
        InitializeComponent();
    

    private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
    
        e.Handled = true;
        if (SomeItemLostFocus != null)
            SomeItemLostFocus();
    

但是当我尝试使用它时,我得到了众所周知的错误:

无法在元素“TextBox”上设置名称属性值“TextBox”。 “TextBox”在元素“MyTreeView”的范围内,当它在另一个范围内定义时,它已经注册了一个名称。

我找到了一些收据来解决这个错误。即,在其代码隐藏中指定控制的 .xaml 部分。 但我不知道我该怎么做。

【问题讨论】:

已经有一个相同的question for WPF。大多数答案也适用于您的情况。此外,您没有显示将Name 设置为TextBox 的位置。 @icebat 我将Name 设置为TextBox 的位置无关紧要。问题是“如何在其代码隐藏中指定控制的 .xaml 部分”。你能帮忙吗? @icebat 我已经看到了这个问题,但我不明白在我的情况下我应该在OnInitialized 方法中写什么...... 【参考方案1】:

代码清楚地表明您想要扩展 TreeView。基本上,如果您想构建可以保存一些内容(可以命名...)的控件,例如 ContentControl、ItemsControl 等。使用 CustomControl 总是更好。带有 XAML 和 CS 代码的 UserControl 不适合这种情况。

在您的情况下,创建一个如下所示的类并扩展功能,

public class MyTreeView : TreeView

    public event Action SomeItemLostFocus;

    public MyTreeView()
    
        DefaultStyleKey = typeof(MyTreeView);
    

    public override void OnLostFocus(object sender, RoutedEventArgs e)
    
        e.Handled = true;
        if (SomeItemLostFocus != null)
            SomeItemLostFocus();
    

如果你想自定义外观,你应该覆盖控件的默认样式。此样式应在 Themes 文件夹内的 generic.xaml 文件中可用。有关自定义控件开发的更多信息是here。

     <Style TargetType="x:Type TreeViewItem">
        <Setter Property="IsExpanded"
                Value="Binding IsExpanded, Mode=TwoWay" />
        <Setter Property="IsSelected"
                Value="Binding IsSelected, Mode=TwoWay" />
    </Style>

【讨论】:

你看到问题了吗?因为,你几乎只是复制粘贴了我的代码 没有。您的代码是用户控件。我的代码是自定义控件。您的代码与 XAML 一起具有部分类。我的代码具有从 TreeView 扩展的独立类。我的代码不会抛出 NameScope 错误。 好的,但您的样式不包含&lt;EventSetter Event="LostFocus" Handler="EventSetter_OnHandler" /&gt;。这个字符串非常重要。如果Style 将与.cs 分开定义,我将无法使用EventSetter,因为它需要事件处理程序。 您可以使用 OnLostFocus 覆盖方法,而不是使用 Eventhandler。 只是不明白你的OnLostFocus 方法将如何被调用,当TreeViewITEM 的LostFocus 事件将上升时。它们之间的联系在哪里?【参考方案2】:

我找到了适合我的解决方案。这就是如何在代码中而不是在 XAML 中定义 TreeViewItemStyle 的方式。现在我只在代码隐藏中定义了TreeView,因此不会出现错误。

public class MyTreeView : TreeView

    public event RoutedEventHandler ItemLostLogicFocus;

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

        var itemContainerStyle = new Style
        
            TargetType = typeof(TreeViewItem),
        ;

        #region Binding

        var expandedBinding = new Binding("IsExpanded")
        
            Mode = BindingMode.TwoWay,
        ;

        var selectedBinding = new Binding("IsSelected")
        
            Mode = BindingMode.TwoWay,
        ;

        #endregion

        #region Setters

        itemContainerStyle.Setters.Add(new Setter
        
            Property = TreeViewItem.IsExpandedProperty,
            Value = expandedBinding
        );
        itemContainerStyle.Setters.Add(new Setter
        
            Property = TreeViewItem.IsSelectedProperty,
            Value = selectedBinding
        );

        #endregion

        #region EventSetters

        itemContainerStyle.Setters.Add(new EventSetter
        
            Event = LostFocusEvent,
            Handler = new RoutedEventHandler(ItemLostLogicFocusHandler)

        );

        #endregion

        ItemContainerStyle = itemContainerStyle;
    

    private void ItemLostLogicFocusHandler(Object sender, RoutedEventArgs e)
    
        e.Handled = true;
        if (ItemLostLogicFocus != null)
            ItemLostLogicFocus(sender, e);
    

【讨论】:

以上是关于在代码隐藏中指定 UserControl 的 .XAML 部分的主要内容,如果未能解决你的问题,请参考以下文章

如何创建一个接受在 XAML 中添加子元素的 UserControl

如何在 RestKit 0.20.x 中指定多任务支持?

js将表格中指定的行隐藏

用nodejs sequelize中指定的字段隐藏db中的一个结果。

有没有办法在 app.xaml 文件的样式中指定 OnPlatform ?

通过 WPF 中的代码隐藏访问资源