如何在WPF视图中正确绑定并显示ObservableCollection数组条目?
问题描述
我写了一个读取XML数据并存入ObservableCollection的程序,现在有两个存储string数组的ObservableCollection,希望在ListView的两列中分别展示这两个数组的数据。最初直接调用ListView的Add方法添加数据,虽能显示但无法对条目进行操作(如选择值计算平均值),于是尝试实现MVVM模式,但无法正确绑定字段,导致ObservableCollection新增条目时视图无法更新。
XAML代码
<views:MvxWpfView x:Class="Ui.MVVMMaPa.Views.HomeView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf" 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:local="clr-namespace:Ui.MVVMMaPa.Views" xmlns:core="clr-namespace:Ui.MVVMMaPa.Core" d:DataContext="{d:DesignInstance Type=core:HomeViewModel}" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <StackPanel> <TextBox Text="{Binding Text,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <Button Content="Browse" Command="{Binding BrowseCommand}" /> <ListBox Name="ListBox"> <TextBlock Name="TextBlockName" Text="{Binding ShortNameStringObservableList}" /> <TextBlock Name="TextBlockid" Text="{Binding ValueStringObservableList}"/> </ListBox> </StackPanel> </Grid> </views:MvxWpfView>
ViewModel代码
using Logic.MVVMMaPa.ListCreater; using MvvmCross.Commands; using MvvmCross.ViewModels; using System.Collections.Generic; using System.Collections.ObjectModel; namespace Ui.MVVMMaPa.Core { public class HomeViewModel : MvxViewModel { public DirectoryIterator directoryIterator = new DirectoryIterator(); private List<MachineparameterClass> machineparameterList = new List<MachineparameterClass>(); public IMvxCommand BrowseCommand => new MvxCommand(Browse); private ObservableCollection<string[]> shortNameStringObservableList = new ObservableCollection<string[]>(); public ObservableCollection<string[]> ShortNameStringObservableList { get { return shortNameStringObservableList; } set { shortNameStringObservableList = value; RaisePropertyChanged(() => nameof(ShortNameStringObservableList)); } } private ObservableCollection<string[]> valueStringObservableList = new ObservableCollection<string[]>(); public ObservableCollection<string[]> ValueStringObservableList { get => valueStringObservableList; set { valueStringObservableList = value; RaisePropertyChanged(() => nameof(ValueStringObservableList)); } } private void Browse() { Comparer comparer = new Comparer(directoryIterator.FileIterationOne(directoryIterator.DirectoryIteratorFunction())); machineparameterList = comparer.ListComparer(); for (int i = 0; i < machineparameterList.Count; i++) { ShortNameStringObservableList.Add(machineparameterList[i].getShortNames()); ValueStringObservableList.Add(machineparameterList[i].getValues()); } } } }
Model代码
using System.ComponentModel; namespace Logic.MVVMMaPa.ListCreater { public class MachineparameterClass : INotifyPropertyChanged { string pluginName; string pluginInstanceName; string[] shortNames; string[] values; string[] typesInInt; public event PropertyChangedEventHandler? PropertyChanged; void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); public string PluginName { get { return pluginName; } set { pluginName = value; } } public string PluginInstanceName { get { return pluginInstanceName; } set { pluginInstanceName = value; OnPropertyChanged(nameof(PluginInstanceName)); } } public string[] ShortNames { get { return shortNames; } set { shortNames = value; OnPropertyChanged(nameof(ShortNames)); } } public string[] Values { get { return values; } set { values = value; OnPropertyChanged(nameof(Values)); } } public string[] TypesInInt { get { return typesInInt; } set { typesInInt = value; OnPropertyChanged(nameof(TypesInInt)); } } public MachineparameterClass() { } public MachineparameterClass(MachineparameterClass machineparameterClass) { this.pluginName = pluginName; this.pluginInstanceName = pluginInstanceName; this.shortNames = shortNames; this.values = values; this.typesInInt = typesInInt; } public void SetProperty(string prop, string val) { } public MachineparameterClass(string pluginName, string pluginInstanceName, string[] shortNames, string[] values, string[] typesInInt) : base() { this.pluginName = pluginName; this.pluginInstanceName = pluginInstanceName; this.shortNames = shortNames; this.values = values; this.typesInInt = typesInInt; } public String getPluginName() { return this.PluginName; } public String getPluginInstanceName() { return this.PluginInstanceName; } public String[] getShortNames() { return this.ShortNames; } public String[] getValues() { return this.Values; } public String[] getTypes() { return this.typesInInt; } } }
解决方案
1. 重构ViewModel,使用单一集合存储完整模型
当前两个独立的ObservableCollection会导致数据关联混乱,直接使用ObservableCollection<MachineparameterClass>存储所有数据,因为MachineparameterClass本身已包含ShortNames和Values数组,更符合MVVM设计逻辑:
// 替换原来的两个ObservableCollection private ObservableCollection<MachineparameterClass> _machineParameters = new ObservableCollection<MachineparameterClass>(); public ObservableCollection<MachineparameterClass> MachineParameters { get => _machineParameters; set { _machineParameters = value; RaisePropertyChanged(nameof(MachineParameters)); } } private void Browse() { Comparer comparer = new Comparer(directoryIterator.FileIterationOne(directoryIterator.DirectoryIteratorFunction())); var tempList = comparer.ListComparer(); // 清空旧数据避免重复 MachineParameters.Clear(); foreach (var param in tempList) { MachineParameters.Add(param); } }
2. 修正XAML布局,使用ListView+GridView实现两列展示
原来的ListBox绑定方式错误,改用ListView定义GridView列,通过数据模板展示数组内容:
<!-- 替换原ListBox --> <ListView ItemsSource="{Binding MachineParameters}" Margin="10"> <ListView.View> <GridView> <!-- 第一列:ShortNames数组 --> <GridViewColumn Header="短名称"> <GridViewColumn.CellTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding ShortNames}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" Margin="2"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <!-- 第二列:Values数组 --> <GridViewColumn Header="数值"> <GridViewColumn.CellTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding Values}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" Margin="2"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView>
3. 完善Model的属性通知
当前Model的PluginName属性未触发PropertyChanged事件,补上以确保属性变更能通知视图:
public string PluginName { get { return pluginName; } set { pluginName = value; OnPropertyChanged(nameof(PluginName)); } }
同时建议移除Model中的getter方法(如getShortNames()),直接使用属性访问,符合C#编码规范。
4. 确认绑定正确性
- MvvmCross通常会自动设置View的DataContext,确保导航逻辑正确;
- ObservableCollection新增元素时会自动通知视图更新,无需额外调用
RaisePropertyChanged(仅替换整个集合时需要)。
内容的提问来源于stack exchange,提问作者smeikey
相关产品推荐
相关产品推荐

