探索者命令式的三分

屏幕打印显示搜索菜单,ProgressBar和目录目前正在处理 文本框中显示的背景线程。 搜索一个可响应的UI 在前面的Explorer命令式中,我添加了一个目录搜索函数并启用了 菜单选择。我对它进行了测试,它在我的XP系统上看起来相当酷。它运行得非常快 在我的XP。以我对现实的短视来看,我认为每个人都会有同样的惊叹 然后我买了一台现代化的电脑,上面运行着现代化的操作系统, 当我有时间的时候,我下载了最新版本的f#和。net 启动我的小玩具程序。在Windows 7上看起来更好,直到我搜索了一下 然后,我的呕吐,它冻结了!让我来解决这个问题吧,我想。我无法挪动窗户检查 当我切换到控制台时,主窗口消失了。 我无法退回去。 但后来我被别的事情打断了,当我回去时,那扇窗户又出现了。我 刚刚经历了由于长时间运行的IO进程阻塞GUI线程而导致的UI无响应。 对于那些因为这次经历而对f#的看法降级的人,我表示诚挚的歉意。如果你 如果您能给我这个机会,我会努力提高您对f#的评价和它的能力。 但我必须脱离严格的命令式范式,引入一些更强大的, 因此,它不容易理解,赋予f#权力的功能构造 多功能性。 这篇文章是一个面子(我的脸),我将试图解释我如何使用 类型扩展,以向阻塞的长时间运行的IO例程添加异步处理 GUI线程。类型扩展的代码是微软公司的版权财产 可以在Microsoft帮助库的后面找到Async.FromContinuations< T> 方法(f#)”。你必须参考该文件作为正式和正确的描述 BackgroundWorker类。我的评论应该被看作是对我为什么构建 像我这样使用它的代码。这并不是说这是最好的或唯一的方法,而只是作为一种方法 将背景工作人员集成到程序中的可能方法。f#类型定义是an 对“System.ComponentModel.BackgroundWorker”中BackgroundWorker类的扩展。“与” 关键字将此类型定义标识为类型“BackgroundWorker”的“扩展”。这 extension创建了一个异步计算,在RunWorkerCompletedEventHandler中, “cont”(成功延续)、“econt”(异常延续)和“ccont”(取消延续)。 回调最终将调用其中一个。我对这个过程的理解是"处理器" 是否会根据“RunWorkerCompletedEventHandler”事件选择“From”三个“Continuations” 当“RunWorkerCompleted”为(completed,即发生RunWorkerCompleted事件)时,会出现“args”。而 'AsyncRunWorker'是计算你的'计算'你的'worker'例行'报告进展'以及 跟踪错误和取消请求。 下面是类型扩展的代码 隐藏,复制Code

type BackgroundWorker with
  member this.AsyncRunWorker (computation, argument : 'T, progressChangedHandler) : Async<'U> =
    let workerAsync =
      Async.FromContinuations (fun (cont, econt, ccont) ->
        let handler = new RunWorkerCompletedEventHandler (fun sender args ->
          if args.Cancelled then
            ccont (new OperationCanceledException()) 
          elif args.Error <> null then
            econt args.Error
          else
            cont (args.Result :?> 'U))
        this.WorkerSupportsCancellation <- true
        this.WorkerReportsProgress <- true
        this.DoWork.AddHandler(new DoWorkEventHandler(fun sender args ->
          args.Result <- computation(argument, this, args)))
        this.ProgressChanged.AddHandler(progressChangedHandler)
        this.RunWorkerCompleted.AddHandler(handler)
        this.RunWorkerAsync(argument)
        )

    async { 
      use! holder = Async.OnCancel(fun _ -> this.CancelAsync())
      return! workerAsync
      }

简而言之,这种类型扩展通过在后台执行来恢复对GUI的响应性, 允许取消任务,也允许异常处理和进度报告给 GUI线程。为了利用这些可能性,我添加了一个取消搜索选择 文本框上方的搜索菜单和进度条。的中显示搜索状态输出 文本框。 背景 所有主要的IDE都有一个优秀的内置搜索功能,通常有一个替换 能力。Windows 7在操作系统中内置了一个很棒的搜索功能 本身。您可以在开始菜单或Windows资源管理器中使用它。这是太棒了! 但有时这并不是我想要的。有时候我真想跳!不幸的是 (或者幸运的是,这种情况并不经常发生,所以我记不住所有这些开关) 他们做什么,甚至我把书放在哪里。通常我想看到的是这条线 出现一些字符串或关键字,但我也想看到行前和行后。而且, 虽然我可能想浏览整个文件,但我不想在编辑器或IDE中打开它 因为它改变了“最近”文件或项目的列表。不久之后,这个列表甚至都没有了 包含我正在处理的任何东西。我在记事本中打开它们,使用它的查找函数。 通常,控制台窗口中的三行“context”输出显示了我要查找的内容。 之前,我在浏览器中添加了文件搜索和“上下文”输出功能 命令式和我打算添加一个'发现'的功能到文本框时,我可以'发现'的时间。 然后我发现,在一台Windows 7机器上,文件搜索阻塞了UI。其他之前在这 这两个问题我都会讲到。 后台工作 上面显示的“type extension”提供了成员“this”。AsyncRunWorker与回报 类型Async<'U>(一般的异步类型),其参数是'计算'(你必须 提供)、“参数”(泛型类型,“T”)和“progressChangedHandler”。参数的类型 必须与定义“计算”的let绑定中命名的参数的类型相同。 在这种情况下,它是“让parseDirsAsync(路径,worker: BackgroundWorker, eventArgs: DoWorkEventArgs) =”。为了澄清这些连接,“parseDirsAsync”是“计算”, “path”是“参数”,“worker”是在调用的代码中定义的lambda表达式 BackgroundWorker例程。下面是计算的代码。 隐藏,收缩,复制Code

let parseDirsAsync (path, worker: BackgroundWorker, eventArgs: DoWorkEventArgs) =
   // Define the computation 
   let mutable root = ""
   if File.Exists(path) then do // it's a file.extract the directory and fname
       fname <- path.Substring(path.LastIndexOf("\\") + 1)
       root  <- path.Substring(0,path.LastIndexOf("\\"))
   else // it's a directory. use it and current fname
       root <- path
   printfn "Searching for %s in %s and it's subdirectories." fname root
   // create 3 empty string arrays on the heap
   let fileList:string[] ref = ref (Array.create 1 "")
   let files:string[] ref = ref (Array.create 1 "")
   let subDirs:string[] ref = ref (Array.create 1 "")
   let gotIt = ref false //create a bool on the heap
   let rec fileSearch root = //Define the recursive function
       gotIt := false //':=' operator puts a value in the ref cell
       try
         files := Directory.GetFiles(root, fname, SearchOption.AllDirectories)
         gotIt := true // only happens when getfiles succeeds
       with
         | :? System.UnauthorizedAccessException -> ()
         | _  as oops -> invalidOp <| sprintf "%O" oops
       if (worker.CancellationPending) then
          eventArgs.Cancel <- true
       elif !gotIt = true then do // '!' operator dereferebces a ref cell
          let mutable count = 0
          fileList := Array.append !fileList !files // build the fileList
          let len = files.Value.Length //number of array elements
          if len > 0 then printfn "\nIn %s" root
          for fi in !files do
             printfn "    found %s" fi
             count <- count + 1 // builds from 0 to len
             let percentComplete = int ( ((float count) / (float (len))) * 100.0)
             worker.ReportProgress(percentComplete, fi)
       else do
          gotIt := false //make sure it's false now
          try
              files := Directory.GetFiles(root, fname) //, SearchOption.TopDirectoryOnly)
              gotIt := true
          with
              | :? System.UnauthorizedAccessException -> ()
              | _  as e -> invalidOp <| sprintf "%O" e
          if (worker.CancellationPending) then
              eventArgs.Cancel <- true
          elif !gotIt = true then do
              let mutable count = 0
              fileList := Array.append !fileList !files  //|>ignore
              let len = files.Value.Length
              if len > 0 then printfn "\nIn %s" root
              for fi in !files do
                 printfn "    found %s" fi
                 count <- count + 1
                 let percentComplete = int ( ((float count) / (float (len))) * 100.0)
                 worker.ReportProgress(percentComplete, fi)
          try
              subDirs := Directory.GetDirectories(root)  //, "*", SearchOption.TopDirectoryOnly)
              gotIt := true
          with
              | :? System.UnauthorizedAccessException -> ()
              | e -> invalidOp <| sprintf "%O" e
          if (worker.CancellationPending) then
              eventArgs.Cancel <- true
          else do
              if !gotIt = true then do
                 let mutable count = 0
                 let len = subDirs.Value.Length
                 for di in !subDirs do
                    count <- count + 1
                    let percentComplete = int ( ((float count) / (float (len))) * 100.0)
                    worker.ReportProgress(percentComplete, di)
                    fileSearch di
   fileSearch root  //Execute the file search function
   !fileList        //return fileList as the result

为阐明例程的意图,我们将递归地获得匹配的所有文件 在帧模式。如果可以,我们可以从所有目录中获取文件。如果成功了,我们就完了 否则,我们从顶部目录获取文件,然后递归所有子目录 前目录。由于fileSearch表达式是一个闭包,所以我们不能捕捉可变值 在它。我们必须传递堆上的所有东西。这就是为什么我们必须使用'ref' !”和“:=”。 因为我们在另一个函数中定义了“fileSearch”,所以我们必须在同一个函数中调用它。 然后返回已构建的文件列表'!fileList'。 调用BackgroundWorker 下面的一小段代码破坏了表达式“计算”及其所有部分。联系 let绑定的代码片段定义了parseDirsAsync,‘value’是‘path’参数。λ 表达'(fun sender eventArgs ->'是参数列表中的'worker'。实际上, 值'parseDirsAsync'是传递给worker的函数,worker是parseDirsAsync的一个参数。 f#编译器理解这一点,但我仍在考虑它。 第二行和第三行安全地更新UI,报告正在处理的当前目录 以及已完成的目录的百分比。因为我们不知道最终结果 将被处理的文件或目录的数量,我们已经任意选择报告进展 对于我们正在处理的目录,而不是整个作业。 第四行'Async.StartWithContinuations(…)'在后台启动计算 将成功的结果提供给第一个延续,将异常的结果提供给第二个延续 而取消的结果则是第三次延续。注意,这个代码片段前面有一些 在other中,后面跟着在GUI线程上完成处理的代码 单词,三个连续词。 隐藏,复制Code

let computation value = worker.AsyncRunWorker(parseDirsAsync, value, (fun sender eventArgs ->
  textBox1.Text <- "Scanning ... " + eventArgs.UserState.ToString()
  myProgressBar.Value <- (float (eventArgs.ProgressPercentage)) ))
Async.StartWithContinuations( computation value, (fun result ->

此代码段后面的行包含成功延续的主体,该延续终止 加上一个结束括号,一个逗号和一个开始括号,还有一个有趣的符号,意思是 用于异常延续的lambda表达式的开头。这个终止于 相同的模式,Lparen,逗号,Rparen后面跟着fun关键字开始第三个继续 处理一个取消。取消继续以结束括号结束 后面跟着另一个结束异步的结束括号。StartWithContinuationsλ 表达式本身。在成功延续中,我添加了进入文件列表队列所需的代码 并通过在数组中附加一个空格来删除嵌入到列表中的任何空白字符串 字符串。然后,我将队列定位到下一个可用项,以匹配当前项 当前项在队列中,以便找到下一个匹配项。如果当前项不在 队列,我们定位到队列的开始。我们找到该项目并试图对其进行定位 在视窗的中间。这是在一个名为“updateMyScreen”的函数中完成的。其他两个 延续在文本框中放置一条消息,指示发生了错误或取消 将“fileSearchStarted”标志设置为“false”,以便构建的下一个搜索菜单可以启用 所有的异步搜索单击和禁用取消单击。当某些条件 不执行异步文件搜索。我们唯一需要做的就是打电话给 findNextFile()和updateMyScreen()例程。 下面是用于异步文件搜索的菜单请求处理程序 隐藏,收缩,复制Code

let findFileReq (myProgressBar:ProgressBar) (textBox1:TextBox) value =
  try
     let mutable currentDir = ""
     let mutable currentfname = ""
     let temp = focusItem.Tag.ToString()
     if File.Exists(temp) then do
        currentDir  <- temp.Substring(0,temp.LastIndexOf("\\"))
        if fname = "???" || fname = "" then do
            currentfname <- temp.Substring(temp.LastIndexOf("\\") + 1)
     else
        currentDir <- temp
     if fname <> "" then
       fSfname <- fname
     elif currentfname <> "" then
       fname <- currentfname
       fSfname <-currentfname
     else
       fSfname <- "???"
     //endif
     if (fQue.Count = 0) || (fSfname <> fTfname) then
        fname <- fSfname
        let targetFile = currentDir + "\\" + fname
        printfn "\nCurrent File is: %s." targetFile
        fQue.Clear()
        mainWindow.Title <- "Searching for  " + fname
        let worker = new BackgroundWorker()
        fQue.Clear()
        fileSearchStarted <- true
        textBox1.Text <- "Computing..."
        let computation value = worker.AsyncRunWorker(parseDirsAsync, value, (fun sender eventArgs ->
            textBox1.Text <- "Scanning ... " + eventArgs.UserState.ToString()
            myProgressBar.Value <- (float (eventArgs.ProgressPercentage)) ))
        Async.StartWithContinuations( computation value, (fun result -> 
            printfn "\nThe following files have been Enquwued:"
            Array.iter (fun elem -> (printfn "%s" elem;fQue.Enqueue elem)) result
            fileSearchStarted <- false
            startInDir <- fQue.Dequeue()
            while startInDir = "" && fQue.Count <> 0  do
               startInDir <- fQue.Dequeue()
            fQue.Enqueue(startInDir)
            if fQue.Contains(targetFile) then
               while startInDir <> targetFile do
                  startInDir <- fQue.Dequeue()
                  fQue.Enqueue(startInDir)
            let mutable qItem = new TreeViewItem()
            let mutable pItem = new TreeViewItem()
            for i in 0 .. (treeTrunk.Items.Count) - 1 do
               pItem <- treeTrunk.Items.[i]:?>TreeViewItem
               if startInDir.Contains(pItem.Tag.ToString()) then 
                  findIt pItem
                  textBox1.Text <- (focusItem.Tag.ToString())
               done
            mainWindow.Title <- focusItem.Tag.ToString()
            myProgressBar.Value <- 0.0
            findNextFile()
            printfn "Current File is:\n%s" (startInDir)
            updateMyScreen()
            // the end of the success continuation
            ),
            (fun exn -> 
               textBox1.Text <- "Operation failed with error:" + exn.Message
               fileSearchStarted <- false
               // the end of the exception continuation
               ),
            (fun _ -> 
               textBox1.Text <- "Operation canceled."
               fileSearchStarted <- false
               // the end of the cancellation continuation 
            )  // the end of StartWithContinuations
     else
        findNextFile()
        printfn "Current File is:%s\n" (startInDir)
        updateMyScreen()
  with
     |e -> eprintf "Error: "

“startHereReq”请求调用“findFileReq”,但将其传递给当前目录 的默认根目录。这使用户可以在任何驱动器上的任何地方搜索。 “findString”例程本身与资源管理器命令的Partie Deux中的文件相同 嵌入在'findStringReq'中的搜索已经被更改为后台版本。搜索的字符串 它本身不是异步的,但是速度很快避免长时间阻塞GUI。 我知道有些人可能不同意,但与其争论这个观点,不如请他们写一个新的 使用此类型扩展的后台字符串搜索或他们想使用的任何其他技术。 菜单。开始异步文件搜索 屏幕打印显示搜索菜单与文件名从屏幕和邀请“打我!” 开始搜索“??” 为了防止用户在搜索过程中意外地启动搜索,有必要 当搜索开始时禁用选择。我们还需要保持弹出菜单打开,以便用户可以 如果他们想的话,取消搜索。但我们需要关闭弹出菜单时,搜索已完成顺序 构造一个重新启用选择的新弹出菜单。这意味着我们必须关闭弹出菜单。 因此,必须在模块级别定义弹出窗口。这使得我们可以在需要的时候关闭它。请注意 只在模块级别创建弹出窗口。它被命名为menuPopup。菜单被放到弹出窗口中 在菜单构建器例程的末尾。我们将关闭'updateMyScreen()'函数中的弹出窗口。我们所做的 这是通过设置它的'IsOpen'标志为假。这是密码。隐藏,复制Code

menuPopup.IsOpen <- false

我们还必须设置一个标志,表明搜索已经完成。它被命名为“fileSearchStarted” 在模块级别设置为false,在成功时,异步的异常和取消延续 调用。它在调用之前被设置为true。时在搜索菜单项中测试 菜单的构造。这是必要的,因为用户可以通过单击outside来强制关闭菜单 当出现这种情况并且用户决定有必要取消搜索时, 该菜单是在只启用取消选择的情况下构建的。本文开头的丝网印刷 显示正在进行搜索的菜单。注意,除了取消选择之外,所有内容都变灰了。 还要注意,查找下一个文件和查找下一个…括号内带有“F3”和“F4”的字符串选择。 按“F3”或“F4”键会做同样的事情,按下其中一个选择。这些函数 将循环通过队列。键调用的函数已被更新,以查找字符串中的字符串 文本框。当cursur鼠标悬停在文本框上时,事件处理程序已经添加到高亮显示选中的单词。 我最初“添加”了一个例行程序和一个触发程序,但当我看到这些例行程序是如此的短,我改变了它 一个lambda表达式。然而,我在程序中保留了原始代码。下面是其中一个的代码。 隐藏,复制Code

textBox1.MouseEnter.Add(fun _ ->
   textBox1.Focus()|>ignore
   )

当Mouae指针在文本框上时,该表达式触发。它所做的就是把焦点转移到 并扔掉文本框中的类型信息。另一个在这个下面。它所做的就是切换 当鼠标离开时聚焦到树视图。它们都在主窗口的末端。加载lambda表达式 这实际上不是菜单的一部分,但在这里是相关的,因为“F3”键将发现下一个出现 文本框中的字符串。如果你在看树状视图鼠标指针正好在文本框的上方 你可能会错过正在发生的事情。 现在回到菜单上。我在Partie Deux中讨论了菜单,所以我只讨论新代码。我们 已经创建了menuPopup弹出窗口,所以当菜单被添加到其中时不要感到困惑。这是代码 浏览“搜索”菜单。它采用代码片段的形式来缩短文章。 隐藏,收缩,复制Code

let menuReq(e:MouseButtonEventArgs) =
  try
     //let menuPopup = new Popup() - now created at module level, Line 291
     .....
     let pmS = new MenuItem()
     pmS.Header <- "Search"
     let pmSfF = new MenuItem()     // We create 
     let pmSfFsH = new MenuItem()   // all of the
     let pmSfS = new MenuItem()     // MenuItems
     let pmSfFsN = new MenuItem()   // in order to
     let pmSfFsS = new MenuItem()   // enable or 
     let pmSasynCan = new MenuItem()// disable them enmass
     if fileSearchStarted = true then
        pmSfF.IsEnabled <- false
        pmSfFsH.IsEnabled <- false
        pmSfS.IsEnabled <- false
        pmSfFsN.IsEnabled <- false
        pmSfFsS.IsEnabled <- false
        pmSasynCan.IsEnabled <- true
     else
        pmSfF.IsEnabled <- true
        pmSfFsH.IsEnabled <- true
        pmSfS.IsEnabled <- true
        pmSfFsN.IsEnabled <- true
        pmSfFsS.IsEnabled <- true
        pmSasynCan.IsEnabled <- false        
     let pmSfFHeader = new TextBox()
     pmSfFHeader.Text  <- "Search For File"
     ... 
     pmSfF.Tag <- thisItem.Tag.ToString()
     pmSfF.StaysOpenOnClick <- true
     pmSfF.Click.Add(fun args -> 
        findFileReq myProgressBar textBox1 (Directory.GetDirectoryRoot(thisItem.Tag.ToString()))
        pmSfF.IsEnabled <- false   // In the line above we are passing the progressBar,
        pmSfFsH.IsEnabled <- false // textBox1 and the root of the directory.
        pmSfS.IsEnabled <- false   
        pmSfFsN.IsEnabled <- false
        pmSfFsS.IsEnabled <- false
        pmSasynCan.IsEnabled <- true
        ) 
     pmSfF.Header <- pmSfFs
     let pmSfFToolTip = new ToolTip()
     pmSfFToolTip.FontSize <- sizeOfFont * 2.0
     pmSfFToolTip.FontWeight <- FontWeights.ExtraBold
     pmSfFToolTip.Content <- "HIT ME ! (To Start File Search)"
     pmSfF.ToolTip <- pmSfFToolTip
     let _ = pmS.Items.Add(pmSfF)
     pmSfFsH.Header <- "Start File Search Here"
     pmSfFsH.Tag <- thisItem.Tag.ToString()
     pmSfFsH.StaysOpenOnClick <- true
     pmSfFsH.Click.Add(fun args -> 
        fTfname <- "..." // Here we set the values to force an asynchronous file search 
        fSfname <- "???" // Note below we are passing the progressBar, textBox1 and the current directory
        findFileReq myProgressBar textBox1 (thisItem.Tag.ToString())
        pmSfFsH.IsEnabled <- false
        pmSfF.IsEnabled <- false
        pmSfS.IsEnabled <- false
        pmSfFsN.IsEnabled <- false
        pmSfFsS.IsEnabled <- false
        pmSasynCan.IsEnabled <- true
        ) 
     let pmSfFsHToolTip = new ToolTip()
     pmSfFsHToolTip.FontSize <- sizeOfFont * 2.0
     pmSfFsHToolTip.FontWeight <- FontWeights.ExtraBold
     pmSfFsHToolTip.Content <- "HIT ME ! (To Search this Directory and it's SubDirectories for File)"
     pmSfFsH.ToolTip <- pmSfFsHToolTip
     let _ = pmS.Items.Add(pmSfFsH)
     let cfilePan = new StackPanel()
     cfilePan.Orientation <- Orientation.Horizontal
     let mutable cargBox = new TextBox()
     cargBox.Text <- "Sratch For String"
     cargBox.Margin <- new Thickness(10.0,2.0,2.0,2.0)
     cargBox.MinWidth <- 15.0
     cargBox.IsReadOnly <- true
     ...
     cfileBox.LostFocus.Add(validateFileBox)
     cfileBox.IsReadOnly <- false
     ...
     let mutable argBox = new TextBox()
     argBox.Margin <- new Thickness(10.0,2.0,2.0,2.0)
     argBox.MinWidth <- 40.0
     let fndStrToolTip = new ToolTip()
     fndStrToolTip.FontSize <- sizeOfFont
     fndStrToolTip.FontWeight <- FontWeights.Bold
     fndStrToolTip.Content <- "Enter or change the string to search for"
     argBox.ToolTip <- fndStrToolTip
     if fndStr = "" then
      argBox.Text <- "???"
      fndStr <- "???"
     else
      argBox.Text <- fndStr
     argBox.LostFocus.Add(validateArgBox)
     argBox.IsReadOnly <- false
     let mutable inBox = new TextBox()
     inBox.Text <- " in "
     inBox.Margin <- new Thickness(10.0,2.0,2.0,2.0)
     inBox.MinWidth <- 15.0
     inBox.IsReadOnly <- true
     let _ = cfilePan.Children.Add(cargBox)
     let _ = cfilePan.Children.Add(argBox)
     let _ = cfilePan.Children.Add(inBox)
     let _ = cfilePan.Children.Add(cfileBox)
     pmSfS.Header <- cfilePan
     pmSfS.Tag <- thisItem.Tag.ToString()
     pmSfS.StaysOpenOnClick <- true
     pmSfS.Click.Add(fun args -> 
        findStrReq myProgressBar textBox1 (Directory.GetDirectoryRoot(thisItem.Tag.ToString()))
        pmSfS.IsEnabled <- false // Here we are passing the progressBar, the TextBox1 
        pmSfF.IsEnabled <- false // and the root of the directory
        pmSfFsH.IsEnabled <- false
        pmSfFsN.IsEnabled <- false
        pmSfFsS.IsEnabled <- false
        pmSasynCan.IsEnabled <- true
        ) 
     let pmSfSToolTip = new ToolTip()
     pmSfSToolTip.FontSize <- sizeOfFont * 2.0
     pmSfSToolTip.FontWeight <- FontWeights.ExtraBold
     pmSfSToolTip.Content <- "HIT ME ! (To Find File with this String in the Queue)"
     pmSfS.ToolTip <- pmSfSToolTip
     let _ = pmS.Items.Add(pmSfS)
     pmSfFsN.Header <- "Find Next File in Queue(F3)"
     pmSfFsN.Click.Add(findNextReq) // Here we are passing nothing
     let pmSfFsNToolTip = new ToolTip()
     pmSfFsNToolTip.FontSize <- sizeOfFont * 2.0
     pmSfFsNToolTip.FontWeight <- FontWeights.ExtraBold
     pmSfFsNToolTip.Content <- "HIT ME ! (Or HIT PF3 To FIND NEXT File in the Queue)"
     pmSfFsN.ToolTip <- pmSfFsNToolTip
     let _ = pmS.Items.Add(pmSfFsN)
     pmSfFsS.Header <- "Find Next File with this String(F4)"
     pmSfFsS.Click.Add(findNextStrReq) // Here we are passing nothing
     let pmSfFsSToolTip = new ToolTip()
     pmSfFsSToolTip.FontSize <- sizeOfFont * 2.0
     pmSfFsSToolTip.FontWeight <- FontWeights.ExtraBold
     pmSfFsSToolTip.Content <- "HIT ME ! (Or HIT PF4 To Find Next File with this String in the Queue)"
     pmSfFsS.ToolTip <- pmSfFsSToolTip
     let _ = pmS.Items.Add(pmSfFsS)
     pmSasynCan.Header <- "Cancel Search in Progress"
     pmSasynCan.Click.Add(fun args -> Async.CancelDefaultToken() )
     let pmSasynCanToolTip = new ToolTip() // The lambda function above cancels any search that might be running
     pmSasynCanToolTip.FontSize <- sizeOfFont * 2.0
     pmSasynCanToolTip.FontWeight <- FontWeights.ExtraBold
     pmSasynCanToolTip.Content <- "HIT ME ! (To Cancel the Asynchronous Search mow in Progress)"
     pmSasynCan.ToolTip <- pmSasynCanToolTip
     let _ = pmS.Items.Add(pmSasynCan)
     let _ = popupMenu.Items.Add(pmS)
     .....

TextBox的FindString函数 现在说另一件事,也就是说,完全不一样。文本框“查找字符串”例程。它不 有一个菜单选择甚至一个事件处理程序。或者至少不是一个专门针对“查找”的事件处理程序。 相反,它是在'textBoxKeyUpDetected'处理程序例程。目前它只能识别“F3”功能键,但是 可以被编码来响应任何键。我把分析留给那些对理解它感兴趣的人。 TextBox FindString函数的代码 隐藏,复制Code

let textBoxKeyUpDetected(e:KeyEventArgs) =
 try
   if foundAt = -1 then
     foundAt <- textBox1.Text.IndexOf(fndStr, foundAt + 1)
   if e.Key.ToString() = "F3" then
     foundAt <- textBox1.Text.IndexOf(fndStr, foundAt + 1)
     if foundAt = -1 then
       foundAt <- textBox1.Text.IndexOf(fndStr, foundAt + 1)
     fndStrInd <- textBox1.GetLineIndexFromCharacterIndex(foundAt)
     mainWindow.Title <- sprintf "Line #:%d" fndStrInd
     textBox1.Select(foundAt, fndStr.Length)
 with
  |e -> eprintf "\n\n Error: %O\n" e

使用异步文件搜索和FindString函数 显示上下文菜单的备用菜单格式的屏幕打印。这导致了相同的惯例存在 与弹出菜单格式一样调用。 在屏幕上显示打印上面的上下文菜单。对于这两种格式,选择是相同的,即使它们 在不同的程序中构造。当按住鼠标右键时,将调用弹出格式。菜单 将捕获鼠标指针,如果它在菜单之上,否则上下文菜单将被调用时鼠标右键 按钮被释放,它将捕获鼠标指针。PreviewRightMouseButton<下| Up>事件处理程序可以 更改为两者都调用相同的菜单,或者其中一个可以被注释掉。 屏幕打印显示成功搜索字符串"?? "的结果。选中的字符串为 当鼠标指针在文本框上时高亮。 在上面的屏幕打印中,我们执行了一个异步文件搜索,从“搜索字符串…”在文件……” 菜单选择。如果鼠标指针在树状视图上方,找到的文件将被高亮显示。如果鼠标 指针在文本框上方,选中的字符串(找到的字符串)将被高亮显示。下面的拆分器栏 文本框可以向上或向下移动,以减少或增加文本框的高度。在文本框中f3将找到 下一个出现的搜索字符串,F4将不做任何事情。在TreeView中,F3会转到队列中的下一个文件 F4将在队列中查找包含搜索字符串的下一个文件。 屏幕打印显示异步文件搜索的控制台输出,以及搜索成功时的深度。 在上面的屏幕打印中,我们有一个输出,它显示了找到的文件以及在查找文件时搜索的深度 被发现。您可以通过比较以' in…'开头的组标题来判断它是否在顶部目录中找到。”, “发现……与在MyPdfes目录中一样,它在顶部目录中找到一个文件。这个信息可能是,也可能不是 对你感兴趣。在这组信息之后是已加入队列的文件列表。 屏幕打印显示找到字符串参数的行以及前一行和后一行 按照正确的顺序。 在上面的屏幕打印中,加入队列的文件列表后面是显示搜索哪些文件的消息 字符串。该组后面是每次出现的上下文,即前一行、行本身和后一行。 使用此输出,您可以快速了解单词在文件中是如何使用的,并确定希望更仔细地检查哪个单词出现 在文本框中。文本框可以扩展到全屏和查找功能,您可能想使用它,而不是,或在 结合记事本。 屏幕打印显示鼠标指针悬停在一个扩展的文本框,“??”,“行#:592” 窗口标题和控制台输出显示找到字符串参数的行,包括592。 在屏幕上打印窗口标题中的'Line #:592'指的是找到所选词的那一行,而不是第一行 行可见的视口。使用此输出,您可以快速了解单词在文件中是如何使用的,并确定出现的单词是哪个 希望更仔细地检查文本框,将控制台输出中的行号与窗口中显示的行号匹配 标题。文本框可以扩展到全屏,与查找功能,您可能想使用它,而不是,或结合 记事本。一个快速的提醒-在文件菜单中,打开,执行和CMD.EXE选择可以用来“打开”一个文件。在大多数情况下, “Open”和“Execute”是等价的,因为“Open”文件执行的是定义为打开它的程序。”。doc'用Word打开,'.pdf' 由adobereader打开)。这包括“打开”文件夹,因为Windows资源管理器是“打开”文件夹的程序。该菜单项 CMD。EXE'的结果与'Open'和'Execute'相同,但是会接受额外的参数。这样做的目的是让您能够执行 批处理文件,传递参数给它或传递TreeView中的文件名给批处理文件。例如,右键单击一个文件 . txt扩展。在“文件”菜单中,您可以通过“打开”、“执行”或CMD.EXE菜单选项“打开”该文件。另一方面,使用开放 bat文件的菜单选择将“执行”该文件,但不会像执行菜单选择那样向它传递amything。与 例如,在CMD.EXE菜单选择中,您可以在第一个输入字段中输入“type”或“notepad”,以查看其内容或输入 参数,并在浏览器命令中运行它。您还可以使用新的菜单选择来创建 批处理文件,不需要任何参数并打开它。记住“另存为”,并将文件类型改为“*.*”。最后一件事, 打开和执行使用一个Comspec实例,但是在完成时关闭它,而CMD.EXE让它保持打开状态。 历史 历史一部分三 本系列的第三集的目的是在文本框中添加一个查找字符串。然后我看到了它能坚持多久 在搜索大型目录树时阻塞用户界面。我意识到从用户的角度来看这是非常糟糕的。我找到了一个解决办法 这不仅使UI没有被阻塞,而且还为用户提供了一些娱乐,让他们对监视ProgressBar感兴趣 文本闪过,然后窗口会重新绘制如果你翻转到互联网,然后再翻转回来。如果这个片段集合 本来是打算有一个特定的目的,那么这将会是什么呢做一个bug修复,但是除了四处闲逛和探索之外,没有计划的目的 升F,然后问自己,“我不知道我能不能做到”,不管这到底是什么。只是碰巧,一个网格似乎属于窗户 一棵树属于一个网格。从那时起,它就开始摇晃了。它摸索着形成了自己,就在丰满中摸索着向前走去。在其他 词语,是它自己发明的。 下面是本文所做更改的摘要。 为BackgroundWorker添加了一个“类型扩展”。这允许异步处理长时间运行的函数,释放阻塞的UI,供给 返回ProgressBar更新和状态信息,使取消请求和异常处理在后台线程。将目录解析例程从递归函数改为包含递归表达式的函数,它是一个闭包 来自外部范围的可变值,需要使用ref单元格来传递这些值。将创建菜单弹出窗口的代码移动到模块级别,这样它就可以在菜单例程之外关闭。改变了搜索菜单和它的处理程序来处理调用异步处理和防止异步进程的同时执行, 导致不可预知的结果增加例程切换焦点到文本框时,鼠标进入和返回到树状视图时,鼠标离开。增加了'查找字符串'功能的文本框。由'F3'调用,它将用'SelectionBrush' hi-lite '字符串,并显示行号 窗口标题。这允许用户匹配从控制台窗口上下文输出的行号,增加了'查找队列中的下一个文件与这个字符串'。由“F4”调用,它将遍历排队的文件,搜索下一个文件 包含当前搜索字符串的。增加了例行程序“updateMyScreen”,如果可能的话,可以将TreeViewItem的焦点移到屏幕中间。注释掉处理鼠标滚轮倾斜的代码,因为只有当鼠标驱动程序不产生时,滚动才会被困在水平状态 适当的δ值。改变了初始窗口大小,以适应Windows 7和Windows XP的物理屏幕。更新了#I和#r交互指令,只包含WPF V4.0搜索路径和必要的库引用。这是 这也是WPFEventLoop所必需的。防止fsx .exe使用WPF V3.0库从WPF事件循环安装而不是 脚本中引用了WPF V4.0,导致在交互执行中未定义在编译版本中定义的名称。同时, 如果没有安装WPF事件循环,则相同的脚本没有未定义的名称。使用当前的#I和#r指令运行脚本 Windows 7和XP,不管它是编译程序还是脚本,都会产生相同的输出。在脚本结束时添加两个分号,以便粘贴到交互控制台。 历史一部分两 本文将专门讨论添加到Explorer Imperative中的新功能,特别是搜索功能, 为了便于演示,我们将重点介绍查找字符串函数。由于需要调用弹出菜单和上下文菜单,本文将对它们进行讨论 搜索方法。我考虑菜单的所有代码,包括增加和减少菜单字体大小的函数 作为新代码,即使它是在原始程序中,因为当时没有讨论。尽管menues自己工作 他们做了,现在代码没有出现在文章中。这没关系,因为我也没有提供任何有关代码的解释。为 这就是为什么我不会列出任何代码chamges,除非是bug修复或功能上的可注意的变化。 以下是你应该注意的变化; “File>New>File”函数在从菜单项提取参数的代码中包含一个bug。这个错误已经被纠正 在这个版本。“File>New>Folder”函数在从菜单项提取参数的代码中包含一个错误。这个错误已经被纠正 在这个版本。增加字体大小的“View>Increase字体大小”的功能被改变,增加菜单instaead的大小为树状视图的大小。改变“缩小字体大小”的功能,将菜单的尺寸缩小到树状视图的尺寸。“Search”菜单已经启用,添加了“Find a File”、“Start The Search from a specific Directory”、“Find a String”和“Find a String”等功能 打印出使用它的上下文,并使用菜单或F3键“查找下一个文件”。 本文转载于:http://www.diyabc.com/frontweb/news367.html

原文地址:https://www.cnblogs.com/Dincat/p/13443865.html