.NET Winform 开发小贴士

来源:互联网 发布:杯酒释兵权知乎 编辑:程序博客网 时间:2024/05/01 13:46

将一些有用的实例整理出来,供参考。FAQ形式,整理多少记多少。

【目录】

  1. 如何制作安装包能在Menu菜单中添加卸载菜单
  2. 如何让应用程序只能启动1次
  3. 如何让DataTable.WriteXml保存的Xml加上Encoding申明
  4. 如何读取两个以上的游标?(DataReader.NextResult)
  5. 将Form加到另一个Form里
  6. 如何在项目中引用exe路径以外的dll
  7. 如何在windows service里运行"cmd.exe"
  8. 如何添加Clickonce的Publish页面
  9. 动态创建 Lambda 表达式
  10. 在WebBrowser控件中注入脚本

 
1.  如何制作安装包能在Menu菜单中添加卸载菜单

首先需要一个Uninstall的bat文件。其实是调用系统的 msiexec.exe 来删除程序。

@echo off
%windir%/system32/msiexec.exe /x {7EFECBCB-357A-47DE-9AC0-C220A62FA217}

红色字体是程序的 ProductCode, 从安装工程的属性里可以看到。

然后把这个bat作为程序的内容添加到安装包里来。最后在用户的Program Menu的程序菜单里添加上这个bat的快捷方式就可以了。(bat会和exe一起输出到安装路径下)


 
2.  如何让应用程序只能启动1次

方法1: 通过ProcessName判断当前进程中,是否启动了当前程序名的进程。

[STAThread]static void Main() {     if (System.Diagnostics.Process.GetProcessesByName(        System.Diagnostics.Process.GetCurrentProcess().ProcessName).Length > 1)    {         MessageBox.Show("应用程序已经启动过了。");         return;     }     Application.Run(new Form1()); }

方法2: 利用System.Threading.Mutex对象,即应用程序启动时都去检查一个共享区,如果已经上锁则退出。

[STAThread]static void Main(){        bool createNew = false;        System.Threading.Mutex mutex = new System.Threading.Mutex(true, "MyApplication", out createNew);    if (createNew == false)        {                MessageBox.Show("应用程序已经启动过了。");                return;       }        Application.EnableVisualStyles();        Application.SetCompatibleTextRenderingDefault(false);       Application.Run(new Form1());        // 释放锁        mutex.ReleaseMutex();}

 需要注意的是: Mutex.ReleaseMutex是在最后调用的。从.NET 2.0开始,Thread如果在Mutex没有释放时退出,那么这个mutex就会被标识为废弃。(如果Application途中Crash的情况发生)那么,下一个尝试取得Mutex的线程,就会抛出:AbandonedMutexException异常。另外,如果Mutex被废弃的情况下,也有可能发生Application被强制终止。因此,上面的代码稍微改进一下。

private static System.Threading.Mutex _mutex;[STAThread]static void Main(){        _mutex = new System.Threading.Mutex(false, "MyApplication");    //Mutex所有权取得        if (_mutex.WaitOne(0, false) == false)        {                MessageBox.Show("应用程序已经启动过了。");        return;        }        Application.Run(new Form1());}

注意:这里的Mutex被定义为Static类成员。这是因为如果定义为局部变量,Mutex所有权取得有时候不能正常进行。另外,也存在程序运行中GC把局部变量销毁的可能性。当然,定义为局部变量的Mutex也可以通过GC.KeepAlive方法来保持。 
另外,对于多用户互斥只要在Mutex名称上加上:"Global\\" 的前缀就可以了。 


 

3.  如何让DataTable.WriteXml保存的Xml加上Encoding声明

先看看通常的写法:

static void DataTableWriteXmlDefault() {     DataTable data = new DataTable("TestData");     data.Columns.Add("Column1");    data.Columns.Add("Column2");     data.Rows.Add("value11", "value12");     data.Rows.Add("value21", "value22");     data.Rows.Add("value31", "value32");     string xmlFilePath = "test1.xml"; data.WriteXml(xmlFilePath);}

生成的XML如下,注意到没,在Xml里并没有指定Encoding(第一行的Xml头)。如果DataTable保存有一些中文信息的话,再次读入的时候就需要指定编码了。(如果系统是非中文系统的话,比如日文系统,保存的Xml就有可能变为乱码)

<?xml version="1.0" standalone="yes"?><DocumentElement>  <TestData>    <Column1>value11</Column1>    <Column2>value12</Column2>  </TestData>  <TestData>    <Column1>value21</Column1>    <Column2>value22</Column2>  </TestData>  <TestData>    <Column1>value31</Column1>    <Column2>value32</Column2>  </TestData></DocumentElement>

那么如何加入Encoding声明呢?——通过XmlWriter的WriteProcessingInstruction来控制。

using (System.Xml.XmlTextWriter xtw = new System.Xml.XmlTextWriter(xmlFilePath, Encoding.UTF8)){          xtw.WriteProcessingInstruction("xml", "version=/"1.0/" encoding=/"UTF-8/"");          xtw.Formatting = System.Xml.Formatting.Indented;          data.WriteXml(xtw);}

这样生成的Xml就加上Encoding声明了:

<?xml version="1.0" encoding="UTF-8"?>
<DocumentElement>
  <TestData>
    <Column1>value11</Column1>
    <Column2>value12</Column2>
  </TestData>
  <TestData>
    <Column1>value21</Column1>
    <Column2>value22</Column2>
  </TestData>
  <TestData>
    <Column1>value31</Column1>
    <Column2>value32</Column2>
  </TestData>
</DocumentElement> 



5.  如何将一个Form加到另一个Form里

我们在开发时,有时会需要非常复杂的画面,里面充满了各种控件,还有多个TabPage。上百个控件充斥着Form,不要说并行开发了,design时修改一个控件都害怕牵一发而动全身。通常我们会抽出共同的用户控件来简化开发。这里介绍另一个方法来应对这种场景的问题:那就是将主画面进行分解,分解到各个子画面中。再通过Controls.Add的方式组合到主画面中。

效果如下:


修改 subForm 的 FormBorderStyle 为 None 之后,看上去 subform 就像是用户控件一样了。



6. 如何在项目中引用exe路径以外的dll
 
1. 主工程添加控件工程,copy local设置为false
(这个意思是控件工程生成的dll不会copy到主工程下,即不会build一份和exe放在一块。)

2. 在主工程里添加一个app.config。配置如下:

<?xml version="1.0"?><configuration>  <startup>    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>  </startup>  <runtime>    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">      <!--<probing privatePath="bin;bin2/subbin;bin3"/>-->      <dependentAssembly>        <assemblyIdentity name="MyCheckBoxCtrl"/>        <codeBase href="ExtDlls/MyCheckBoxCtrl.dll" mce_href="ExtDlls/MyCheckBoxCtrl.dll"/>      </dependentAssembly>    </assemblyBinding>  </runtime></configuration>

注意:如果是强名称的dll,可以是放在任意位置。没有强名称的dll只能放在exe目录下面的某个子目录。
比如:exe是在bin/debug,那么可以放在 bin/debug/bin2 或者其他的别的名字的目录里。



7. 如何在window service里运行"cmd.exe"

一般默认安装完的service运行"cmd.exe"不会有什么效果。这里是说:可能调用不会出错但实际上并没有成功的执行。
原因在于:安全上限制。你可以看到下面抓图中,"Allow Service to interact with desktop"没有勾选上。

下面的代码可以让你在安装时将此选项勾选上:
【在 ProjectInstaller 里重写了 Commit 方法】

public override void Commit(IDictionary savedState){    // set the service "allow service interact with desktop" as "true"    // e.g. service can run "cmd.exe"    var coOptions = new ConnectionOptions {Impersonation = ImpersonationLevel.Impersonate};    var mgmtScope = new ManagementScope(@"root\CIMV2", coOptions);    mgmtScope.Connect();    var wmiService = new ManagementObject("Win32_Service.Name='" + serviceInstaller1.ServiceName + "'");    var inputParams = wmiService.GetMethodParameters("Change");    inputParams["DesktopInteract"] = true;    var outParams = wmiService.InvokeMethod("Change", inputParams, null);    base.Commit(savedState);}




8. 如何添加 Clickonce 的Publish页面






9. 动态创建 Lambda 表达式

class Program{    static void Main(string[] args)    {        var user = new User { ID = "123" };        var parameter = Expression.Parameter(typeof(User), "s");        var memberAccessor = Expression.MakeMemberAccess(parameter, typeof(User).GetField("ID"));        var constant = Expression.Constant("123", typeof(string));        // static method call expression        var method = typeof(string).GetMethod("Equals", new[] {typeof(string), typeof(string)});        var callExpr = Expression.Call(method, memberAccessor, constant);        var lambda = Expression.Lambda<Func<User, bool>>(callExpr, parameter);        var exec = lambda.Compile();        var result = exec(user);        Console.WriteLine(lambda.ToString() + "\t" + result);        // binary expression                    var binaryExpr = Expression.Equal(memberAccessor, constant);        var lambda1 = Expression.Lambda<Func<User, bool>>(binaryExpr, parameter);        var exec1 = lambda1.Compile();        var result1 = exec1(user);        Console.WriteLine(lambda1.ToString() + "\t" + result1);        // instance method call expression        var method1 = typeof(User).GetMethod("Validate");        var callExpr1 = Expression.Call(parameter, method1, constant);        var lambda2 = Expression.Lambda<Func<User, bool>>(callExpr1, parameter);        var exec2 = lambda2.Compile();        var result2 = exec2(user);        Console.WriteLine(lambda2.ToString() + "\t" + result2);        Console.Read();    }}class User{    public string ID;    public bool Validate(string arg)    {        return string.Equals(ID, arg);    }}


10.  在WebBrowser控件中注入脚本
(1) 添加 Microsoft.mshtml.dll 引用
(2) 添加代码如下:
private void Form1_Load(object sender, EventArgs e){       webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);}void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e){    var htmlDoc = (IHTMLDocument3)webBrowser1.Document.DomDocument;    HTMLHeadElement head = htmlDoc.getElementsByTagName("head").Cast<HTMLHeadElement>().First();    var script = (IHTMLScriptElement)((IHTMLDocument2)htmlDoc).createElement("script");    script.text = "window.onload=function() { alert('test') }";    head.appendChild((IHTMLDOMNode)script);}private void button1_Click(object sender, EventArgs e){    webBrowser1.Navigate("http://www.hao123.com");}private void Form1_Load(object sender, EventArgs e){       webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);}void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e){    var htmlDoc = (IHTMLDocument3)webBrowser1.Document.DomDocument;    HTMLHeadElement head = htmlDoc.getElementsByTagName("head").Cast<HTMLHeadElement>().First();    var script = (IHTMLScriptElement)((IHTMLDocument2)htmlDoc).createElement("script");    script.text = "window.onload=function() { alert('test') }";    head.appendChild((IHTMLDOMNode)script);}private void button1_Click(object sender, EventArgs e){    webBrowser1.Navigate("http://www.hao123.com");}




原创粉丝点击