.NET Winform 开发小贴士

17 篇文章 1 订阅
11 篇文章 0 订阅

将一些有用的实例整理出来,供参考。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.οnlοad=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.οnlοad=function() { alert('test') }";
    head.appendChild((IHTMLDOMNode)script);
}

private void button1_Click(object sender, EventArgs e)
{
    webBrowser1.Navigate("http://www.hao123.com");
}




  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值