现在的位置: 首页 > 综合 > 正文

.NET Winform 开发小贴士

2013年08月30日 ⁄ 综合 ⁄ 共 7644字 ⁄ 字号 评论关闭

将一些有用的实例整理出来,供参考。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");
}

抱歉!评论已关闭.