简体   繁体   English

如何绑定到 ListView 中的 model 而不是 WinUI3 中的属性?

[英]How to bind to a model in ListView instead of property in WinUI3?

I am trying to directly bind to a model ( similar to the situation here ), instead of the property on that model, but it is not working.我试图直接绑定到 model( 类似于这里的情况),而不是 model 上的属性,但它不起作用。 I previously asked a question about this , and the answer had worked in UWP. 我之前问过一个关于这个的问题,答案在 UWP 中有效。 I am now working in WinUI3 (packaged UWP) and am not sure if the same solution applies.我现在在 WinUI3(打包的 UWP)中工作,不确定是否适用相同的解决方案。

I have a class:我有一个 class:

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

I have a UserControl that displays that class:我有一个显示 class 的 UserControl:

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

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

With XAML:使用 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>

Now, I have a Window I am displaying that control on, inside of a list view:现在,我有一个 Window 我在列表视图中显示该控件:

<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>

And the code behind for the Window is: 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();
    }
}

When I run this code (which compiles and runs), I get a window with the expected number of items in the ListView (10), but they are all blank.当我运行此代码(编译并运行)时,我得到一个 window ,其中包含ListView中的预期项目数(10),但它们都是空白的。 There are also no XAML binding failures being reported (via the new feature in Visual Studio 2022).也没有报告 XAML 绑定失败(通过 Visual Studio 2022 中的新功能)。 In an attempt to investigate this, I added in a textblock inside of the listview (denoted as TEST 1 in the comments) bound to the Title property of the Message .为了对此进行调查,我在列表视图(在注释中表示为 TEST 1)内添加了一个文本块,该文本块绑定到MessageTitle属性。 This worked as expected, displaying the correct titles for the 10 messages.这按预期工作,显示了 10 条消息的正确标题。 I then added a single MessageControl to the Window outside of the ListView (denoted as TEST 2 in the comments) bound to a single Message property on the Window .然后,我将单个MessageControl添加到ListView之外的Window (在评论中表示为 TEST 2 )绑定到Window上的单个Message属性。 This also worked exactly as expected.这也完全按预期工作。

Am I missing something?我错过了什么吗? Why does the specific case of binding to a model directly (as opposed to a property on that model - ie a binding with no path) not work, when the data is there and works with other binding configurations?为什么直接绑定到 model 的特定情况(而不是 model 上的属性 - 即没有路径的绑定)不起作用,当数据在那里并且与其他绑定配置一起使用时?

Turns out this specific case exposes some of the ordering of how bindings work in XAML.事实证明,这个特定案例揭示了绑定在 XAML 中如何工作的一些顺序。 To understand what is happening here:要了解这里发生了什么:

  1. The Window is being initialized, which initializes its UI, including the ListView . Window正在初始化,它会初始化其 UI,包括ListView
  2. The Message models are then retrieved and added to the ObservableCollection .然后检索Message模型并将其添加到ObservableCollection中。
  3. When each Message is added to the ObservableCollection , the ListView using that ObservableCollection as its ItemsSource then creates a new Item using the ItemTemplate specified in the XAML, and then initializes and binds the newly initialized MessageControl to the Message .当每个Message添加到ObservableCollection时,使用该ObservableCollection作为其ItemsSourceListView然后使用 XAML 中指定的ItemTemplate创建一个新Item ,然后初始化并将新初始化的MessageControl绑定到Message The TEST 1 verifies this is happening. TEST 1 验证这正在发生。

The problem here is in step 3: The MessageControl s are initialized AND THEN bound to.这里的问题出在第 3 步: MessageControl被初始化然后绑定到。 When the binding happens, the UI is never notified that it needs to update the binding.绑定发生时,UI 永远不会收到需要更新绑定的通知。 This is why adding Mode=OneWay to the {x:Bind} doesn't fix the issue.这就是为什么将Mode=OneWay添加到{x:Bind}并不能解决问题的原因。 The OneWay mode tells the binding to update whenever the model updates... but the XAML is still never aware the model was updated. OneWay模式告诉绑定在 model 更新时更新……但 XAML 仍然不知道 model 已更新。

I am not entirely sure why this is different than the other two bindings (the TEST 1 and TEST 2 comments), but this seems to be an issue with user-defined types (classes) specifically, and NOT with primitive or built-in type properties (such as string and int ).我不完全确定为什么这与其他两个绑定(TEST 1 和 TEST 2 注释)不同,但这似乎是用户定义类型(类)的问题,而不是原始类型或内置类型属性(例如stringint )。

The fix is to enable the model to inform (or "notify") the UI that is has been updated.修复是让 model 通知(或“通知”)已更新的 UI。 This is done be implementing the INotifyPropertyChanged interface.这是通过实现INotifyPropertyChanged接口完成的。 Update the MessageControl to: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();
    }
}

A final note: implementing INotifyPropertyChanged MUST be done on the MessageControl for this to work.最后一点:必须在MessageControl上实现INotifyPropertyChanged才能使其正常工作。 Implementing INotifyPropertyChanged on the Message model would allow for the MessageControl (or any other data bound UI, such as the commented TEST 1) to update if a particular property of the Message model updates, but does not fix the issue of the Message model iself on the MessageControl being updated. Implementing INotifyPropertyChanged on the Message model would allow for the MessageControl (or any other data bound UI, such as the commented TEST 1) to update if a particular property of the Message model updates, but does not fix the issue of the Message model iself on MessageControl正在更新。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM