【问题标题】:Use UserControl in ListView UWP, Databinding Issue在 ListView UWP 中使用 UserControl,数据绑定问题
【发布时间】:2024-01-12 07:07:01
【问题描述】:

我想创建一个应用程序来管理各种项目,所以我创建了一个类来代表这些项目。为了显示它们,我创建了一个用户控件,该控件应在随后显示,然后还管理用户和项目对象之间的交互。 我试图在页面中显示项目列表,但它不起作用。 我不确定在数据模板中绑定什么,我尝试过的方法都没有。 我还使用了实时可视化树,我认为 ProjectViewer 的属性项目设置正确,但教科书仍然没有显示项目名称。 (但我对实时视觉树不太熟悉,所以我可能是错的)。 如果我将用户控件用作单个实例而不是在列表中,我可以设置项目属性并显示名称。 我认为这是 DataBinding 的问题,但我无法解决。 任何帮助将不胜感激,因为这只是应用程序的第一步,我已经陷入困境。

这是我为重现问题而创建的一个最小示例:

我有一个班级项目:

using System.Collections.Generic;
using Windows.UI.Xaml;

namespace ProjectTimeTracker
{
    public class Project:DependencyObject
    {
        public string ProjName
        {
            get { return (string)GetValue(ProjNameProperty); }
            set { SetValue(ProjNameProperty, value); }
        }
        public static readonly DependencyProperty ProjNameProperty =
                DependencyProperty.Register(nameof(ProjName), typeof(string), typeof(Project), new PropertyMetadata("TestName"));

        public Project(){}
    }
}

我创建了 UserControl ProjectViewer:

namespace ProjectTimeTracker.UI
{
    public sealed partial class ProjectViewer : UserControl
    {
        public Project Project
        {
            get { return (Project)GetValue(ProjectProperty); }
            set { SetValue(ProjectProperty, value); }
        }
        public static readonly DependencyProperty ProjectProperty =
                DependencyProperty.Register(nameof(Project), typeof(Project), typeof(ProjectViewer), new PropertyMetadata(null));


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

其中有以下 xaml 代码:

<UserControl
    x:Class="ProjectTimeTracker.UI.ProjectViewer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ProjectTimeTracker.UI"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="100"
    d:DesignWidth="400">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="38"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <RadioButton x:Name="RBSelector" HorizontalAlignment="Left" Margin="8,0,0,0" VerticalAlignment="Center" Grid.RowSpan="2"/>
        <TextBlock x:Name="TBName" Grid.Row="0" Grid.Column="1" Text="{x:Bind Project.ProjName}" VerticalAlignment="Center" HorizontalAlignment="Left" Style="{ThemeResource TitleTextBlockStyle}" Width="110"/>
        <TextBlock x:Name="TBTotalTime" Grid.Row="1" Grid.Column="1" Text="NYI" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{ThemeResource CaptionTextBlockStyle}" Margin="0,-4,0,0"/>

    </Grid>
</UserControl>

我尝试使用主页中的用户控件来创建项目列表

using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace ProjectTimeTracker
{
    public sealed partial class MainPage : Page
    {
        public List<Project> Projects
        {
            get { return (List<Project>)GetValue(ProjectsProperty); }
            set { SetValue(ProjectsProperty, value); }
        }
        public static readonly DependencyProperty ProjectsProperty =
                DependencyProperty.Register(nameof(Projects), typeof(List<Project>), typeof(MainPage), new PropertyMetadata(new List<Project>()));


        public MainPage()
        {
            this.InitializeComponent();

            Projects.Add(new Project() { ProjName="Projekt1"});
            list.ItemsSource = Projects;
        }
    }
}

xaml 如下所示:

<Page xmlns:UI="using:ProjectTimeTracker.UI" 
    x:Class="ProjectTimeTracker.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ProjectTimeTracker"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <ListView x:Name="list"  HorizontalAlignment="Center" VerticalAlignment="Center">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:Project">
                    <UI:ProjectViewer Project="{x:Bind }"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

【问题讨论】:

  • 您可能根本不使用 x:Bind to Project 属性,订阅 DataContextChanged 并在回调中设置属性,DataContext 已经引用了您的 Project 对象。为此目的使用现有的属性。
  • 我删除了Project="{x:Bind }",但现在当我将项目添加到项目列表时应用程序崩溃。这似乎是 ProjectViewer xaml 中与 ProjName 的数据绑定的问题。我收到以下错误:{Windows.UI.Xaml.UnhandledExceptionEventArgs} 异常:{System.NotImplementedException:方法或操作未实现。在 ProjectTimeTracker.UI.ProjectViewer.ProjectViewer_obj1_Bindings.ProcessBindings(Object item, Int32 itemIndex, Int32 phase, Int32& nextPhase)} 到目前为止我什至没有实现 DataContextChanged 处理程序

标签: c# xaml listview data-binding uwp


【解决方案1】:

在 ListViev UWP 中使用 UserControl,数据绑定问题

我已经修改了你的代码,请参考以下,

ProjectViewer.xaml.cs

public sealed partial class ProjectViewer : UserControl
{
    public ProjectViewer()
    {
        this.InitializeComponent();
    }

    public Project ProjectSource
    {
        get { return (Project)GetValue(ProjectSourceProperty); }
        set { SetValue(ProjectSourceProperty, value); }
    }

    public static readonly DependencyProperty ProjectSourceProperty =
        DependencyProperty.Register("ProjectSource", typeof(Project), typeof(ProjectViewer), new PropertyMetadata(0, new PropertyChangedCallback(CallBack)));

    private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var current = d as ProjectViewer;
        if (e.NewValue != e.OldValue)
        {
            current.TBName.Text = (e.NewValue as Project).ProjName;
        }
    }
}

public class Project
{
    public string ProjName { get; set; }

    public Project()
    {
    }
}

ProjectViewer.xaml

<RadioButton
    x:Name="RBSelector"
    Grid.RowSpan="2"
    Margin="8,0,0,0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    />
<TextBlock
    x:Name="TBName"
    Grid.Row="0"
    Grid.Column="1"
    Width="110"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Style="{ThemeResource TitleTextBlockStyle}"      
    />
<TextBlock
    x:Name="TBTotalTime"
    Grid.Row="1"
    Grid.Column="1"
    Margin="0,-4,0,0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Style="{ThemeResource CaptionTextBlockStyle}"
    Text="NYI"
    />

用法

public sealed partial class MainPage : Page
{
    public List<Project> Projects = new List<Project>();
    public MainPage()
    {
        this.InitializeComponent();     

        Projects.Add(new Project() { ProjName = "Projekt1" });       
        list.ItemsSource = Projects;
    }
    public string TestString { get; set; }
}

Xaml

<ListView
    x:Name="list"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"
    >
    <ListView.ItemTemplate>
        <DataTemplate >
            <local:ProjectViewer ProjectSource="{Binding }" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

【讨论】:

  • 非常感谢,这对我有用。我想我可以使用 DataContextChanged 事件来设置我的属性项目。但这现在有效。但是我很高兴,因为我有更多的数据绑定而不仅仅是 ProjName(只是想保持示例小)。我可以不再在我的 UserControl 中使用 Xaml DataBinding 并且必须在回调中手动执行吗?让我感觉有点奇怪的是,当我的 UserControl 没有在列表中使用时,它与 Databinding 一起工作得很好。如果有一种简单的方法仍然可以使用 DataBinding?这将使编码更清晰。 (至少在我看来)