MOSS工作流 InfoPath+WorkFlow+Moss 开发要点

来源:互联网 发布:学校网络舆情监控制度 编辑:程序博客网 时间:2024/04/25 16:11

 

项目基本完成了,一期的东西基本都做完了,采用的纯Moss下面的开发,也应该总结一些要点和东西,为了不使自己以后完全忘记,或者说:为了让广大困惑的人,能在此找到一些捷径和关键点,在此能有所用,也就欣慰不已。

要点一:INFOPATHXML处理

          在对带有VBAINFOPATH编码当中,你会发现在支持浏览器的INFOPATH表单里面,能够使用的对象模型和处理十分有限,但是为了快捷,我们采用了它,因为利用现有的XML处理,加上页面当中的规则,基本上可以完成所有的业务逻辑。其实在简单的理解来说,InfoPath表单,其本质数据完全是XML,只是在展现的时候,用特定的控件来展示你XML的数据的,而控件基本都是采用绑定的形式和你的XML数据交互的,并且辅之以相当强大的规则和操作,来完成你的应用逻辑,所以处理方式比较特殊。所有规则能做的,代码都能做,但是目前在展示方面还有挺多的局限,后面会有所提到。

问题一:如何处理你的重复节?

重复节,也许就是我们在INFOPATH里面用的最多的东西,其十分类似于我们在Asp.NET里面的Reperate,可以用来存放重复数据字段,而其在INFOPATHXML数据的定义中,通常是三级层次结构的,如图,第一层是整个节点,第二层是每个子节点,每个子节点里面可以包含多个属性或者字段,用xpath来解析的话,就是类似于如此:

/my:HQContentType/my:Enclosuregroup

/my:HQContentType/my:Enclosuregroup/my:EnclosureNode

/my:HQContentType/my:Enclosuregroup/my:EnclosureNode/my:Enclosure

最前面的是我的表单的名字。而用来处理节的对象模型,在支持浏览器的InfoPath表单里面,我们用得最多的是:

XmlNamespaceManager XPathNavigatorXmlNamespaceManager是对整个XML的命名控件管理的对象模型,也可以理解为表单的命名空间,而XPathNavigator就是采用XPATH来对xml数据进行操作的对象模型。

读和取的办法如下代码:

///<summary>

       
/// 把节里的重复节里的DropDownlist的值连成字符串

       
/// </summary>

       
/// <param name="xpath_Group">节里的组</param>

       
/// <param name="strDll_Name">DropDownlist的名字</param>

       
/// <param name="splitchar">分割字符</param>

       
/// <returns>正却执行返回结果,异常返回空值</returns>

       
publicstring RepeatDllValuesTostring(string xpath_Group,string strDll_Name,char splitchar)

        {

           
string returnValue= string.Empty;

           
try

            {

                XmlNamespaceManager ns
=this.NamespaceManager;

                XPathNavigator xpRoot
=this.MainDataSource.CreateNavigator();

                XPathNavigator xpCompanyGroup
= xpRoot.SelectSingleNode(xpath_Group, ns);

                XPathNodeIterator xpGroupIter
= xpCompanyGroup.SelectChildren(XPathNodeType.Element);

                StringBuilder SBvalue
=new StringBuilder();

               
while (xpGroupIter.MoveNext())

                {

                    XPathNavigator xpCurrent
= xpGroupIter.Current;

                   
string strValue= xpCurrent.SelectSingleNode("my:"+ strDll_Name, ns).Value;

                   
if (!(string.IsNullOrEmpty(strValue)|| strValue.Trim().Length<= 0))

                    {

                        SBvalue.Append(strValue
+ splitchar);

                    }

                }



               
if (SBvalue.Length> 0)

                {

                    returnValue
= SBvalue.ToString();

                    returnValue
= returnValue.Remove(returnValue.LastIndexOf(splitchar));

                }

            }

           
catch (SPException spex)

            {

               
//错误处理

               
returnstring.Empty;

            }

           
return returnValue;

        }




问题二:如何处理你的附件。

附件节的处理方式,比较多样,默认的如果附件节存储的地方是InfoPath本身的话,InfoPath有自己的一套编码转化方式,实际上还是将附件打成了流,存储到了相应的XML的节里面。试想,如果文件比较大,那么表单的载入将会十分的缓慢,这样做的效率比较低下。但是可以利用附件节做为媒介,运用文档库,把文件上传到Moss里面,在前台留一个简单的url,同样,在InfoPath表单载入的时候,也可以把文档库里面的文件读取到InfoPath的附件节里,提供用户下载查看。

这里提供两个类,一个用来编码,一个用来解码,下面是编码和解码的类,这里有需要注意的是,附件节对应的xml节中,如果读入了文件,要把nil属性给去掉,否则会报错。

XPathNavigator xpExcelFileNode= xpExcels.SelectSingleNode("my:AssistFileNode", ns);

                    xpExcels.AppendChild(xpExcelFileNode);

                    XPathNodeIterator xpExcelsIterator
= xpExcels.SelectChildren(XPathNodeType.Element);



                   
while (xpExcelsIterator.MoveNext())

                    {

                        XPathNavigator xptempExcelFileNode
= xpExcelsIterator.Current;



                        XPathNavigator xpExcel
= xptempExcelFileNode.SelectSingleNode("my:AssistFile", ns);



                       
if (xpExcel.MoveToAttribute("nil","http://www.w3.org/2001/XMLSchema-instance"))

                        {

                            xpExcel.DeleteSelf();

                            InfoPathAttachmentEncoder encoder
=new InfoPathAttachmentEncoder();

                           
string fileStream= encoder.GetTheBase64String(fo);

                            xpExcel.SetValue(fileStream);

                           
break;

                        }

                    }

publicclass InfoPathAttachmentEncoder

    {

       
privatestring base64EncodedFile= string.Empty;

       
privatestring fullyQualifiedFileName;



       
/// <summary>

       
/// Creates an encoder to create an InfoPath attachment string.

       
/// </summary>

       
/// <param name="fullyQualifiedFileName"></param>

       
public InfoPathAttachmentEncoder(string fullyQualifiedFileName)

        {

           
if (fullyQualifiedFileName== string.Empty)

               
thrownew ArgumentException("Must specify file name","fullyQualifiedFileName");



           
if (!File.Exists(fullyQualifiedFileName))

               
thrownew FileNotFoundException("File does not exist:" + fullyQualifiedFileName, fullyQualifiedFileName);



           
this.fullyQualifiedFileName= fullyQualifiedFileName;

        }



       
public InfoPathAttachmentEncoder()

        {



        }



       
publicstring GetTheBase64String(SPFile spFile)

        {



            Stream tempFileStream
= spFile.OpenBinaryStream();

           
//BinaryReader br = new BinaryReader(tempFileStream);



           
// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.

            MemoryStream ms
=new MemoryStream();



           
// Get the file information.



           
using (BinaryReader br= new BinaryReader(tempFileStream))

            {

               
string fileName= spFile.Name;



               
uint fileNameLength= (uint)fileName.Length+ 1;



               
byte[] fileNameBytes= Encoding.Unicode.GetBytes(fileName);



               
using (BinaryWriter bw= new BinaryWriter(ms))

                {

                   
// Write the InfoPath attachment signature.

                    bw.Write(
newbyte[] { 0xC7, 0x49,0x46, 0x41 });



                   
// Write the default header information.

                    bw.Write((
uint)0x14);      // size

                    bw.Write((
uint)0x01);      // version

                    bw.Write((
uint)0x00);      // reserved



                   
// Write the file size.

                    bw.Write((
uint)br.BaseStream.Length);



                   
// Write the size of the file name.

                    bw.Write((
uint)fileNameLength);



                   
// Write the file name (Unicode encoded).

                    bw.Write(fileNameBytes);



                   
// Write the file name terminator. This is two nulls in Unicode.

                    bw.Write(
newbyte[] { 0, 0 });



                   
// Iterate through the file reading data and writing it to the outbuffer.

                   
byte[] data= newbyte[64* 1024];

                   
int bytesRead= 1;



                   
while (bytesRead> 0)

                    {

                        bytesRead
= br.Read(data,0, data.Length);

                        bw.Write(data,
0, bytesRead);

                    }

                }

            }





           
// This memorystream will hold the Base64 encoded InfoPath attachment.

            MemoryStream msOut
=new MemoryStream();



           
using (BinaryReader br= new BinaryReader(new MemoryStream(ms.ToArray())))

            {

               
// Create a Base64 transform to do the encoding.

                ToBase64Transform tf
=new ToBase64Transform();



               
byte[] data= newbyte[tf.InputBlockSize];

               
byte[] outData= newbyte[tf.OutputBlockSize];



               
int bytesRead= 1;



               
while (bytesRead> 0)

                {

                    bytesRead
= br.Read(data,0, data.Length);



                   
if (bytesRead== data.Length)

                        tf.TransformBlock(data,
0, bytesRead, outData,0);

                   
else

                        outData
= tf.TransformFinalBlock(data,0, bytesRead);



                    msOut.Write(outData,
0, outData.Length);

                }

            }



            msOut.Close();



           
return base64EncodedFile= Encoding.ASCII.GetString(msOut.ToArray());

           



        }



       
/// <summary>

       
/// Returns a Base64 encoded string.

       
/// </summary>

       
/// <returns>String</returns>

       
publicstring ToBase64String()

        {

           
if (base64EncodedFile!= string.Empty)

               
return base64EncodedFile;



           
// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.

            MemoryStream ms
=new MemoryStream();



           
// Get the file information.

           
using (BinaryReader br= new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read)))

            {

               
string fileName= Path.GetFileName(fullyQualifiedFileName);



               
uint fileNameLength= (uint)fileName.Length+ 1;



               
byte[] fileNameBytes= Encoding.Unicode.GetBytes(fileName);



               
using (BinaryWriter bw= new BinaryWriter(ms))

                {

                   
// Write the InfoPath attachment signature.

                    bw.Write(
newbyte[] { 0xC7, 0x49,0x46, 0x41 });



                   
// Write the default header information.

                    bw.Write((
uint)0x14);      // size

                    bw.Write((
uint)0x01);      // version

                    bw.Write((
uint)0x00);      // reserved



                   
// Write the file size.

                    bw.Write((
uint)br.BaseStream.Length);



                   
// Write the size of the file name.

                    bw.Write((
uint)fileNameLength);



                   
// Write the file name (Unicode encoded).

                    bw.Write(fileNameBytes);



                   
// Write the file name terminator. This is two nulls in Unicode.

                    bw.Write(
newbyte[] { 0, 0 });



                   
// Iterate through the file reading data and writing it to the outbuffer.

                   
byte[] data= newbyte[64* 1024];

                   
int bytesRead= 1;



                   
while (bytesRead> 0)

                    {

                        bytesRead
= br.Read(data,0, data.Length);

                        bw.Write(data,
0, bytesRead);

                    }

                }

            }





           
// This memorystream will hold the Base64 encoded InfoPath attachment.

            MemoryStream msOut
=new MemoryStream();



           
using (BinaryReader br= new BinaryReader(new MemoryStream(ms.ToArray())))

            {

               
// Create a Base64 transform to do the encoding.

                ToBase64Transform tf
=new ToBase64Transform();



               
byte[] data= newbyte[tf.InputBlockSize];

               
byte[] outData= newbyte[tf.OutputBlockSize];



               
int bytesRead= 1;



               
while (bytesRead> 0)

                {

                    bytesRead
= br.Read(data,0, data.Length);



                   
if (bytesRead== data.Length)

                        tf.TransformBlock(data,
0, bytesRead, outData,0);

                   
else

                        outData
= tf.TransformFinalBlock(data,0, bytesRead);



                    msOut.Write(outData,
0, outData.Length);

                }

            }



            msOut.Close();



           
return base64EncodedFile= Encoding.ASCII.GetString(msOut.ToArray());

        }

    }

publicclass InfoPathAttachmentDecoder

    {

       
privateconst int SP1Header_Size = 20;

       
privateconst int FIXED_HEADER = 16;



       
privateint fileSize;

       
privateint attachmentNameLength;

       
privatestring attachmentName;

       
privatebyte[] decodedAttachment;



       
/// <summary>

       
/// Accepts the Base64 encoded string

       
/// that is the attachment.

       
/// </summary>

       
public InfoPathAttachmentDecoder(string theBase64EncodedString)

        {

           
byte[] theData= Convert.FromBase64String(theBase64EncodedString);

           
using (MemoryStream ms= new MemoryStream(theData))

            {

                BinaryReader theReader
=new BinaryReader(ms);

                DecodeAttachment(theReader);

            }

        }



        



       
privatevoid DecodeAttachment(BinaryReader theReader)

        {

           
//Position the reader to get the file size.

           
byte[] headerData= newbyte[FIXED_HEADER];

            headerData
= theReader.ReadBytes(headerData.Length);



            fileSize
= (int)theReader.ReadUInt32();

            attachmentNameLength
= (int)theReader.ReadUInt32()* 2;



           
byte[] fileNameBytes= theReader.ReadBytes(attachmentNameLength);

           
//InfoPath uses UTF8 encoding.

            Encoding enc
= Encoding.Unicode;

            attachmentName
= enc.GetString(fileNameBytes,0, attachmentNameLength- 2);

            decodedAttachment
= theReader.ReadBytes(fileSize);

        }



       
publicvoid SaveAttachment(string saveLocation)

        {

           
string fullFileName= saveLocation;

           
if (!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()))

            {

                fullFileName
+= Path.DirectorySeparatorChar;

            }



            fullFileName
+= attachmentName;



           
if (File.Exists(fullFileName))

                File.Delete(fullFileName);



            FileStream fs
=new FileStream(fullFileName, FileMode.CreateNew);

            BinaryWriter bw
=new BinaryWriter(fs);

            bw.Write(decodedAttachment);



            bw.Close();

            fs.Close();

        }



       
publicstring Filename

        {

           
get {return attachmentName; }

        }



       
publicbyte[] DecodedAttachment

        {

           
get {return decodedAttachment; }

        }

    }

问题三:怎么处理表单的关闭和数据的提交?

InfoPath里面,怎么向宿主环境提交数据,怎么吧表单提交到宿主里面呢?实现的方法有三种,一种是直接利用InfoPath的条件和规则以及操作无需编码,另外一个是在后台代码里面进行填写,前者局限性比较大,虽然能够完成一定的功能,但是比较局限,而用后台代码的话就不存在这个问题,可以很轻松的控制自己的逻辑和业务。第三种采用两者结合的方式,在尽量少的编码情况下,做到尽量多的东西(懒人的终极选择)。在提交的时候,采用的对象模型为,FileSubmitConnection,给段样例如下,原来还研究了一下ADO数据源的读取和提交,发现在表单的InternalStartup()以后,对其数据源的东西就无法更改了,所以和数据库操作联动的话,采用的还是自定义的assembly来进行操作,简单实用。

//获取 提交数据源 并设置提交的参数 提交到的文件夹 文件名

                    FileSubmitConnection fileSubmit
= (FileSubmitConnection)this.DataConnections["FileSubmit"];

                   
if (CurrentWeb.Url.EndsWith("/"))

                    {

                        fileSubmit.FolderUrl
= HQIssueDocLib.ParentWeb.Url+ HQIssueDocLib.RootFolder.Url;

                    }

                   
else

                    {

                        fileSubmit.FolderUrl
= HQIssueDocLib.ParentWeb.Url+ "/"+ HQIssueDocLib.RootFolder.Url;

                    }



                   
//如果表单不存在

                   
if (xpIsExist.Value.Equals("0"))

                    {

                       
//提交

                       
if (xpSubmitOrNot.Value.Equals("1"))

                        {

                           
if (xpRedtape.Value.Length<= 0)

                            {

                                xpErrorMessage.SetValue(formSaveSate.EmptyRedFile);

                                xpDispatchTitle.SetValue(
string.Empty);

                               
return;

                            }

                           
//设置 CanModify为"0"为不可修改,IsSubmit为"1"已提交,IsExist为"1"为已存在,并提交

                            xpCanModify.SetValue(
"0");

                            xpIsSubmit.SetValue(
"1");

                            xpIsExist.SetValue(
"1");

                            fileSubmit.Filename.SetStringValue(filename);

                            fileSubmit.Execute();

}

}

问题四:怎么来处理表单的多视图?

一个InfoPath表单,可以采用多个视图来进行展现,并且绑定相关xml数据源的控件,可以定义新的规则和新的展现方式,这个个人认为是InfoPath良好设计的最佳体现。充分分离了数据和视图,但是目前感觉在WEB这块的INFOPATH表单,展现局限性还是很大,虽然可以自定自己的类似于AciviteX的控件,但是十分麻烦。不过后期肯定会陆续完善,因为微软内部的新Moss测试版本已经到14了。你可以在载入的时候根据读入的xml数据,来选择你所需要展现的视图,回传的时候,同样也可以更新你的视图,具体例子代码如下,思路就是通过检查传入的变量,调用SetDefaultView方法,这两个都是包含在FormLoadLoadingEventArgs里面的,具体取传入的变量的方法是看InputParameters键值对集合里面的值,和queryString类似。

public bool SetCanModify(LoadingEventArgs loadingEventArgs)

        {

           
try

            {

                XmlNamespaceManager ns
=this.NamespaceManager;

                XPathNavigator xpRoot
=this.MainDataSource.CreateNavigator();

               
//表单的标题

                XPathNavigator xpDispatchTitle
= xpRoot.SelectSingleNode("my:HQContentType/my:DispatchTitleNode/my:DispatchTitle", ns);

               
//值 "0" 没有提交,值 "1"提交

                XPathNavigator xpIsSubmit
= xpRoot.SelectSingleNode("my:HQContentType/my:IsSubmit", ns);

               
//值 "0"不允许修改 ,"1"允许修改

                XPathNavigator xpCanModify
= xpRoot.SelectSingleNode("my:HQContentType/my:CanModify", ns);

               
//值 "0"不允许关闭,"1"允许关闭

                XPathNavigator xpIsAllowClose
= xpRoot.SelectSingleNode("my:HQContentType/my:StateGroup/my:IsAllowClose", ns);

               
//表单是否存在

                XPathNavigator xpIsExist
= xpRoot.SelectSingleNode("my:HQContentType/my:StateGroup/my:IsExist", ns);



               
//包含openfrom,表示是从表单中的链接打开的

               
bool isOpenFromForm= loadingEventArgs.InputParameters.ContainsKey("openfrom");

               
//Task5是特殊情况,链接中的参数canmodify是标志允许修改

               
bool isSpecialCanModify= loadingEventArgs.InputParameters.ContainsKey("canmodify");



               
//对视图和关闭按钮进行设置

               
if (xpDispatchTitle!= null&& xpDispatchTitle.Value.Trim().Length> 0&& xpIsExist!= null&& xpIsExist.Value.Equals("1"))

                {

                   
using (SPWeb web= new SPSite(SPContext.Current.Site.ID).OpenWeb(SPContext.Current.Web.ID))

                    {

                       
//获得当前项目

                        SPList HQIssueDocLib
= web.Lists["总部发文库"];

                        SPListItem currentItem
= FindItemByFileName(HQIssueDocLib, xpDispatchTitle.Value);



                       
//取Field的显示名

                       
string strAuthor= string.Empty;

                       
string strFileOrigin= GetDisplayNameByStaticName(HQIssueDocLib,"FileOrigin");

                       
string strCanModify= GetDisplayNameByStaticName(HQIssueDocLib,"CanModify");



                       
//如果文件存在,且CanModify域值不为空

                       
if (xpIsExist!= null&& xpIsExist.Value.Equals("1")&& currentItem[strCanModify]!= null)

                        {

                           
//依据不同情况取得文件的创建者的Field的显示名,下属呈文的时候Drafter是我们需要的创建者

                           
if (currentItem[strFileOrigin]!= null&& currentItem[strFileOrigin].ToString().Equals("下属呈文"))

                            {

                                strAuthor
= GetDisplayNameByStaticName(HQIssueDocLib,"Drafter");

                            }

                           
else

                            {

                                strAuthor
= GetDisplayNameByStaticName(HQIssueDocLib,"Author");

                            }

                           
//取得当前用户和文件创建者的相关对象

                            SPFieldUserValue userValue
=new SPFieldUserValue(web, currentItem[strAuthor].ToString());

                            SPUser currentUser
= SPContext.Current.Web.CurrentUser;

                           
string currentUserLogName= currentUser.LoginName;



                           
//通过项的CanModify域值设置不同的视图和参数

                           
string strVaue= currentItem[strCanModify].ToString();

                           
switch (strVaue)

                            {

                               
case"":

                                   
//允许修改,当前用户就是创建者,则采取默认视图

                                   
if (currentUserLogName.Equals(userValue.User.LoginName))

                                    {

                                       
//并且是从表单里打开,则不允许关闭表单

                                       
if (isOpenFromForm)

                                        {

                                            xpIsAllowClose.SetValue(
"0");

                                        }

                                       
else//从文档库打开,允许关闭表单

                                        {

                                            xpIsAllowClose.SetValue(
"1");

                                        }

                                        xpCanModify.SetValue(
"1");

                                        loadingEventArgs.SetDefaultView(
"DefaultView");

                                    }

                                   
else//允许修改,但当前用户不是创建者,设置只读视图,并且不允许关闭表单

                                    {

                                       
//if (isOpenFromForm)

                                       
//{

                                       
//    xpIsAllowClose.SetValue("0");

                                       
//}

                                       
//else

                                       
//{

                                       
//    xpIsAllowClose.SetValue("1");

                                       
//}

                                        xpIsAllowClose.SetValue(
"0");

                                        xpCanModify.SetValue(
"0");

                                        loadingEventArgs.SetDefaultView(
"ReadOnlyView");

                                    }

                                   
break;

                               
default:

                                   
//这个是特殊的标志为,强制进行修改,是从表单链接打开,不允许关闭

                                   
if (isSpecialCanModify)

                                    {

                                        xpCanModify.SetValue(
"1");

                                        xpIsAllowClose.SetValue(
"0");

                                        loadingEventArgs.SetDefaultView(
"DefaultView");

                                       
returntrue;

                                    }



                                   
if (isOpenFromForm)//不允许修改,从表单链接打开,不允许关闭

                                    {

                                        xpIsAllowClose.SetValue(
"0");

                                    }

                                   
else//不允许修改,从文档库链接打开,允许关闭

                                    {

                                        xpIsAllowClose.SetValue(
"1");

                                    }

                                    xpCanModify.SetValue(
"0");

                                    loadingEventArgs.SetDefaultView(
"ReadOnlyView");

                                   
break;

                            };

                        }

                    }

                }



            }

           
catch (SPException spex)

            {

               
returnfalse;

            }

           
returntrue;

        }

原创粉丝点击