C#学习心得

版本：v2.0

Crifan Li

摘要

本文主要介绍了C#中的一些学习心得，包括但不限于Regex,TreeView,字典类型变
量,spritf,UrlEncode等

[提 本文提供多种格式供：
示]      在线阅读      HTML HTMLs PDF CHM TXT RTF WEBHELP
    下载（7zip压缩包） HTML HTMLs PDF CHM TXT RTF WEBHELP

    HTML版本的在线地址为：

    http://www.crifan.com/files/doc/docbook/csharp_summary/release/html/
    csharp_summary.html

    有任何意见，建议，提交bug等，都欢迎去讨论组发帖讨论：

    http://www.crifan.com/bbs/categories/csharp_summary/

2013-08-20

┌─────────────────────────────────────────────────────────────────────────────┐
│修订历史                                                                     │
├────────────────────────────┬─────────────────────────────────────┬──────────┤
│修订 2.0                    │2013-08-20                           │crl       │
├────────────────────────────┴─────────────────────────────────────┴──────────┤
│ 1. 将crifanLib.cs弄成独立的book                                             │
│ 2. 整理更多的关于C#的心得                                                   │
├────────────────────────────┬─────────────────────────────────────┬──────────┤
│修订 1.0                    │2012-11-06                           │crl       │
├────────────────────────────┴─────────────────────────────────────┴──────────┤
│ 1. 将C#从language_summary移至此独立book                                     │
│ 2. 更新crifanLib.cs的最新链接                                               │
└─────────────────────────────────────────────────────────────────────────────┘

版权 © 2013 Crifan, http://crifan.com

本文章遵从：署名-非商业性使用 2.5 中国大陆(CC BY-NC 2.5)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

目录

前言

    1. 本文目的
    2. 待完成

1. C#学习心得

    1.1. C#中的字典类型变量
    1.2. C#中关于字符串的处理

        1.2.1. C#中类似于spritf的函数
        1.2.2. 字符串的中间对齐左右填充的效果

    1.3. C#中ComboBox的使用

        1.3.1. 给ComboBox设置数据源

2. C#的网络相关处理

    2.1. C#中HTTP处理

        2.1.1. C#中自动处理返回的压缩的HTML
        2.1.2. C#中给HTTP添加代理Proxy支持
        2.1.3. ReadLine或ReadToEnd会无限期挂掉

    2.2. C#中处理cookie

        2.2.1. C#中自动处理cookie
        2.2.2. SetCookie解析有bug

    2.3. C#中处理URL

        2.3.1. 关于UrlEncode把空格编码为加号"+"而不是%20的问题

    2.4. C#中的WebBrowser

        2.4.1. DocumentCompleted被调用两次

3. C#的处理Excel和CSV

    3.1. C#中处理Excel

        3.1.1. Excel自动适应列宽
        3.1.2. 选中Excel的某列后再去自动适应列宽
        3.1.3. Could not load file or assembly ‘Microsoft.Office.Interop.Excel
        3.1.4. Microsoft.Vbe.Interop.dll和office.dll

    3.2. C#中处理CSV

4. C#的TreeView控件使用心得

    4.1. 添加Node的方法
    4.2. 给TreeView添加图标
    4.3. 获得右击所在节点
    4.4. 添加右键菜单（Context Menu）
    4.5. 给TreeNode添加名字

参考书目

前言

目录

1. 本文目的
2. 待完成

1. 本文目的

本文目的在于，把之前自己折腾C#的心得和注意实现都整理出来，供其他人参考。

2. 待完成

目标只整理了到了page 2：

http://www.crifan.com/category/work_and_job/programming_language/csharp/page/2/

之后的，有空再整理到此book中。

第 1 章 C#学习心得

目录

1.1. C#中的字典类型变量
1.2. C#中关于字符串的处理

    1.2.1. C#中类似于spritf的函数
    1.2.2. 字符串的中间对齐左右填充的效果

1.3. C#中ComboBox的使用

    1.3.1. 给ComboBox设置数据源

1.1. C#中的字典类型变量

C#中也有类似于Python中的字典类型的变量

参考代码如下：


Dictionary<String, int> dicPicIdx;
Dictionary<string, int>.KeyCollection dicKeys;

dicPicIdx = new Dictionary<string, int>();
dicPicIdx.Add("EmptyDocumentFolder", 1);
dicPicIdx.Add("NonEmptyDocumentFolder", 1);

dicPicIdx.Add("NonEmptyAlbum", 3);
dicPicIdx.Add("EmptyAlbum", 3);

dicPicIdx.Add("NonEmptyFavoriteFolder", 4);
dicPicIdx.Add("EmptyFavoriteFolder", 4);

dicPicIdx.Add("Photo", 6);
dicPicIdx.Add("Audio", 7);
dicPicIdx.Add("Video", 8);

dicKeys = dicPicIdx.Keys;



1.2. C#中关于字符串的处理

1.2.1. C#中类似于spritf的函数

C#中，类似于C中常用的spritf函数，是String.Format函数。

最简单的用法举例如下：

    string spritfTestStr = String.Format("Test sprintf in C#, number={0:D}, string=\"{1:s}\", float={2:0.000}", 100, "crifan", Math.PI);
    //spritfTestStr = Test sprintf in C#, number=100, string="crifan", float=3.142


关于Format函数的更多的示例，可以参考微软官方文档：String.Format Method (String,
Object)

关于其他更多不同类型的参数，比如日期，数值，枚举等，如何指定对应的格式，可以参
考：

      □ For more information about the composite formatting feature supported
        by methods such as Format, AppendFormat, and some overloads of
        WriteLine, see Composite Formatting.
      □ For more information about numeric format specifiers, see Standard
        Numeric Format Strings and Custom Numeric Format Strings.
      □ For more information about date and time format specifiers, see
        Standard DateTime Format Strings and Custom DateTime Format Strings.
      □ For more information about enumeration format specifiers, see
        Enumeration Format Strings.
      □ For more information about formatting, see Formatting Types and
        Formatting Overview.

1.2.2. 字符串的中间对齐左右填充的效果

代码：


//input: [4] Valid: B0009IQZFM
//output: ============================ [4] Valid: B0009IQZFM =============================
public string formatString(string strToFormat, char cPaddingChar = '*', int iTotalWidth = 80)
{
    //auto added space
    strToFormat = " " + strToFormat + " "; //" [4] Valid: B0009IQZFM "

    //1. padding left
    int iPaddingLen = (iTotalWidth - strToFormat.Length)/2;
    int iLefTotalLen = iPaddingLen + strToFormat.Length;
    string strLefPadded = strToFormat.PadLeft(iLefTotalLen, cPaddingChar); //"============================ [4] Valid: B0009IQZFM "
    //2. padding right
    string strFormatted = strLefPadded.PadRight(iTotalWidth, cPaddingChar); //"============================ [4] Valid: B0009IQZFM ============================="

    return strFormatted;
}



详见：【已解决】C#中实现字符串的中间对齐左右填充的效果

1.3. C#中ComboBox的使用

1.3.1. 给ComboBox设置数据源

主要包含三步：

 1. 设置结构体，其中的field的名字首字母大写，支持get和set


    private struct keyValueList
    {
        public string Key{get;set;} // key
        public List<string> ValueStrList{get;set;} // the string value list for the key
    }



 2. 创建对应的结构体数组


    List<keyValueList> gFootprintTypeSelList; // footprint type

    gFootprintTypeSelList = new List<keyValueList>();

    //1. option1: Guest Blogging
    keyValueList keyValueListGuestBlogging = new keyValueList();
    keyValueListGuestBlogging.Key = "Guest Blogging";
    keyValueListGuestBlogging.ValueStrList = new List<string>();
    //keyValueListGuestBlogging.ValueStrList.Add("Specify Footprint");
    keyValueListGuestBlogging.ValueStrList.Add("Guest Blogging");
    keyValueListGuestBlogging.ValueStrList.Add("Contribute");
    keyValueListGuestBlogging.ValueStrList.Add("Write for us");
    keyValueListGuestBlogging.ValueStrList.Add("Guest Category");
    keyValueListGuestBlogging.ValueStrList.Add("Submit Content");
    gFootprintTypeSelList.Add(keyValueListGuestBlogging);




 3. 把结构体数组赋值给ComboBox的DataSource，把DisplayMember设置为对应的结构体的
    field的名字（全小写）


    cmbFootprintType.DataSource = gFootprintTypeSelList;
    cmbFootprintType.DisplayMember = "key";



详见：【已解决】C#中给ComboBox设置数据源

第 2 章 C#的网络相关处理

目录

2.1. C#中HTTP处理

    2.1.1. C#中自动处理返回的压缩的HTML
    2.1.2. C#中给HTTP添加代理Proxy支持
    2.1.3. ReadLine或ReadToEnd会无限期挂掉

2.2. C#中处理cookie

    2.2.1. C#中自动处理cookie
    2.2.2. SetCookie解析有bug

2.3. C#中处理URL

    2.3.1. 关于UrlEncode把空格编码为加号"+"而不是%20的问题

2.4. C#中的WebBrowser

    2.4.1. DocumentCompleted被调用两次

2.1. C#中HTTP处理

2.1.1. C#中自动处理返回的压缩的HTML

当你处理http时，返回的html的header中包含：

Content-Encoding: deflate

或

Content-Encoding: gzip

时，说明是返回的是压缩的HTML。

想要支持压缩的html的话，C#中，在发送HttpWebRequest之前，设置
AutomaticDecompression为对应的参数即可：


HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);

......

req.Headers["Accept-Encoding"] = "gzip, deflate";
//req.AutomaticDecompression = DecompressionMethods.GZip;
req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;



更多解释详见：

【已解决】C#中HttpWebRequest使用Proxy后异常

2.1.2. C#中给HTTP添加代理Proxy支持

示例代码：


using System.Net;
WebProxy gProxy = new WebProxy("127.0.0.1", 8087);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Proxy = gProxy;



详见：【已解决】C#中HttpWebRequest支持代理

2.1.3. ReadLine或ReadToEnd会无限期挂掉

在用C#的GetResponseStream，常会遇到，ReadLine或ReadToEnd会无限期挂掉，所以，需
要去加上对应的超时（timeout）设置：

详见：【已解决】C#中在GetResponseStream得到的Stream后,通过StreamReader去
ReadLine或ReadToEnd会无限期挂掉 + 给StreamReader添加Timeout支持

2.2. C#中处理cookie

2.2.1. C#中自动处理cookie

【整理】C#中如何自动处理cookie

2.2.2. SetCookie解析有bug

C#中，关于解析SetCookie，前后遇到多个bug，详见：

【经验记录】C#中，库函数有bug，会将http所返回的response响应中的headers头信息中
的Set-Cookie值，解析错误，丢失部分cookie

【已解决】又发现一个C#中解析Set-Cookie的一个bug：无故地添加cookie的path域

2.3. C#中处理URL

2.3.1. 关于UrlEncode把空格编码为加号"+"而不是%20的问题

默认的HttpUtility.UrlEncode会把空格编码为加号，而不是很多人所期望的%20，导致很
多人以为函数出了问题呢。其实，微软官方文档HttpUtility.UrlEncode方法中，已经解释
了此问题：

    您可以使用 UrlEncode 方法或 UrlPathEncode 方法对 URL 编码。

    但是，方法返回不同的结果。UrlEncode 方法将每个空格字符转换为加号 (+) 字符。

    UrlPathEncode 方法将每个空格字符转换为字符串 "%20"，它表示一个用十六进制表
    示法表示的空格。

    在对 URL 的路径部分编码时使用 UrlPathEncode 方法，以保证一致的已解码 URL，
    与执行解码的平台或浏览器无关。

所以，想要把空格编码为%20，用 UrlPathEncode 即可。

2.4. C#中的WebBrowser

C#中使用WebBrowser，相对还是很简单的，比如，打开网页，直接用uri即可：


wbsChaseFootprint.Url = new Uri(strEncodedFullFootprintUrl);



详见：【记录】C#中使用WebBrowser浏览google页面

2.4.1. DocumentCompleted被调用两次

目前的解决办法，在DocumentCompleted中加上：


if (!e.Url.Equals(wbsChaseFootprint.Url))
{
    //not actually complete, do nothing
    return;
}



详见：【已解决】C#中的WebBrowser的DocumentCompleted被调用两次

第 3 章 C#的处理Excel和CSV

目录

3.1. C#中处理Excel

    3.1.1. Excel自动适应列宽
    3.1.2. 选中Excel的某列后再去自动适应列宽
    3.1.3. Could not load file or assembly ‘Microsoft.Office.Interop.Excel
    3.1.4. Microsoft.Vbe.Interop.dll和office.dll

3.2. C#中处理CSV

3.1. C#中处理Excel

3.1.1. Excel自动适应列宽

全选所有列再调用AutoFit：


//auto adjust column width (according to content)
Range allColumn = xlWorkSheet.Columns;
allColumn.AutoFit();



详见：【已解决】C#中操作刚导出的Excel，设置其为自动调整列宽

3.1.2. 选中Excel的某列后再去自动适应列宽

核心逻辑：

获得对应的列的Range，再去AutoFit


Range firstColumn = xlWorkSheet.get_Range("A1");
firstColumn.EntireColumn.AutoFit();



详见：【已解决】C#中操作Excel文件实现单行的自动适应列宽+C#中如何选中Excel的某列

3.1.3. Could not load file or assembly ‘Microsoft.Office.Interop.Excel

使用C#操作Excel常会出现类似错误：

Could not load file or assembly ‘Microsoft.Office.Interop.Excel, Version=
14.0.0.0

其可能原因，现总结如下：

C#的exe用到了excel的话，希望拿到别人的地方也正常运行不报错的话，

其中，别的地方：

  • 可能没装excel
  • 也可能装了excel，但是版本低

那么有几种选择：

 1. 最好自己在exe中集成了对应的excel的dll库。

    关于，集成dll到exe中，简述为：

     1. 把dll加到自己的资源resource中
     2. 把dll加到自己的项目中，且属性设置为嵌入的资源（Embedded Resource）
     3. 自己的类的初始化函数中，加上对应的load dll的相关代码

    详见：【已解决】C#中集成DLL库到自己的exe程序中

 2. 即使不集成excel的dll，在引用excel的dll
     1. 也要尽量引用低版本

        比如别人已装的excel，即Office是Office12

        那么你也就不要去引用Office14==Office2010：

        C:\Program Files (x86)\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll

        了，而去引用Office12==Office2007：

        C:\Program Files (x86)\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office12\Microsoft.Office.Interop.Excel.dll

     2. 或者是和对方电脑中安装的excel的版本一致

        当然，最好确认一下，对方的电脑中装了哪个版本的Office（的excel）

        比如是Office 2010==Office14，那么你也就去引用对应的Office14：

        C:\Program Files (x86)\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll

        就好了。

详见：【已解决】虽然已经安装了Office的Excel但是C#的exe还是运行出错：Could not
load file or assembly ‘Microsoft.Office.Interop.Excel, Version=14.0.0.0

3.1.4. Microsoft.Vbe.Interop.dll和office.dll

Microsoft.Vbe.Interop.dll和office.dll，与Microsoft.Office.Interop.Excel.dll，一
起，都是属于“Microsoft Office system 的可用程序集”

所以，最好也是要在集成，Microsoft.Office.Interop.Excel.dll，时，连带的一起把：
Microsoft.Vbe.Interop.dll和office.dll都集成进来。

这样，才可以避免，别人在使用exe时，内部用到excel的dll时，完整的所需要的函数，都
可以自带的找到了。

不会再对于Microsoft.Vbe.Interop.dll或office.dll报错说找不到。

详见：【已解决】Microsoft.Vbe.Interop.dll和office.dll是啥

3.2. C#中处理CSV

第 4 章 C#的TreeView控件使用心得

目录

4.1. 添加Node的方法
4.2. 给TreeView添加图标
4.3. 获得右击所在节点
4.4. 添加右键菜单（Context Menu）
4.5. 给TreeNode添加名字

4.1. 添加Node的方法

当前已有一个TreeView控件trvFolder，添加节点的代码如下：

TreeNode curNode = new TreeNode(name);
trvFolder.Nodes.Add(curNode);


4.2. 给TreeView添加图标

先拖拽出来一个ImageList控件iglIcons，然后手动编辑ImageList的Images属性
Collection，添加几个小图片。

对应的index分别是0,1，2，。。。，然后用：

trvFolder.ImageList = iglIcons;

将其关联到当前TreeView控件trvFolder。

最后，每次添加TreeNode的时候，多加一句：

curNode.ImageIndex = 0;

即为：

TreeNode curNode = new TreeNode(name);
curNode.ImageIndex = 0;
trvFolder.Nodes.Add(curNode);


其中0为图片的index，可根据自己的实际情况改为对应的index。

这样就可以实现给不同的TreeNode添加对应的图标了。

4.3. 获得右击所在节点

选择的是别的节点，对着另外一个节点右击，要获得被右击的节点，主要代码是：


private void trvCategoryTree_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
    // Select the clicked node
    trvCategoryTree.SelectedNode = trvCategoryTree.GetNodeAt(e.X, e.Y);
}
}



之后通过cmsSelection_ItemClicked也就可以通过SelectedNode得到当前右击的那个
TreeNode了。

详见：【已解决】如何获得C#中右击弹出菜单时对应的当前所右击那个TreeNode

4.4. 添加右键菜单（Context Menu）

给TreeNode添加右键的总体的思路是：

 1. 添加ContextMenuStrip
 2. 在其下添加子菜单
 3. 实现ItemClicked事件，在其中判断点击了哪个子菜单，然后添加代码做你要做的事情
    即可。

详见：【已解决】C#中给TreeNode添加右键（Context Menu）

4.5. 给TreeNode添加名字

TreeNode加入到TreeView但是不显示名字，是因为加入的TreeNode没有设置Text

（虽然设置了TreeNode的Name，但是实际上用于显示的是Text而不是Name）

所以，在把TreeNode加入TreeView之前，设置好对应的Text即可正常显示。

详见：【已解决】给TreeView添加TreeNode节点后但是名字没显示出来

参考书目

[1] 正则表达式包含引号怎么办?

[2] c# Regex常用

[3] c#学习笔记《1》——regex类（个人理解）

[4] 为 Windows 窗体 TreeView 控件设置图标

[5] winform中利用Treeview模仿资源管理器实现图片文件列表

[6] C#键值对容器

