使用 MVVM 增强 ListView 操作





5.00/5 (5投票s)
使用 MVVM 标准过滤和导出 ListView 数据到 Excel。
引言
本文介绍如何创建一个具有扩展功能的 ListView
,例如对其数据进行过滤和导出到 Excel,同时支持 MVVM 模式,并且 ListView.ItemsSource
的类型不会成为障碍,同时还能够确定每种类型如何进行过滤。
背景
我被分配了一项工作任务,即创建一个呈现一些数据的应用程序。这些数据需要以类似于 Windows 文件过滤的方式进行过滤。作为 MVVM 的坚定支持者,我希望我的代码干净、解耦,并且通用,以便也可以在其他情况下使用。
使用代码
由于 ListView
需要处理所有数据类型,我希望 ViewModel
决定如何进行过滤和导出。因此,我可以有一个学生详细信息的列表,通过学生的私人姓名进行过滤,以及一个汽车列表,仅通过制造商进行过滤。为了处理此任务,我创建了一个接口,ViewModel
必须实现该接口
/// <summary>
/// This is an iterface for every object which
/// can have its data filtered given a certain string
/// </summary>
public interface IEnhancedListObject
{
/// <summary>
/// Filter the data given a filter string
/// </summary>
/// <param name="strFilter"></param>
/// <returns></returns>
bool Filter(string strFilter);
/// <summary>
/// Write the Header into the excel sheet
/// </summary>
/// <param name="worksheet">The worksheet to use</param>
/// <param name="nRow">The row to start with</param>
/// <param name="nColumn">The column to start with</param>
/// <returns></returns>
void WriteHeaderIntoExcelSheet(Worksheet worksheet, ref int nRow, ref int nColumn);
/// <summary>
/// Write the data into an Excel worksheet
/// </summary>
/// <param name="worksheet">The worksheet to use</param>
/// <param name="nRow">The row to start with</param>
/// <param name="nColumn">The column to start with</param>
/// <returns></returns>
void WriteDataIntoExcelSheet(Worksheet worksheet, ref int nRow, ref int nColumn);
}
该接口包含三个方法
Filter
:决定元素是否通过给定的字符串。WriteHeaderIntoExcelSheet
:写入导出的数据的列。WriteDataIntoExcelSheet
:将实际数据写入 Excel 表格。
为了使我的代码尽可能健壮,我尝试避免在 Xaml.cs 文件中编写任何代码。为此,我使用了附加属性。
过滤
public class ListViewFilterExtension
{
/// <summary>
/// FilterSource Attached Dependency Property
/// </summary>
public static readonly DependencyProperty FilterSourceProperty =
DependencyProperty.RegisterAttached("FilterSource",
typeof(TextBox), typeof(ListViewFilterExtension),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnTextBoxSet)));
/// <summary>
/// Gets the FilterSource property.
/// </summary>
public static TextBox GetFilterSource(DependencyObject d)
{
return (TextBox)d.GetValue(FilterSourceProperty);
}
/// <summary>
/// Sets the FilterSource property.
/// </summary>
public static void SetFilterSource(DependencyObject d, TextBox value)
{
d.SetValue(FilterSourceProperty, value);
}
/// <summary>
/// Handles changes to the FilterSource property.
/// </summary>
private static void OnTextBoxSet(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ListView listView = d as ListView;
TextBox textBox = e.NewValue as TextBox;
if ((listView != null) && (textBox != null))
{
textBox.TextChanged += delegate(object sender,
TextChangedEventArgs tcea)
{
ICollectionView view =
CollectionViewSource.GetDefaultView(listView.ItemsSource);
view.Filter = null;
view.Filter = delegate(object obj)
{
IEnhancedListObject filterableObject = obj as IEnhancedListObject;
if (filterableObject == null) return false;
string textFilter = ((TextBox)sender).Text;
if (textFilter.Trim().Length == 0) return true;
// the filter is empty - pass all items
return filterableObject.Filter(textFilter.Trim());
};
};
}
}
}
过滤假定过滤器字符串将从 TextBox
中获取。附加属性将 TextBox
作为参数,并将其注册为 TextChanged
事件的侦听器。它注册的委托遍历 ListView.ItemSource
中的元素,假设它们正在实现 IEnhancedListObject
接口,并在它们上运行过滤操作。
导出到 Excel
public class ListViewExcelExporterExtension
{
/// <summary>
/// ExcelExporter Attached Dependency Property
/// </summary>
public static readonly DependencyProperty ExcelExporterProperty =
DependencyProperty.RegisterAttached("ExcelExporter",
typeof(ListView), typeof(ListViewExcelExporterExtension),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnExcelExportRequest)));
/// <summary>
/// Gets the ExcelExporter property.
/// </summary>
public static ListView GetExcelExporter(DependencyObject d)
{
return (ListView)d.GetValue(ExcelExporterProperty);
}
/// <summary>
/// Sets the ExcelExporter property.
/// </summary>
public static void SetExcelExporter(DependencyObject d, ListView value)
{
d.SetValue(ExcelExporterProperty, value);
}
/// <summary>
/// Handles changes to the FilterSource property.
/// </summary>
private static void OnExcelExportRequest(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
System.Windows.Controls.Button button = d as System.Windows.Controls.Button;
ListView listView = e.NewValue as ListView;
if ((button != null) && (listView != null))
{
button.Click += delegate(object sender, RoutedEventArgs er)
{
Microsoft.Office.Interop.Excel.Application application =
new Microsoft.Office.Interop.Excel.Application();
application.Visible = true;
// Workaround to fix MCST bug - The threads culture
// info must be en-US for the Excel sheet
// to open properly
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Workbook workbook = application.Workbooks.Add(Missing.Value);
Worksheet worksheet = workbook.ActiveSheet as Worksheet;
if (worksheet != null)
{
// Set the display to show from left to right
worksheet._DisplayRightToLeft = 0;
if (listView.Items.Count > 0)
{
// Write the header
IEnhancedListObject vm = listView.Items[0] as IEnhancedListObject;
int nRow = 1;
int nColumn = 1;
if (vm != null)
{
vm.WriteHeaderIntoExcelSheet(worksheet, ref nRow, ref nColumn);
}
// Write all the elements in the view model
foreach (var item in listView.Items)
{
vm = item as IEnhancedListObject;
vm.WriteDataIntoExcelSheet(worksheet, ref nRow, ref nColumn);
}
}
}
// Work around MSFT bug - return the culture info to the previous set
Thread.CurrentThread.CurrentCulture = currentCulture;
};
}
}
}
导出附加属性假定它将由 Button
使用。它将 ListView
作为其参数。它注册到 Button.Click
操作。单击后,它遍历 ListView.ItemsSource
,假设它们正在实现 IEnhancedListObject
接口,并使用该接口将数据添加到 Excel 表格中。
在示例中,我展示了 S&P 指数中 500 家公司的列表,我从 Wikipedia 获取的。
历史
- 2010年6月29日:初始版本。