简体   繁体   English

VSTO-Excel-自动筛选-试图获取日期筛选条件(年,月或日)引发异常

[英]VSTO - Excel - AutoFilter - Trying to get Date filter criteria (year, month or day) throws exception

I'm trying to get the filters of a table in order to reapply them after some modifications. 我正在尝试获取表的过滤器,以便在进行一些修改后重新应用它们。 Everything is ok, but the trouble begins when the filter is on a Date column. 一切正常,但是当过滤器位于“日期”列上时,麻烦就开始了。 This is the way I'm doing it: 这就是我的做法:

for (int i = 1; i <= filters.Count; i++)
{
  FilterTemp f = new FilterTemp();
  f.On = filters[i].On;

  if (f.On)
  {
    f.Field = i;

    try
    {
      f.Criteria1 = filters[i].Criteria1;
    }
    catch { }

    f.Operator = (int)filters[i].Operator;

    try
    {
      f.Criteria2 = filters[i].Criteria2;
    }
    catch { }
  }

  fs.Add(f);
}

When the filter is on a text or number column, everything works beautifully, but when a date column is filtered by year, month or day, I get an exception on line 5 when trying to get the "Criteria1". 当过滤器位于text或number列上时,一切正常,但是当按年,月或日过滤日期列时,尝试获取“ Criteria1”时在第5行出现异常。

I tried to change the operator to xlFilterDynamic, as mentioned on an answer of this MSDN post: https://social.msdn.microsoft.com/Forums/vstudio/en-US/15ec8d69-3e6f-450d-82c0-ca53e63c8f64/getting-data-of-list-object-filters-for-date-column?forum=vsto 我试图将运算符更改为xlFilterDynamic,如对此MSDN帖子的答案所述: https ://social.msdn.microsoft.com/Forums/vstudio/en-US/15ec8d69-3e6f-450d-82c0-ca53e63c8f64/getting 论坛列表对象过滤器的数据?论坛= vsto

Something like this: 像这样:

for (int i = 1; i <= filters.Count; i++)
{
  FilterCache f = new FilterCache();
  f.On = filters[i].On;

  if (f.On)
  {
    f.Field = i;

    try
    {
      f.Criteria1 = filters[i].Criteria1;
    }
    catch
    {
      filters[i].Operator = XlAutoFilterOperator.xlFilterDynamic;

      f.Criteria1 = filters[i].Criteria1;
    }

    f.Operator = (int)filters[i].Operator;

    if (f.Operator == 0)
      f.Operator = (int)XlAutoFilterOperator.xlAnd;

    try
    {
      f.Criteria2 = filters[i].Criteria2;
    }
    catch { }
  }

  fs.Add(f);
}

No success. 没有成功 The filters[i].Criteria1 is now returning 1 forever, it doesn't matter the filter I use on that date column. 现在filter [i] .Criteria1永远返回1,我在该日期列上使用的过滤器都没有关系。

In order to simulate this issue, it is necessary to create a table on an Excel worksheet and then put some random dates in a column. 为了模拟此问题,有必要在Excel工作表上创建一个表,然后将一些随机日期放在一列中。 Then, filter this column selecting at least 3 different dates. 然后,过滤此列,至少选择3个不同的日期。 Run the code. 运行代码。

There's already a post about this on stackoverflow: Excel VSTO - accessing AutoFilter's Array Criteria throws exceptions 在stackoverflow上已经有关于此的帖子: Excel VSTO-访问AutoFilter的数组条件会引发异常

Also at: https://social.msdn.microsoft.com/Forums/office/en-US/281fdbc5-6535-497f-b427-f69f4b092e24/excel-vsto-accessing-autofilters-array-criteria-throws-exceptions 同样在: https : //social.msdn.microsoft.com/Forums/office/en-US/281fdbc5-6535-497f-b427-f69f4b092e24/excel-vsto-accessing-autofilters-array-criteria-throws-exceptions

... But there are no satisfactory answers or maybe it is a little bit difficult to understand the question. ...但是没有令人满意的答案,或者可能很难理解这个问题。

FilterTemp class: FilterTemp类:

public class FilterTemp
{
  public bool On;
  public object Field;
  public object Criteria1;
  public int Operator;
  public object Criteria2;
}

So this is just an idea The ultimate goal is to return the date back to client in a way that it can be re-applied via AutoFilter like here 因此,这只是一个主意 。最终目标是将日期返回给客户端,以便可以通过AutoFilter重新应用该日期,如下所示

string[] FilterList = new string[] { "10/31/2013", "5/4/2013" };
visibleCells.AutoFilter(1, FilterList.Length > 0 ? FilterList.ToArray() : Type.Missing, Excel.XlAutoFilterOperator.xlFilterValues, Type.Missing, true);

Hopefully this will give you a hint how to continue. 希望这会给您提示如何继续。 I'll try to finalize it some day. 我将有一天尝试完成。 All code with example on GitHub GitHub上所有带有示例的代码

using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Excel = Microsoft.Office.Interop.Excel;
using System.IO;

namespace AutoFilterRetriever
{
    /// <summary>
    /// An example class how to get AutoFilter criteria from internal XML representation
    /// </summary>
    public class CriteriaFilterRetriever : IDisposable
    {
        private readonly Excel.Worksheet wks;
        private readonly string filePath;
        private Stream docStream;
        private SpreadsheetDocument openXmlDoc;
        private WorkbookPart wkb;        

        public CriteriaFilterRetriever(Excel.Worksheet sheet)
        {
            wks = sheet;
            filePath = sheet.Application.ActiveWorkbook.FullName;
            if (!filePath.Contains("\\"))
            {
                throw new FileLoadException("Save the file in order to get autofilter criteria");
            }
        }

        /// <summary>
        /// This can be changed to a complex object instead of just list of strings
        /// </summary>
        public List<string> FilterCriteria { get; private set; }

        public void GetFilterCriteria()
        {
            if (!OpenFile()) throw new FileLoadException($"Couldn't open the file - {filePath}");
            if (wks.AutoFilter == null) return;

            // here we get sheet in the workbook.xml (Equals don't work here)
            var sheetInWkb = wkb.Workbook.Descendants<Sheet>().Where(s => s.Name == wks.Name).FirstOrDefault();
            // get a reference to the worksheet part. Imagine part as the folder in the zip structure
            WorksheetPart wsPart = (WorksheetPart)(wkb.GetPartById(sheetInWkb.Id));
            // finally get the xml file e.g. sheet1.xml
            var sheet = wsPart.Worksheet;
            // there should be just one autofilter per sheet
            var filter = sheet.Descendants<AutoFilter>().First();
            if (filter == null) throw new InvalidOperationException($"Couldn't get autofilter data from the {wks.Name} sheet.");
            ManageFilterData(filter);
        }

        private void ManageFilterData(AutoFilter filter)
        {
            FilterCriteria = new List<string>();
            // this is always the first element in AutoFilter
            foreach (FilterColumn filterCol in filter)
            {
                // here we get the filters data
                var filters = filterCol.FirstChild;
                if (filters is Filters)
                {
                    foreach (var item in filters)
                    {
                        if (item is DateGroupItem)
                        {
                            FilterCriteria.Add(GetDateFilterCriteria(item as DateGroupItem));
                        }
                        else if (item is Filter)
                        {
                            FilterCriteria.Add(((Filter)item).Val);
                        }
                        else
                        {
                            throw new Exception("Not sure what to do here");
                        }
                    }
                }
                else if (filters is CustomFilters)
                {
                    // if custom filter is applied (more than one criteria it falls to this category
                    foreach (var item in filters)
                    {
                        if (item is CustomFilter)
                        {
                            var tmp = item as CustomFilter;                            
                            FilterCriteria.Add($"{tmp.Operator}, {tmp.Val}");
                        }                        
                        else
                        {
                            throw new Exception("Not sure what to do here");
                        }
                    }
                }
            }
        }

        private string GetDateFilterCriteria(DateGroupItem criteria)
        {
            if (criteria.DateTimeGrouping == DateTimeGroupingValues.Year)
            {
                return criteria.Year.ToString();
            }
            else if (criteria.DateTimeGrouping == DateTimeGroupingValues.Month)
            {
                return $"{criteria.Year.ToString()}-{criteria.Month.ToString()}";
            }
            else if (criteria.DateTimeGrouping == DateTimeGroupingValues.Day)
            {
                return $"{criteria.Year.ToString()}-{criteria.Month.ToString()}-{criteria.Day.ToString()}";
            }
            else if (criteria.DateTimeGrouping == DateTimeGroupingValues.Hour)
            {
                return $"{criteria.Year.ToString()}-{criteria.Month.ToString()}-{criteria.Day.ToString()} {criteria.Hour.ToString()}:00:00";
            }
            else if (criteria.DateTimeGrouping == DateTimeGroupingValues.Minute)
            {
                return $"{criteria.Year.ToString()}-{criteria.Month.ToString()}-{criteria.Day.ToString()} {criteria.Hour.ToString()}:{criteria.Minute.ToString()}:00";
            }
            else
            {
                return $"{criteria.Year.ToString()}-{criteria.Month.ToString()}-{criteria.Day.ToString()} " +
                       $"{criteria.Hour.ToString()}:{criteria.Minute.ToString()}:{criteria.Second.ToString()}";
            }
        }

        /// <summary> Opens the given file via the DocumentFormat package </summary>
        /// <returns></returns>
        private bool OpenFile()
        {
            try
            {
                docStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                openXmlDoc = SpreadsheetDocument.Open(docStream, false);
                wkb = openXmlDoc.WorkbookPart;                
                return true;
            }
            catch (Exception)
            {                
                return false;
            }
        }

        public void Dispose()
        {
            openXmlDoc?.Close();
            docStream?.Close();
        }

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM