WinUI中ListView重置后TextBox文本残留问题咨询
我在目标框架为net6.0-windows10.0.19041.0的WinUI 3应用中,使用了包含TextBox的ListView,其ItemsSource绑定至后台代码中的ObservableCollection。列表后方设有按钮,用于清空集合并添加新项。
异常现象:在TextBox中输入内容后,点击清除按钮,文本仍会保留。预期应为ListView生成全新的空白TextBox。
请问该现象的原因是什么?是否与Virtualization(虚拟化)有关?
相关代码
XAML文件
<Window x:Class="WinUISample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WinUISample" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <ListView ItemsSource="{x:Bind _items }" > <ListView.ItemTemplate> <DataTemplate x:DataType="x:String"> <TextBox Width="250" /> <!-- intentionally empty --> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button Click="myButton_Click">Clear</Button> </StackPanel> </Window>
后台代码文件
using Microsoft.UI.Xaml; using System.Collections.ObjectModel; namespace WinUISample { public sealed partial class MainWindow : Window { private readonly ObservableCollection<string> _items; public MainWindow() { InitializeComponent(); _items = new ObservableCollection<string>(new[] { "1" }); } private void myButton_Click(object sender, RoutedEventArgs e) { _items.Clear(); _items.Add("1"); } } }
原因分析
这个现象确实和ListView的虚拟化机制直接相关。
WinUI 3的ListView默认开启虚拟化(默认使用VirtualizingStackPanel作为ItemsPanel),核心逻辑是复用UI元素以提升性能:当集合项被移除后,对应的ListItem容器不会立即销毁,而是被放入回收池;后续添加新项时,系统会直接复用这些已有的容器,而非创建全新的控件。
你的代码里,TextBox没有和集合项(string类型)做任何绑定,只是一个独立的输入控件。清空集合再添加新项时,ListView复用了之前的ListItem容器,里面的TextBox自然会保留之前输入的内容——毕竟没有绑定关系来同步新项的值(就算新项是"1",TextBox也没和这个值关联)。
顺带提一句:如果关闭虚拟化,这个问题就不会出现,因为此时ListView会为每个项创建全新的容器,更新集合时会生成新的空白TextBox。但虚拟化是默认行为,所以才会触发这个复用问题。
解决方法
有两种常用的修复方式:
方式一:给TextBox添加数据绑定
让TextBox的Text属性和集合项绑定,这样集合更新时,Text会同步为新项的值。修改DataTemplate:
<DataTemplate x:DataType="x:String"> <TextBox Width="250" Text="{x:Bind Mode=OneWay}" /> </DataTemplate>
如果需要点击按钮后TextBox是空白的,把添加的项改成空字符串即可:
private void myButton_Click(object sender, RoutedEventArgs e) { _items.Clear(); _items.Add(string.Empty); }
方式二:禁用ListView虚拟化
如果你的列表项数量很少,不需要虚拟化的性能优化,可以直接禁用:
<ListView ItemsSource="{x:Bind _items}" ScrollViewer.IsVirtualizing="False"> <!-- 原有ItemTemplate不变 --> </ListView>
这样每次更新集合时,ListView都会创建全新的容器,TextBox自然是空白的。
内容的提问来源于stack exchange,提问作者Gabriel S.

