【问题标题】:Unable to bind properties using custom controls in UWP in a ListView无法在 ListView 中使用 UWP 中的自定义控件绑定属性
【发布时间】:2020-01-24 14:49:00
【问题描述】:

我在 UWP 中有一个 ListView,它显示自定义控件列表 CustomControl。阅读周围我发现其他用户也面临类似的问题,他们的解决方案主要围绕设置他们的控件的DataContext,但我无法理解在我的示例中如何做到这一点。为了动态更新我在模型中使用DependencyProperties 的视图,如下所示:

    public class DataObject : DependencyObject
    {
        public string Name
        {
            get { return (string)GetValue(nameProperty); }
            set { SetValue(nameProperty, value); }
        }

        // Using a DependencyProperty as the backing store for name.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty nameProperty =
            DependencyProperty.Register("Name", typeof(string), typeof(DataObject), new PropertyMetadata("Name"));
    }

然后在我的主页中,我实现了以下逻辑来更改我的第三个元素的Name

    public sealed partial class MainPage : Page
    {
        private readonly ObservableCollection<DataObject> dataList;

        public MainPage()
        {
            this.InitializeComponent();
            this.dataList = new ObservableCollection<DataObject>();

            for (int i = 0; i < 5; i++)
            {
                DataObject dataObject = new DataObject();
                dataObject.Name = "Item " + i.ToString();

                this.dataList.Add(dataObject);
            }
            DataListView.ItemsSource = dataList;

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var obj = dataList.ElementAt(2);

            obj.Name = "Hello!";

        }
    }

主页的 XAML 如下:

<Page
    x:Class="ListViewTest.MainPage"
    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:controls="using:ListViewTest.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <ListView Name="DataListView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <controls:CustomControl DisplayName="{Binding Name}"></controls:CustomControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button Content="Button" HorizontalAlignment="Center" Height="92" Width="238"
                Click="Button_Click"/>
    </Grid>
</Page>

自定义控件CustomControl是这样的:

namespace ListViewTest.Controls
{
    public sealed partial class CustomControl : UserControl
    {


        public string DisplayName
        {
            get { return (string)GetValue(DisplayNameProperty); }
            set { 
                SetValue(DisplayNameProperty, value);
                DisplayText.Text = value;
            }
        }

        // Using a DependencyProperty as the backing store for DisplayName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DisplayNameProperty =
            DependencyProperty.Register("DisplayName", typeof(string), typeof(CustomControl), new PropertyMetadata("DisplayText"));


        public CustomControl()
        {
            this.InitializeComponent();
        }
    }

它的结构很简单:

    <Grid>
        <Button Name="ClickButton" Content="Button" Margin="171,165,0,0" VerticalAlignment="Top"/>
        <TextBlock Name="DisplayText" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center"/>
    </Grid>

问题是当我点击按钮时什么都没有发生,我很难理解为什么。

【问题讨论】:

    标签: c# xaml uwp


    【解决方案1】:

    DataObject 不应继承自 DependencyObject。应该定义为实现INotifyPropertyChanged的CLR对象:

    public class DataObject : INotifyPropertyChanged
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    此外,CustomControls 中依赖属性的 CLR 包装器的设置器应该设置依赖属性的值。您可以使用PropertyChangedCallback 设置TextBlock 的值:

    public sealed partial class CustomControl : UserControl
    {
        public string DisplayName
        {
            get { return (string)GetValue(DisplayNameProperty); }
            set { SetValue(DisplayNameProperty, value); }
        }
    
        public static readonly DependencyProperty DisplayNameProperty =
            DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(CustomControl), 
                new PropertyMetadata("DisplayText", new PropertyChangedCallback(OnChanged)));
    
        private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CustomControl customControl = (CustomControl)d;
            d.DisplayText = e.NewValue as string;
        }
    
        public CustomControl()
        {
            this.InitializeComponent();
        }
    }
    

    【讨论】:

    • 具有 DependentProperties 的 DependencyObject 不是 INotifyPropertyChanged 接口的“替代品”吗?
    • @daljit97:不完全是。数据对象不应依赖于DependencyObject。您通常会在 .NET Standard 类库中实现它们,然后从您的客户端应用程序中引用该类库。并且DataTemplates 可能不适用于DependencyObjects
    • 啊,我明白了,这是有道理的。
    • 好的,我已经检查过了,但是您对DependencyObject 继承不正确,事实证明,可以使用它来实现模型而不是实现INotifyPropertyChanged 接口。我的代码中的问题是我试图为 DependencyProperty 设置一个自定义设置器,这似乎是不可能的。因此,正如您所建议的,使用 PropertyChangedCallBack 可以工作(尽管我认为它作为解决方案并不是很优雅)。
    猜你喜欢
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    相关资源
    最近更新 更多