2016-08-15 68 views
5

下面是从C#动态创建依赖选项列表的一般性尝试。当从pick1中选择值'A'时,pick2应该显示SecondaryRangeA中的值。带有验证和间接的C#Excel依赖选项表

这段代码几乎可以工作,但不是显示SecondaryRangeA,而是显示字面值'A'。

pick2.Validation.Add(XlDVType.xlValidateList, 
        XlDVAlertStyle.xlValidAlertStop, 
        XlFormatConditionOperator.xlBetween, 
        "=INDIRECT(\"A5\")"); 

当我打开excel后,它出口并修改数据验证它显示公式。

=INDIRECT("A5") 

如果我在Excel中手动修改公式以排除引号,它将按预期工作。

=INDIRECT(A5) 

当我修改下面的代码时,我得到一个异常。有任何想法吗?

pick2.Validation.Add(XlDVType.xlValidateList, 
        XlDVAlertStyle.xlValidAlertStop, 
        XlFormatConditionOperator.xlBetween, 
        "=INDIRECT(A5)"); 

例外:

System.Runtime.InteropServices.COMException was unhandled 
    ErrorCode=-2146827284 
    Message=Exception from HRESULT: 0x800A03EC 
Source="" 


StackTrace: 
    at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) 
    at Microsoft.Office.Interop.Excel.Validation.Add(XlDVType Type, Object AlertStyle, Object Operator, Object Formula1, Object Formula2) 
    at TestExcelValidation.Program.Main(String[] args) in C:\TFS\ExcelInterop\TestExcelValidation\Program.cs:line 44 
    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: 

完整的例子:

using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using Microsoft.Office.Interop.Excel; 

namespace TestExcelValidation 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string temporaryPath = Path.GetTempPath(); 
      string temporaryFile = Path.GetTempFileName(); 
      Application appl = new Application(); 
      appl.Visible = true; 
      Workbook workbook = appl.Workbooks.Open(temporaryFile, 0, true, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0); 
      Worksheet worksheet = (Worksheet)workbook.Worksheets.Add(); 



      List<string> primaryList = new List<string>(); 
      primaryList.Add("A"); 
      primaryList.Add("B"); 

      List<string> secondaryListA = new List<string>(); 
      secondaryListA.Add("A1"); 
      secondaryListA.Add("A2"); 
      secondaryListA.Add("A3"); 

      List<string> secondaryListB = new List<string>(); 
      secondaryListB.Add("B1"); 
      secondaryListB.Add("B2"); 
      secondaryListB.Add("B3"); 

      Range primaryRange = AddToExcelNamedRange(worksheet, primaryList, 'A', 1, "PrimaryRange"); 
      Range secondaryRangeA = AddToExcelNamedRange(worksheet, secondaryListA, 'B', 1, "A"); 
      Range secondaryRangeB = AddToExcelNamedRange(worksheet, secondaryListB, 'C', 1, "B"); 

      Range pick1 = worksheet.Range["A5"]; 
      pick1.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, "=PrimaryRange"); 
      Range pick2 = worksheet.Range["A6"]; 
      pick2.Validation.Delete(); 
      pick2.NumberFormat = "Text"; 
      pick2.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, "=INDIRECT(\"A5\")"); 
      pick2.Validation.InCellDropdown = true; 
      pick2.Validation.IgnoreBlank = true; 
     } 

     private static Range AddToExcelNamedRange(Worksheet worksheet, List<string> primaryList, char col, int row, string rangeName) 
     { 
      Range range = worksheet.Range[col.ToString() + row.ToString(), col.ToString() + primaryList.Count().ToString()]; 
      range.Name = rangeName; 
      foreach (string item in primaryList) 
      { 
       worksheet.Cells[row, col - 64] = item; 
       row++; 
      } 
      return range; 
     } 
    } 
} 

回答

3

我有一个解决办法,但我想知道为什么这是行不通的。我相信我会再次遇到这个问题。

这是英文的答案,上帝只知道其他两个人在说什么。

问题

添加级联经由C#(或VBA)使用验证在Excel下拉列表失败,收到COMException 0x800A03EC。

原因

它不工作的原因是因为实际上是空的。

让我告诉你我是如何工作的。我在Excel中注入了宏并运行它:

Range pick1 = worksheet.Range["A5"]; 
pick1.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, "=PrimaryRange"); 
Range pick2 = worksheet.Range["A6"]; 

StringBuilder sb = new StringBuilder(); 
sb.Append("Sub InsertCascadingDropDown()" + Environment.NewLine); 
sb.Append(" Range(\"A6\").Select" + Environment.NewLine); 
sb.Append(" With Selection.Validation" + Environment.NewLine); 
sb.Append("  .Delete" + Environment.NewLine); 
sb.Append("  .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= xlBetween, Formula1:=\"=INDIRECT(A5)\"" + Environment.NewLine); 
sb.Append("  .IgnoreBlank = True" + Environment.NewLine); 
sb.Append("  .InCellDropdown = True" + Environment.NewLine); 
sb.Append("  .ShowInput = True" + Environment.NewLine); 
sb.Append("  .ShowError = True" + Environment.NewLine); 
sb.Append(" End With" + Environment.NewLine); 
sb.Append("End Sub" + Environment.NewLine); 

//You need to add a COM reference to Microsoft Visual Basic for Applications Extensibility for this to work 
var xlmodule = workbook.VBProject.VBComponents.Add(Microsoft.Vbe.Interop.vbext_ComponentType.vbext_ct_StdModule); 

xlmodule.CodeModule.AddFromString(sb.ToString()); 
appl.Run("InsertCascadingDropDown"); 

这会导致运行时错误“1004”当宏执行行添加验证:

.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:="=INDIRECT(A5)" 

enter image description here

所以这让我相信选择对象没有被定义(或者更多的是它是空的,这取决于你如何解释单词选择)。

解决方案

我这个发挥各地,并最终停止了代码控制和手动添加的验证,当我发现这一点:

enter image description here

源目前的计算结果为错误

这是一个吸烟枪,选择对象不为空,它是A5下拉列表的选择/值是事实上是空的!

添加级联下拉列表需要其父项有一个值!

因此,这是所有你需要做的:

pick1.Value2 = "A"; //< set the parent to have a value 
pick2.Validation.Delete(); //<- this is not really needed, unless you run this again 
pick2.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, "=INDIRECT(A5)"); 
+0

这并获得成功! –

0

至少在Excel 2007和2010,=Indirect(A5)不带引号评估在小区中使用时#REF。我在想如何从C#传递的代码与异常有关(因为它会计算出错误)。另外,手动创建Validation时使用相同的函数也会给出评估为excel错误的消息。编辑:虽然我在这里更多的研究纠正了一些观点。

来自Indirect()的预期输入需要A1或R1C1格式的字符串值,而不是实际的单元格引用。除非,目标范围是单元格引用ex:A5是A1。

根据MSDN,Indirect()只会在文件打开时和内存中计算MSDN 151323。打开工作簿并更改验证列表并正确评估它并不意味着当C#中的代码运行时,错误不存在。

0

如果例如在A5值为B然后=INDIRECT(A5)相同=INDIRECT("B")其是相同=B这不是有效的公式或单元格引用。

=INDIRECT("A5")相同=A5