zoukankan      html  css  js  c++  java
  • 基于异步方式的语法着色器

    首先说明一下,我这里的异步方式是指获取代码文件的时候,采用的是异步方式,其原因就是我要搜索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;
    }

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

     

  • 相关阅读:
    Hibernate ManyToOne Mappings 多对一关联映射
    CF860E Arkady and a Nobody-men
    23种设计模式(23):代理模式
    23种设计模式(22):享元模式
    23种设计模式(21):外观模式
    23种设计模式(20):装饰模式
    23种设计模式(19):组合模式
    23种设计模式(18):桥接模式
    23种设计模式(17):适配器模式
    23种设计模式(16):状态模式
  • 原文地址:https://www.cnblogs.com/scy251147/p/2257681.html
Copyright © 2011-2022 走看看