菜鸟想写一个可以对CTAIS自动填表的软件,主要是WinForm+Webbrowser,目前已经解决了文本框和下拉框的自动填表,但碰到这个表格不知道该如何下手了,请教各位大虾,多谢!
写成这样不知道如何继续……
//填充表格
mshtml.IHTMLElementCollection FixRC;
FixRC = (mshtml.IHTMLElementCollection)doc.all.tags("datawindow");
mshtml.IHTMLElement FixRChwmc = (mshtml.IHTMLElement)FixRC.item("dw_dk_zb", 0);//货物名称
//后面写不下去了……
这是HTML源文件,如果需要xdatawindow.htc文件我可以上传:
<TABLE style="WIDTH: 100%; HEIGHT: 30%" border=0>
<TBODY><TR><TD style="WIDTH: 100%; HEIGHT: 100%">
<ctais:datawindow id=dw_dk_zb style="WIDTH: 100%; HEIGHT: 100%" node="DSO_FP/FP_DK_FP_ZB" autoAddRow="true">
<COLUMN id=HWMC fill="yes" colwidth="6" caption="货物名称" type="INPUT" />
<COLUMN id=GGXH colwidth="2" caption="规格型号" type="INPUT" />
<COLUMN id=JLDW colwidth="2" caption="计量单位" type="INPUT" />
<COLUMN id=HWSL fill="yes" onchange="HWSL_onchange(this)" colwidth="3" caption="货物数量" type="INPUT" preset="number(10,8)" />
<COLUMN id=DJ fill="yes" onchange="DJ_onchange(this)" colwidth="3" caption="含税单价" type="INPUT" preset="number(10,8)" />
<COLUMN id=JE onchange="JE_onchange(this)" colwidth="3" caption="含税金额" type="INPUT" preset="number(14,2)" total="yes" />
<COLUMN id=SL fill="yes" view="SL_MC" key="SL_DM" node="DSO_SL" onchange="SL_onchange(this)" colwidth="2" caption="税率" type="SELECT" />
<COLUMN id=SE colwidth="3" caption="税额" type="INPUT" preset="number(14,2)" total="yes" /></ctais:datawindow>
</TD></TR></TBODY></TABLE>
系统界面如下:

解决方案 »

  1.   

    按f12可以看到htc生成的dom结构,然后用GetElementById或其它查找方法找到需要的input元素。
      

  2.   

    htc是很古老的技术,只在IE中支持,并且IE10也不支持了。
    如果可能的话,不要用htc,改成其它js框架
      

  3.   

    菜鸟我实在不知道该怎样对这个表格的Cell进行赋值,求解……
      

  4.   

    对了还有,因为该页面跨域,我是用CodecentrixSample取得的IHTMLWindow2和IHTMLDocument2
      

  5.   

    你贴出来的图并不是生成的最终dom元素。
    点左上角的箭头(单击选择元素),在页面上的货物名称中点一下,再把看到的dom结构贴出来。
      

  6.   

    你好!我今天尝试用F12和IE WebDeveloper查看Dom元素,发现都无法查看此表格的最终Dom元素。详情请见此图
    请问是不是该表格是用JS动态创建就无法看得到?
    另外,我查看了下挂下该页面下的index.js,发现了这些相关的代码:
    1.
    xmlNodeCopy(frm_fpxx.DSO_FP,DSO_PTTD,"FP_DK_FP_ZB");
    2.这貌似是删除空行?
    for (var i=0;i<frm_fpxx.dw_dk_zb.rowCount;i++){
    if (frm_fpxx.dw_dk_zb.cell(i,0).value==null || frm_fpxx.dw_dk_zb.cell(i,0).value==""){
    frm_fpxx.dw_dk_zb.deleteRow(i);
    }
    }
    3.注释导出是乱码,抱歉……
    function   dso_bj(){
    if (frm_fpxx.dw_dk_zb.modified)
    {return  false;
    }
    for (var i=0;i<23 ;i++ )
    {  
    //DWԪУ
    var  fp_dso=frm_fpxx.DSO_FP.childNodes(0).childNodes(i).text;
    var  copy_dso=frm_fpxx.DSO_COPY.childNodes(0).childNodes(i).text;
    if (fp_dso!=copy_dso)
    {return  false;
    }

    }
        return  true;
    4.此处应该为税额和总计,分别在表格的第8列和第6列,结果保留两位小数
    var nSe=frm_fpxx.dw_dk_zb.col(7).total;
    DSO_PTTD.selectSingleNode("ROOT/DYNR/SEHJ").text=convertCN(round(nSe,2)); var  iNum=frm_fpxx.dw_dk_zb.rowCount;
    var sum=frm_fpxx.dw_dk_zb.col(5).total;
    DSO_PTTD.selectSingleNode("ROOT/DYNR/HJ_XX").text= round(sum,2);
    var dd=round(sum,2);
    DSO_PTTD.selectSingleNode("ROOT/DYNR/HJ_DX").text=convertCN(dd);
    5.此处貌似是金额合计,有如下代码:(请问如何设置"frm_fpxx.dw_dk_zb.cell(i,5).value"?)
            var oDoc=loadXml(arg);
        je =round(oDoc.documentElement.selectSingleNode("JEHJ").text,2);
    var  iNum=frm_fpxx.dw_dk_zb.rowCount;
    for (var i=0; i < iNum; i++)
    {
    jehj += round(frm_fpxx.dw_dk_zb.cell(i,5).value,2);
    }
      

  7.   

    你把htc和页面贴出来看下,或者打包传到云盘我下载看下
      

  8.   

    非常感谢jshi123大大!周末不先休息一下吗?
    因为是单位的内网,所以页面暂时没办法贴出来了……
    HTC文件在此:http://pan.baidu.com/share/link?shareid=148816522&uk=2785352337
      

  9.   

    我看了下这个文件,里面用到了一个htcDatawindow的ActiveX控件(在文件的最后)。
    这个htc中的大部分函数,都是对htcDatawindow功能的简单封装,即htc本身没有实际地进行处理,而是调用了htcDatawindow的相应功能。
    这就意味着,页面上显示的可能不是html元素,而是由这个ActiveX呈现的UI界面。这样用f12确实就定位不到元素,因为根本就不存在,同时也意味着用定位html元素来填充表格的做法是行不通的。
      

  10.   

    当然办法还是有的,可以通过其它方式实现自动填表的要求。
    好消息是,这个htc封装和公开了比较完整的htcDataWindow的功能,所以你可以直接操作这个datawindow对象来对表格进行处理。
    比如htc公开了setCellValue方法:
      function setCellValue(rowIndex,colIndex,value,bRaiseEvent,bRecordChangeModified)
    你可以试试在页面上写一个js:
      document.getElementById('dw_dk_zb').setCellValue(0,0,'test');
    看看能不能把“test”填到表格中。
      

  11.   

    上面这个setCellValue方法中的参数你可能需要多试验几次才能知道和ui上的单元格的位置的对应关系。
    htc中还有其它方法,也可以通过试验来确定它的功能,大部分方法看名字也能大概猜到它的功能。比如:
    addRow应该是添加一行,htcDatawindow.cmdSave应该对应保存的功能。
      

  12.   

    请教jshi123大大,我发现HTC文件最后有关于kb912945.js的部分,虽然不太明白……
    这里是kb912945.js文件,貌似直接有载入xdatawindow.htc的方法?
    http://pan.baidu.com/share/link?shareid=3776759826&uk=2785352337
    请问是否有办法直接调用xdatawindow.htc中的setCellValue函数?
    如果要在Webbrowser中添加JS,使用这样的语句是否可行?
    IHTMLDocument2.createElement("<script>……</script>");
    再写什么JS貌似又是一个对我这菜鸟来说有难度的问题了……
      

  13.   

    这个kb是针对IE6激活ActiveX的一些处理,跟你要做的事情没有关系。你现在是不是无法找到dw_dk_zb这个元素?你的程序怎么写的?
    你能直接在html页面上加js吗?
      

  14.   

    呃,我是这样写的,断点看貌似找到了datawindow所在的table,请大大鉴定
    PS:目前还不会怎样在html页面上加js……
                //需要调用的Frame
                IHTMLWindow2 win1 = (IHTMLWindow2)webBrowser2.Document.Window.Frames[1].Frames[0].DomWindow;
                //获得Iframe doc
                IHTMLDocument2 doc = CodecentrixSample.CrossFrameIE.GetDocumentFromWindow(win1);
                mshtml.IHTMLElementCollection Fix;
                Fix = (mshtml.IHTMLElementCollection)doc.all.tags("datawindow");
                mshtml.IHTMLElement FixDW = (mshtml.IHTMLElement)Fix.item("dw_dk_zb", 0);
      

  15.   

    CodecentrixSample.CrossFrameIE这个是什么?你自己写的类?还是网上的类库?这样吧,你想办法搭一个测试环境能够访问到你要填写的页面,我直接看下吧。
      

  16.   


    那个是从网上找的类库,是为了解决跨域访问frame的问题,貌似是用IHTMLWindow2取得接口,代码如下:
    PS:要搭建测试环境的话,这个我得准备准备……
    namespace CodecentrixSample
    {
        public class CrossFrameIE
        {
            // Returns null in case of failure.
            public static IHTMLDocument2 GetDocumentFromWindow(IHTMLWindow2 htmlWindow)
            {
                if (htmlWindow == null)
                {
                    return null;
                }            // First try the usual way to get the document.
                try
                {
                    IHTMLDocument2 doc = htmlWindow.document;
                    return doc;
                }
                catch (COMException comEx)
                {
                    // I think COMException won't be ever fired but just to be sure ...
                    if (comEx.ErrorCode != E_ACCESSDENIED)
                    {
                        return null;
                    }
                }
                catch (System.UnauthorizedAccessException)
                {
                }
                catch
                {
                    // Any other error.
                    return null;
                }            // At this point the error was E_ACCESSDENIED because the frame contains a document from another domain.
                // IE tries to prevent a cross frame scripting security issue.
                try
                {
                    // Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider.
                    IServiceProvider sp = (IServiceProvider)htmlWindow;                // Use IServiceProvider.QueryService to get IWebBrowser2 object.
                    Object brws = null;
                    sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out brws);                // Get the document from IWebBrowser2.
                    SHDocVw.IWebBrowser2 browser = (SHDocVw.IWebBrowser2)(brws);                return (IHTMLDocument2)browser.Document;
                }
                catch
                {
                }            return null;
            }        private const int E_ACCESSDENIED = unchecked((int)0x80070005L);
            private static Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
            private static Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
        }    // This is the COM IServiceProvider interface, not System.IServiceProvider .Net interface!
        [ComImport(), ComVisible(true), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"),
        InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IServiceProvider
        {
            [return: MarshalAs(UnmanagedType.I4)]
            [PreserveSig]
            int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
        }
    }
      

  17.   

    代码看上去对的, 用((IHTMLDocument3)doc).getElementById("dw_dk_zb")取到的是null?
    没有环境很难猜到问题,理论上你的跨域取document的方法对的,id、包括frame的索引号什么都是对的话,应该能取到dw_dk_zb元素
      

  18.   

    呃,因为是内网且单位上外网不便(只能手机),所以现在我只能晚上在家里查好资料第二天测试……
    请教大大,既然“用定位html元素来填充表格的做法是行不通的”,取到dw_dk_zb元素之后下一步该往哪里走呢?
      

  19.   

    dw_dk_zb.InvokeScript("setCellValue", new object[] {0,0,"test"});
    大概就是这样,htc中所有public的method都可以调用
      

  20.   

    今天上班还是很忙……
    不过大大,貌似IHTMLDocument2没有InvokeScript方法,只能用execScript的样子,譬如这里也有同样的问题:http://social.msdn.microsoft.com/Forums/windows/en-US/64703e92-9195-488f-a6fc-e9c8a6caab5e/calling-javascripts-from-c
    请问,改成这样写可以吗?
    execScript("setCellValue(0,0,'test')" , "jscript" );
      

  21.   

    嗯,我随手写的,是不对的。
    因为你用WebBrowser,按通常做法取到的是System.Windows.Forms.HtmlElement,对它可以用
    element.InvokeMember("setCellValue", new object [] {0, 0, "test"})如果你能取到嵌套在iframe里面的document(System.Windows.Forms.HtmlDocument),那么可以用
    document.InvokeScript("eval", new object[]{@"
        var dataWindow = document.getElementById('dw_dk_zb');
        dataWindow.setCellValue(0,0,'test');
    "});IHTMLWindow2.execScript应该也可以,但这个IHTMLWindow2应该要是代表iframe的window对象,
    脚本中也要先找到htc元素:
    execScript(@"document.getElementById('xxx').setCellValue(0,0,'test')" 。。总之如何从c#去执行js并不是难点,困难是如何定位、找到这个htc元素,以及验证用htc提供的public方法去操作DataWindow是否可行。我看你的iframe好像是嵌套了两层的,不知道它们是否都是跨域的?调试的时候不要着急,一步一步来,先找到外层的iframe,看看它是否为null,然后随便在这个iframe里找个元素,用outerHTML看看是否得到它的html代码,这样可以验证第一层跨域读取成功,然后再进第二层这样慢慢调试,仔细找应该可以得到dw_dk_zb这个DataWindow元素的,得到同样用outerHTML看一下验证确实是这个元素无误,再对其调用htc成员方法。
      

  22.   

    多谢大大,今天居然忘记把调试的截图带回来了,那就直接汇报吧
    我是这样写的://需要调用的Frame
    IHTMLWindow2 win1 = (IHTMLWindow2)webBrowser2.Document.Window.Frames[1].Frames[0].DomWindow;
    //获得Iframe doc
    IHTMLDocument2 doc = CodecentrixSample.CrossFrameIE.GetDocumentFromWindow(win1);
    mshtml.IHTMLElementCollection Fix;
    Fix = (mshtml.IHTMLElementCollection)doc.all.tags("datawindow");
    mshtml.IHTMLElement FixDW = (mshtml.IHTMLElement)Fix.item("dw_dk_zb", 0);
    通过断点可以看见FixDW的innerHTML是:
       <column type=INPUT id=HWMC caption="货物名称" fill="yes" colwidth=6 />
       <column type=INPUT id=GGXH caption="规格型号" colwidth=2 />
       <column type=INPUT id=JLDW caption="计量单位" colwidth=2 />
       <column type=INPUT id=HWSL caption="货物数量" colwidth=3 fill="yes" onchange="HWSL_onchange(this)" preset="number(10,8)"/>
       <column type=INPUT id=DJ caption="含税单价" colwidth=3 fill="yes" onchange="DJ_onchange(this)"  preset="number(10,8)"/>
       <column type=INPUT id=JE caption="含税金额" colwidth=3   onchange="JE_onchange(this)"  preset="number(14,2)" total="yes"  />
       <column type=SELECT id=SL caption="税率" colwidth=2 fill="yes"   node="DSO_SL" key="SL_DM" view="SL_MC" onchange="SL_onchange(this)"/>
    outerHTML是<ctais:datawindow id=dw_dk_zb style=……请问大大,这样是不是就意味着已经取得了dw_dk_zb这个DataWindow元素?
    然后我这样写execScript: win1.execScript("FixDW.setCellValue(0,0,'test')", "javascript");然后就运行出错了……
      

  23.   

    是的,定位DataWindow已经成功了。接下去应该好办了。
     win1.execScript("FixDW.setCellValue(0,0,'test')", "javascript");
    这句是错的,FixDW是c#中的变量,js中当然是不认识这个变量的。应该是
    doc.parentWindow.execScript(@"document.getElementById('dw_dk_zb').setCellValue(0,0,'test')");
      

  24.   

    一早到单位进行测试,成功往目标填入内容!太感谢啦!
    请教大大,像这样“exeScript(@“……”);”用法,和通常的exeScript("…","Script")不同,麻烦大大进一步解说下,多谢!
      

  25.   

    没有区别,@符号只是表示字符串的另一种转义方式:就是\不作转义符号用、一个串可以多行,所以比较适合写多行的,包含很多斜杠的情况,像js用这种字符串写比直接用不带@的字符串要方便。
    @这种叫原意字符串,看msdn说明:
    http://msdn.microsoft.com/zh-cn/library/vstudio/ms228362.aspx第二参数不写,默认就是javascript
      

  26.   

    多谢jshi123大大,您真是授人以渔!
    100分双手奉上