简体   繁体   English

如何从 xaml 中的 ItemTemplate 访问在代码中定义的用户控件的依赖项属性?

[英]How can I access a dependency property of a user control that was defined in code behind from a ItemTemplate in xaml?

I have a ListBox whose ListBoxItems are a control derived from UserControl.我有一个 ListBox,其 ListBoxItems 是从 UserControl 派生的控件。 Each of these controls contain a UserControl derived from Button and some TextBoxes.这些控件中的每一个都包含一个派生自 Button 的 UserControl 和一些 TextBox。 I want to bind these to elements in my model by using an ItemTemplate in the ListBox, but I'm having trouble assigning values to the button from the ItemTemplate.我想通过使用 ListBox 中的 ItemTemplate 将这些绑定到我模型中的元素,但是我无法从 ItemTemplate 为按钮分配值。

The ListViewer contains the ListBox and its DataTemplate: ListViewer 包含 ListBox 及其 DataTemplate:

<UserControl x:Class="TestListBoxBinding.ListViewer"
             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"
             xmlns:model="clr-namespace:Model"
             xmlns:vm="clr-namespace:ViewModel"
             xmlns:local="clr-namespace:TestListBoxBinding"
             mc:Ignorable="d">
    <UserControl.DataContext>
        <vm:ListViewerViewModel/>
    </UserControl.DataContext>
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type model:Person}">
            <Grid Name="theListBoxItemGrid">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <local:PersonButton Grid.Column="0" x:Name="thePersonButton"
                                    FirstName="Ken"
                                    LastName="Jones"/>
                <TextBlock Grid.Column="1" Name="theFirstName" Text="{Binding FirstName}" Margin="5,0,5,0"/>
                <TextBlock Grid.Column="2" Name="theLastName" Text="{Binding LastName}" Margin="0,0,5,0"/>
            </Grid>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Name="theHeader" Text="These are my people"/>
        <ListBox Grid.Row="1" Name="theListBox"
                 ItemsSource="{Binding MyPeople}"/>
        <StackPanel Grid.Row="2" Name="theFooterPanel" Orientation="Horizontal">
            <TextBlock Name="theFooterLabel" Text="Count = "/>
            <TextBlock Name="theFooterCount" Text="{Binding MyPeople.Count}"/>
        </StackPanel>
    </Grid>
</UserControl>

And its code behind:其背后的代码:

using Model;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace TestListBoxBinding
{
    /// <summary>
    /// Interaction logic for ListViewer.xaml
    /// </summary>
    public partial class ListViewer : UserControl
    {
        public ListViewer()
        {
            InitializeComponent();
        }
    }
}

namespace ViewModel
{
    public class ListViewerViewModel
    {
        // Properties

        public People MyPeople { get; private set; }

        // Constructors

        public ListViewerViewModel()
        {
            MyPeople = (People)Application.Current.Resources["theOneAndOnlyPeople"];
            MyPeople.Add(new Person("Able", "Baker"));
            MyPeople.Add(new Person("Carla", "Douglas"));
            MyPeople.Add(new Person("Ed", "Fiducia"));
        }
    }
}

namespace Model
{
    public class Person
    {
        // Properties

        public string FirstName { get; set; }
        public string LastName { get; set; }

        // Constructors

        public Person()
        {
            FirstName = "John";
            LastName = "Doe";
        }

        public Person(string firstName, string lastName)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
        }
    }

    public class People : ObservableCollection<Person>
    {
    }
}

The PersonButton has FirstName and LastName DependencyProperties defined in code behind that I will eventually use to set the button's contents for display. PersonButton 在后面的代码中定义了 FirstName 和 LastName DependencyProperties,我最终将使用它们来设置按钮的显示内容。 Here's its xaml:这是它的xaml:

<UserControl x:Class="TestListBoxBinding.PersonButton"
             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" 
             xmlns:local="clr-namespace:TestListBoxBinding"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button Name="theButton" Padding="5,0,5,0"/>
    </Grid>
</UserControl>

And its code behind:其背后的代码:

using System.Windows;
using System.Windows.Controls;

namespace TestListBoxBinding
{
    /// <summary>
    /// Interaction logic for PersonButton.xaml
    /// </summary>
    public partial class PersonButton : UserControl
    {
        // Properties

        public static readonly DependencyProperty FirstNameProperty = DependencyProperty.Register("FirstName", typeof(string), typeof(PersonButton));
        public string FirstName { get { return (string)GetValue(FirstNameProperty); } set { SetValue(FirstNameProperty, value); } }

        public static readonly DependencyProperty LastNameProperty = DependencyProperty.Register("LastName", typeof(string), typeof(PersonButton));
        public string LastName { get { return (string)GetValue(LastNameProperty); } set { SetValue(LastNameProperty, value); } }

        // Constructors
        public PersonButton()
        {
            InitializeComponent();
            FirstName = "Ira";
            LastName = "Jacobs";
            theButton.Content = FirstName + " " + LastName;
        }
    }
}

As you can see from the output:从输出中可以看出:

在此处输入图片说明

The attempts to set the FirstName and LastName properties of the PersonButton from the DataTemplate for the ListBox fail.尝试从 ListBox 的 DataTemplate 设置 PersonButton 的 FirstName 和 LastName 属性失败。 I set breakpoints on the property sets and they only hit when the controls were constructed, but never from the DataTemplate.我在属性集上设置了断点,它们只在构造控件时触发,但从未从 DataTemplate 触发。 I used the Live Visual Tree to confirm that the properties were, in fact, still set as they were initialized.我使用 Live Visual Tree 来确认这些属性实际上仍然在初始化时设置。

I should mention, too, that initially, I got an Intellisense warning that the properties were not accessible from the DataTemplate, but after I deleted the bin and obj folders, and cleaned and rebuilt the solution, that went away.我还应该提到,最初,我收到了一个 Intellisense 警告,指出无法从 DataTemplate 访问这些属性,但是在我删除 bin 和 obj 文件夹并清理并重建解决方案后,该警告就消失了。

Update更新

I believe this is not the same as the suggested duplicate in the comments.我相信这与评论中建议的重复不同。

As to PersonButton not making any sense, this whole collection of code is a greatly simplified representation of a piece of a much more complex application that I created simply to show the problem I'm having in a more easily read form.至于 PersonButton 没有任何意义,这整个代码集合是我创建的一个更复杂的应用程序的一个大大简化的表示,只是为了以更易于阅读的形式显示我遇到的问题。 I have tested this code and it duplicates the problem I'm having exactly.我已经测试了这段代码,它完全复制了我遇到的问题。 The actual control that PersonButton represents is much more complex and the use of properties defined in the code behind is entirely appropriate for that control. PersonButton 表示的实际控件要复杂得多,并且在后面的代码中定义的属性的使用完全适合该控件。 The only reason PersonButton is doing what it is doing is to make it easy to tell if those properties got set by the assignments. PersonButton 正在做它正在做的事情的唯一原因是可以很容易地判断这些属性是否由分配设置。

Now, to address my question here, it is, "Why are PersonButton.FirstName and PersonButton.LastName not getting changed by the their assignment statements for thePersonButton in the DataTemplate for theListBox in my ListViewer control?"现在,为了解决我的问题,“为什么 PersonButton.FirstName 和 PersonButton.LastName 没有被我的 ListViewer 控件中 ListBox 的 DataTemplate 中的 PersonButton 的赋值语句改变?”

As I mentioned before, I have run several tests, and they all verify that those values in the actual control are not getting set by the assignments.正如我之前提到的,我已经运行了几次测试,它们都验证了实际控件中的那些值没有被赋值设置。 Since seeing Clemens' reply, I have even implemented the PropertyChangedCallback functions, and they, too, confirm that the PersonButton properties are not getting set by the ListBox assignments.自从看到 Clemens 的回复后,我什至实现了 PropertyChangedCallback 函数,他们也确认了 PersonButton 属性没有被 ListBox 分配设置。

Of specific interest to me is that these assignments will become bindings themselves that allow me to capture changes to each individual item in the list in my Model through the ItemsSource mechanism.我特别感兴趣的是,这些分配本身将成为绑定,允许我通过 ItemsSource 机制捕获对模型中列表中每个单独项目的更改。 In my original application, those bindings work, but my problem is in actually assigning them to the PersonButton control.在我的原始应用程序中,这些绑定有效,但我的问题在于实际将它们分配给 PersonButton 控件。

I found the problem!我发现了问题! It has to do with dependency property value precedence.它与依赖属性值优先级有关。 When the same dependency property is set more than once in quick succession (close enough that they may interfere with each other) the system applies a precedence to their handling.当相同的依赖属性被快速连续设置多次(足够接近以至于它们可能会相互干扰)时,系统会对它们的处理应用优先级。 In essence locally set values are handled with higher priority than remotely set values.本质上,本地设置值的处理优先级高于远程设置值。

In this case, when the ListViewViewModel initializes the People collection in the Model, the property changed handler hits twice in rapid succession (often less than 1 ms): once when the Person is constructed and once when the Person is added to the collection (and, hence, the ListBox).在这种情况下,当 ListViewViewModel 初始化 Model 中的 People 集合时,属性更改处理程序快速连续(通常小于 1 毫秒)命中两次:一次是在构造 Person 时,一次是在将 Person 添加到集合时(和,因此,列表框)。

    public ListViewerViewModel()
    {
        MyPeople = (People)Application.Current.Resources["theOneAndOnlyPeople"];
        MyPeople.Add(new Person("Able", "Baker"));
        MyPeople.Add(new Person("Carla", "Douglas"));
        MyPeople.Add(new Person("Ed", "Fiducia"));
    }

Thus, they conflict with each other and the system gives precedence to the value set by the PersonButton's constructor, because it is local.因此,它们相互冲突,并且系统优先考虑由 PersonButton 的构造函数设置的值,因为它是本地的。

I commented out the assignment of the default values in the PersonButton constructor and, voila, the assignment from the ListBox worked.我注释掉了 PersonButton 构造函数中默认值的分配,瞧,来自 ListBox 的分配起作用了。

    public Person()
    {
        FirstName = "John";
        LastName = "Doe";
    }

This was a PITA to chase down, but now that I have the answer, it was also kind of fun.这是一个追击的PITA,但现在我有了答案,这也很有趣。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用XAML和后面的代码序列化WPF用户控件 - How can I serialize wpf user control with xaml and code behind 如何绑定到XAML代码隐藏中定义的属性 - How to Bind to a property defined in code-behind from XAML 从后面的代码访问XAML中的未命名控件 - Access unnamed control in xaml from code behind 如何在XAML中“创建”的Code Behind中访问控件 - How to access control in Code Behind that was 'created' in XAML 如何从 Generic.xaml 中定义的资源字典绑定到自定义控件的依赖属性? - How can I bind to dependency properties of a custom control from a resource dictionary defined in Generic.xaml? 我可以通过绑定到wpf父控件上的属性的依赖项属性在xaml中设置用户控件datacontext吗? - Can I set my user control datacontext in xaml via a dependency property bound to a property on the parent control in wpf? 如何从用户控件页面后面的代码在ListView的ItemTemplate中找到控件? - how to find control in ItemTemplate of the ListView from code behind of the usercontrol page? 如何通过使用后面的代码在Gridview的itemtemplate内的控件上添加只读属性? - How can I add readonly attribute at a control inside itemtemplate of Gridview by using code behind? 从后面的代码将控件(在xaml中定义)添加到堆栈面板 - Add a control (defined in xaml) to a Stack Panel from Code behind 在asp.net中,要启用/禁用图像按钮,如何在asp.net代码后面的文件中访问Gridview-&gt; Itemtemplate-&gt;面板的ImageButton控件 - In asp.net, To enable/disable imagebutton, How can I access ImageButton control of Gridview->Itemtemplate->panel in asp.net code behind file
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM