65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MVVM 增强 ListView 操作

2010年6月29日

CPOL

2分钟阅读

viewsIcon

45425

downloadIcon

1009

使用 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日:初始版本。
© . All rights reserved.