C#集合类ObservableCollection<T>
类似于泛型列表类List<T>,表示一个动态数据收集,该集合在添加或删除项或刷新整个列表时提供通知。
所在命名空间:System.Collections.ObjectModel
继承关系:
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
参考微软官方网站
ObservableCollection
有两个重要的事件
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected event PropertyChangedEventHandler PropertyChanged;
实现
INotifyCollectionChanged INotifyPropertyChanged
注解
在许多情况下,你使用的数据是 对象的集合。 例如,数据绑定中的常见方案是使用 ItemsControl (如 、 ListView或 TreeView )ListBox来显示记录集合。
你可以枚举实现 IEnumerable 接口的任何集合。 但是,若要设置动态绑定,以便集合中的插入或删除操作可以自动更新 UI,则集合必须实现 INotifyCollectionChanged 接口。 此接口公开 CollectionChanged 事件,只要基础集合发生更改,就会引发该事件。
WPF 提供 ObservableCollection<T> 类,该类是实现 INotifyCollectionChanged 接口的数据收集的内置实现。
在实现自己的集合前,请考虑使用 ObservableCollection<T> 或现有集合类之一,例如 List<T>、Collection<T> 和 BindingList<T> 等。 如果你有一个高级方案并且想要实现自己的集合,请考虑使用 IList,它提供可由索引单独访问的对象的非泛型集合。 实现 IList 提供数据绑定引擎的最佳性能。
注意
若要完全支持将数据值从绑定源对象传输到绑定目标,集合中支持可绑定属性的每个对象都必须实现适当的属性更改通知机制,例如 INotifyPropertyChanged 接口。
测试使用ObservableCollection
新建WPF应用程序DataGridDemo,在默认的MainWindow.xaml设计器中添加DataGrid和Button
MainWindow.xaml的设计器相关代码如下:
窗体绑定Loaded事件,按钮绑定Click事件
<Window x:Class="DataGridDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:DataGridDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Grid Margin="0,0,-49,0">
<DataGrid Name="dgTest" ItemsSource="{Binding}" AutoGenerateColumns="False" HorizontalAlignment="Left" Height="387" Margin="6,6,0,0" VerticalAlignment="Top" Width="699">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding EnglishName}" Width="120" Header="Mes名称"/>
<DataGridTextColumn Binding="{Binding FieldName}" Width="120" Header="字段名称"/>
<DataGridTextColumn Binding="{Binding MappingName}" Width="120" Header="映射名称"/>
<DataGridTextColumn Binding="{Binding ChineseName}" Width="120" Header="中文名称"/>
<DataGridTextColumn Binding="{Binding DataType}" Width="120" Header="数据类型"/>
<DataGridTextColumn Binding="{Binding Value}" Width="120" Header="当前值"/>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="btnSaveEdit" Content="保存编辑后的数据" HorizontalAlignment="Left" Margin="712,16,0,0" VerticalAlignment="Top" Width="101" Height="58" Click="btnSaveEdit_Click"/>
</Grid>
</Window>
新建测试绑定类ParametricData
ParametricData.cs如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataGridDemo
{
/// <summary>
/// 需要上传的某条生产数据,由name,value,dataType等组成
/// </summary>
public class ParametricData
{
/// <summary>
/// 中文名称,该上传数据项的描述
/// </summary>
public string ChineseName { get; set; }
/// <summary>
/// 英文名称,上传数据的键名。例如:SCGL1【输出功率1】
/// </summary>
public string EnglishName { get; set; }
/// <summary>
/// 数据表的列名或字段名,与EnglishName组成对应关系。用于手动上传出站数据时,从数据表查找数据绑定到指定的键上
/// </summary>
public string FieldName { get; set; }
/// <summary>
/// PLC地址表配置的英文名称,与EnglishName组成映射关系,用于获取实际的生产数据值
/// </summary>
public string MappingName { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public string DataType { get; set; }
/// <summary>
/// 当前值
/// </summary>
public string Value { get; set; }
}
}
MainWindow.xaml.cs相关处理代码如下
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DataGridDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
//List<ParametricData> ParametricDataList = new List<ParametricData>();
System.Collections.ObjectModel.ObservableCollection<ParametricData> ParametricDataList = new System.Collections.ObjectModel.ObservableCollection<ParametricData>();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
ParametricDataList.Add(new ParametricData()
{
EnglishName = $"TestData{i + 1}",
ChineseName = $"测试数据{i + 1}",
FieldName = $"TestData{i + 1}",
MappingName = $"TestData{i + 1}",
DataType = "数字",
Value = new Random(Guid.NewGuid().GetHashCode()).Next(1, 1000).ToString("D3"),
});
}
dgTest.DataContext = ParametricDataList;
}
private void btnSaveEdit_Click(object sender, RoutedEventArgs e)
{
int affectCount = 0;
for (int i = 0; i < dgTest.Items.Count; i++)
{
ParametricData parametricData = dgTest.Items.GetItemAt(i) as ParametricData;
if (parametricData == null || string.IsNullOrWhiteSpace(parametricData.EnglishName))
{
continue;
}
WriteLogCsv(AppDomain.CurrentDomain.BaseDirectory + "test.csv", parametricData);
affectCount++;
}
MessageBox.Show($"保存编辑后的数据成功,保存数据条数:【{affectCount}】");
}
/// <summary>
/// 写CSV日志 按列显示
/// </summary>
/// <param name="fileName">写入的csv文件名,全路径</param>
/// <param name="barcode">条码</param>
/// <param name="responseResult">MES请求与返回信息对象</param>
/// <returns></returns>
public static bool WriteLogCsv(string fileName, ParametricData parametricData)
{
try
{
string directoryPath = System.IO.Path.GetDirectoryName(fileName);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
string columnContents = "Mes名称,字段名称,映射名称,中文名称,数据类型,当前值\r\n";
if (!File.Exists(fileName))
{
File.AppendAllText(fileName, columnContents, Encoding.Default);
}
StringBuilder sb = new StringBuilder();
using (StreamWriter write = new StreamWriter(fileName, true, Encoding.Default))
{
sb.Append($"{parametricData.EnglishName},");
sb.Append($"{parametricData.FieldName},");
sb.Append($"{parametricData.MappingName},");
sb.Append($"{parametricData.ChineseName},");
sb.Append($"{parametricData.DataType},");
sb.Append($"{ProcessPunctuationForCsv(parametricData.Value)}");
write.WriteLine(sb.ToString());
}
return true;
}
catch (Exception ex)
{
MessageBox.Show("CSV文件写入失败:" + ex.Message);
return false;
}
}
/// <summary>
/// 处理csv文件中的双引号和逗号,使其在Excel中完美显示为一个单元格
/// </summary>
/// <param name="srcStr"></param>
/// <returns></returns>
private static string ProcessPunctuationForCsv(string srcStr)
{
if (srcStr == null)
{
return string.Empty;
}
bool quoteFlag = false;//是否添加过双引号
//如果存在双引号,需要将字符串的一个双引号 替换为 两个双引号。并且需要在字符串的前后加上双引号
if (srcStr.Contains("\""))
{
srcStr = srcStr.Replace("\"", "\"\"");
srcStr = "\"" + srcStr + "\"";
quoteFlag = true;
}
//如果只存在逗号(不存在引号),将前后加引号即可
if (srcStr.Contains(",") && !quoteFlag)
{
srcStr = "\"" + srcStr + "\"";
}
return srcStr;
}
}
}