首先说明一下,我这里的异步方式是指获取代码文件的时候,采用的是异步方式,其原因就是我要搜索C:\Program Files文件夹下面的含有关键字为scyGroupBox的代码文件。那么,从我的描述就可以知道,这是一个相当费时的操作,而如果利用程序来遍历这个文件夹,其结果就是将要耗费数秒钟或者更长的时间,并且同步操作会导致界面在搜索完毕后才出来,这样用户就不得不等待几秒钟甚至是数十秒钟,这种体验是相当不友好的。

那么如何解决这种方式呢?

其实,利用委托方式结合其BeginInvoke和EndInvoke方法,可以先显示出界面,然后再加载搜索结果,这样一来,就大大提高了用户体验,具体方式如下:

在程序中,我需要将搜索到的代码文件加载到ListView控件中显示出来,这样就需要有一个函数来添加ListViewItem,代码文件如下:

     ///<summary>
/// filter the filename from some files and attach them to ListView control
///</summary>
///<param name="fileList"></param>
public void LoadFileIntoForm(List<FileInfo> fileList)
{
List<FileInfo> myFiles = fileList;

AddListViewCrossThreads(null,deleteFlag); // remove all the items

for (int i = 0; i < myFiles.Count; i++)
{
FileInfo file = myFiles[i];
ListViewItem lvi = new ListViewItem();
lvi.Text = GetFileName.GetFileName(file.FullName);
lvi.Tag = file.FullName; // store the fullname
AddListViewCrossThreads(lvi,addFlag);
}
}

而我们读取那个耗时的代码文件的函数如下: 

        ///<summary>
/// this function is the target to make async.
/// because it takes a long time to load.
///</summary>
///<returns></returns>
public List<FileInfo> LoadFileInfoAsync()
{
return LoadFiles.LoadFileByName();
}

需要注意的是,这个LoadFileInfoAsync函数需要耗费数秒甚至是数十秒来搜索文件,其具体实现如下:

        public List<FileInfo> LoadFileByName()
{
string keyWords = "scyGroupBox"; // file name contains words
string searchPath = @"C:\Program Files\"; // file path

DirectoryInfo directory = new DirectoryInfo(searchPath);

var result = from p in directory.GetFiles("*.cs",SearchOption.AllDirectories).ToList()
where p.Name.Contains(keyWords)
select p;
List<FileInfo> files = result.ToList(); // transfer filenames into list collection
return files; // return the result
}

其中SearchOption.AllDirectories表明搜索父文件夹下的子文件夹。

如何对LoadFileByName函数实现异步操作呢?

这个需要定义个委托,用委托来对这个函数进行异步操作:

        ///<summary>
/// this is the Begin Invoke method
///</summary>
public void InvokeListView()
{
//delegate the funtion that perform long running operation
AddListViewDelegate asyncAdd = new AddListViewDelegate(LoadFileInfoAsync);
//start to load the funtion asynchorous., GetFileInfoResult is the end process
IAsyncResult iar = asyncAdd.BeginInvoke(new AsyncCallback(GetFileInfoResult), asyncAdd);
//perform loading notification
AddListViewCrossThreads(AddNotification(),addFlag);
}

利用Delegate的BeginInvoke可以实现异步操作,在异步操作的过程中,我们可以向用户显示提示信息,比如说“正在加载,请稍等….”,这样能达到一种比较好的用户体验,至于这种提示信息,我们在函数AddNotification中实现:

        ///<summary>
/// add the loading notification
///</summary>
///<returns></returns>
public ListViewItem AddNotification()
{
ListViewItem lvi = new ListViewItem();
lvi.Text = "Loading now, please wait...";
lvi.Tag = 1;
return lvi;
}

但是现在遇到一个比较严重的问题,由于采用了异步,导致线程和界面发生了交互,这样就会产生exception,怎么解决呢,当然是利用Invoke方式来判断当前界面控件是否需要线程交互,如果需要,则利用委托方式来进行调用:

     ///<summary>
/// used to avoid the cross threads exception
///</summary>
///<param name="lvi">listview item</param>
///<param name="action">0:add 1:delete</param>
public void AddListViewCrossThreads(ListViewItem lvi,int action)
{
if (lsvName.InvokeRequired)
{
AddListViewCrossThreadDelegate addlistviewdelegate = new AddListViewCrossThreadDelegate(AddListViewCrossThreads);
lsvName.Invoke(addlistviewdelegate, lvi,action);
}
else
{
if (addFlag == action)
{
this.lsvName.Items.Add(lvi);
}
else if (deleteFlag == action)
{
this.lsvName.Items.Clear();
}
}
}

这样当异步进行完毕,我们就可以还原异步对象为当前代理对象,然后获取返回值了。

     ///<summary>
/// Invoke Async Complete
///</summary>
///<param name="iar"></param>
public void GetFileInfoResult(IAsyncResult iar)
{
AddListViewDelegate asyncAdd = (AddListViewDelegate)iar.AsyncState; // get the operation object
List<FileInfo> list = asyncAdd.EndInvoke(iar); // get the async result
LoadFileIntoForm(list); // add the ListViewItem result to ListView control
}

-----------------------------------------------------华丽的分割线--------------------------------------------------------

下面来说语法高亮,这个主要是采用了正则表达式,由于我写的不太完整,还请见谅,下面是主要代码,需要注意的是,在代码着色过程中,需要涉及到懒惰匹配法,意思就是最小匹配。需要用到(?i)来实现:

    public static void RichTextBoxEx(this RichTextBox richTextBox,string input)
{
richTextBox.Text = input;

input = input.Replace("\r\n","~"); //注意,\r\n会被认为是4字节,其实他只是一个字节,所以替换为一个字符来表示

//匹配关键字
string regexGrammer = @"using|namespace|
public|partial|
class|private|protected|
if|else|
int|string|double|
return|override|void|this|null|virtual
";
MatchCollection mc = GetMatchedValue(regexGrammer, input);
foreach (Match match in mc)
{
int length = match.Length;
int index = match.Index;
richTextBox.Select(index, length);
richTextBox.SelectionColor = Color.Blue;
}

//匹配对象
string classGrammer = @"color|GraphicsPath|rectangle|Graphics|pen|LinearGradientBrush";
MatchCollection classMc = GetMatchedValue(classGrammer,input);
foreach (Match match in classMc)
{
int length = match.Length;
int index = match.Index;
richTextBox.Select(index, length);
richTextBox.SelectionColor = Color.FromArgb(43,145,175);
}

//匹配注释符号-->//这里使用了懒惰匹配(?i)
string commentGrammer = @"(?i)\//.*?~";
MatchCollection commentMC = GetMatchedValue(commentGrammer,input);
foreach (Match match in commentMC)
{
int length = match.Length;
int index = match.Index;
richTextBox.Select(index, length);
richTextBox.SelectionColor = Color.Green;
}

//匹配#号
string regionGrammer = @"(?i)\#.*?~";
MatchCollection regionMC = GetMatchedValue(regionGrammer, input);
foreach (Match match in regionMC)
{
int length = match.Length;
int index = match.Index;
richTextBox.Select(index, length);
Font f = new Font(new Font("宋体",12), FontStyle.Bold);
richTextBox.SelectionFont = f;
}

//匹配数字
string digitGrammer = "[0-9]";
MatchCollection digitMC = GetMatchedValue(digitGrammer, input);
foreach (Match match in digitMC)
{
int length = match.Length;
int index = match.Index;
richTextBox.Select(index, length);
richTextBox.SelectionColor = Color.Red;
}
}

private static MatchCollection GetMatchedValue(string grammer,string input)
{
Regex digitRegex = new Regex(grammer, RegexOptions.IgnoreCase | RegexOptions.Singleline);
MatchCollection matchCollection = digitRegex.Matches(input);
return matchCollection;
}

好了,介绍到这里,基本上就完了,附上效果图:

 

posted on 2011-11-21 21:37  程序诗人  阅读(650)  评论(2编辑  收藏  举报