0

我正在尝试直接绑定到模型(类似于此处的情况),而不是该模型上的属性,但它不起作用。我之前问过一个关于这个的问题,答案在 UWP 中有效。我现在在 WinUI3(打包的 UWP)中工作,不确定是否适用相同的解决方案。

我有一堂课:

public class Message
{
    public string Title { get; set; }
    public string Content { get; set; }
}

我有一个显示该类的 UserControl:

public sealed partial class MessageControl : UserControl
{
    /// <summary>
    /// The message to display.
    /// </summary>
    public Message Message { get; set; }

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

使用 XAML:

<UserControl
    x:Class="MessageClient.Controls.EmailControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MessageClient.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <StackPanel>
            <!-- Displays the title of the message. -->
            <TextBlock Name="HeaderTextBlock"
                       Text="{x:Bind Message.Title, Mode=OneWay}"></TextBlock>
        
            <!-- Displays the content of the message. -->
            <TextBlock Name="ContentPreviewTextBlock"
                       Text="{x:Bind Message.Content, Mode=OneWay}"></TextBlock>
        </StackPanel>
    </Grid>
</UserControl>

现在,我有一个在列表视图内显示该控件的窗口:

<Window
    x:Class="MessageClient.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:EmailClient"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:controls="using:MessageClient.Controls" xmlns:models="using:MessageClient.Models"
    mc:Ignorable="d">

    <Grid>
        <!-- The list of messages. -->
        <ListView x:Name="MessagesListView"
                  ItemsSource="{x:Bind Messages}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="models:Message">
                    <controls:MessageControl Margin="5"
                                           Message="{x:Bind Mode=OneWay}"></controls:MessageControl>

                    <!-- TEST 1 -->
                    <!--<TextBlock Text="{x:Bind Title}"></TextBlock>-->
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <!-- TEST 2 -->
        <!--<controls:MessageControl Message="{x:Bind TestMessage, Mode=OneWay}"></controls:MessageControl>-->
    </Grid>
</Window>

Window 背后的代码是:

public sealed partial class MainWindow : Window
{
    public ObservableCollection<Message> Messages;
    public Message TestMessage;

    public MainWindow()
    {
        this.InitializeComponent();

        // Populate the test message.
        this.PopulateTestMessages();
    }

    private void PopulateTestMessages()
    {
        // The TestDataGenerator just returns a staticaly defined list of test messages.
        this.Messages = new ObservableCollection<Message>(TestDataGenerator.GetTestMessages(10));
        this.TestMessage = this.Messages.First();
    }
}

当我运行此代码(编译并运行)时,我得到一个窗口,其中包含ListView(10) 中的预期项目数,但它们都是空白的。也没有报告 XAML 绑定失败(通过 Visual Studio 2022 中的新功能)。为了对此进行调查,我在列表视图(在注释中表示为 TEST 1)内添加了一个文本块,该文本块绑定TitleMessage. 这按预期工作,显示了 10 条消息的正确标题。然后MessageControl,我WindowListView绑定MessageWindow. 这也完全按预期工作。

我错过了什么吗?当数据存在并与其他绑定配置一起使用时,为什么直接绑定到模型的特定情况(与该模型上的属性相反 - 即没有路径的绑定)不起作用?

4

1 回答 1

0

事实证明,这个特定案例揭示了绑定在 XAML 中如何工作的一些顺序。要了解这里发生了什么:

  1. Window正在初始化,这将初始化其 UI,包括ListView.
  2. 然后Message检索模型并将其添加到ObservableCollection.
  3. 当每个Message被添加到 时ObservableCollectionListView使用那个ObservableCollection作为它的然后使用 XAML 中指定的ItemsSource创建一个新的,然后初始化并将新初始化的绑定到. TEST 1 验证这正在发生。ItemItemTemplateMessageControlMessage

这里的问题出在第 3 步:MessageControls 被初始化然后绑定到。绑定发生时,UI 永远不会收到需要更新绑定的通知。这就是为什么添加Mode=OneWay{x:Bind}不能解决问题的原因。该OneWay模式告诉绑定在模型更新时更新......但 XAML 仍然永远不会知道模型已更新。

我不完全确定为什么这与其他两个绑定(TEST 1 和 TEST 2 注释)不同,但这似乎是用户定义类型(类)的问题,而不是原始类型或内置类型属性(例如stringint)。

修复是使模型能够通知(或“通知”)已更新的 UI。这是通过实现INotifyPropertyChanged接口完成的。更新MessageControl为:

public sealed partial class MessageControl : UserControl, INotifyPropertyChanged
{
    private Message _message;
    /// <summary>
    /// The message to display.
    /// </summary>
    public Message Message
    {
        get { return this._message; }
        set
        {
            this._message = value;
            this.RaisePropertyChanged("Message");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

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

最后一点:INotifyPropertyChanged必须在 上进行实施才能MessageControl使其正常工作。INotifyPropertyChanged在模型上实现Message将允许MessageControl(或任何其他数据绑定 UI,例如注释的 TEST 1)在Message模型的特定属性更新时进行更新,但不能解决Message模型本身在MessageControl更新时的问题。

于 2021-11-22T09:03:11.473 回答