I had a very nice surprise in my inbox, this morning. Thorsten Meinecke, from GTB in Berlin, decided to convert the VB.NET code contained in the last post into an F# script and to share it with this blog’s readership. Thanks, Thorsten! :-)
One thing about it being an F# script (typically stored in a .fsx file) is that it can be loaded and executed directly from the “F# Interactive” (FSI) component in Visual Studio without the need to build it into a project creating an executable. What’s also very nice is that the XAML defining the WPF dialog is embedded directly into the script, making it a simple matter to copy & paste the application into Visual Studio.
Here’s the F# code:
#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
#r "WindowsBase.dll"
#r "WindowsFormsIntegration.dll"
open System.IO
open System.Windows.Forms
open System.Windows
open System.Windows.Controls
open System.Windows.Markup
open System.Windows.Media
let xaml =
"<Window
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='Sort On Drawing Version' Height='327' Width='511'
WindowStartupLocation='CenterScreen'>
<Grid Height='Auto'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width='270*' />
<ColumnDefinition Width='270*' />
</Grid.ColumnDefinitions>
<Label Grid.ColumnSpan='2' Height='25' HorizontalAlignment='Left'
Name='Label1' VerticalAlignment='Top'
Width='77'>Root folder:</Label>
<TextBox Grid.ColumnSpan='2' Height='26' Margin='73,-1,166,0'
Name='FolderBox' VerticalAlignment='Top' />
<Button Height='29' Margin='5,0,0,33' Name='CopyButton'
VerticalAlignment='Bottom' IsEnabled='False'>Copy</Button>
<Button Grid.ColumnSpan='2' Height='28' Margin='0,0,85,0'
Name='BrowseButton' VerticalAlignment='Top' Width='75'
HorizontalAlignment='Right'>Browse...</Button>
<ListView Grid.ColumnSpan='2' Margin='5,30,6,68'
Name='FileList'/>
<Button Height='28' HorizontalAlignment='Right' Margin='0,0,6,0'
Name='ListButton' VerticalAlignment='Top' Width='73'
IsEnabled='False' Grid.Column='1'>List</Button>
<ProgressBar Grid.ColumnSpan='2' Height='28' Margin='5,0,6,2'
Name='SortProgress' VerticalAlignment='Bottom' />
<Button Grid.Column='1' Height='29' Margin='0,0,6,33'
Name='MoveButton' VerticalAlignment='Bottom'
IsEnabled='False'>Move</Button>
</Grid>
</Window>"
let (?) (this : Control) (prop: string) : 'T =
this.FindName prop :?> 'T
let (+=) e f = Observable.add f e
let window = XamlReader.Parse xaml :?> Window
let label1: Label = window?Label1
let folderBox: TextBox = window?FolderBox
let copyButton: Button = window?CopyButton
let browseButton: Button = window?BrowseButton
let fileList: ListView = window?FileList
let listButton: Button = window?ListButton
let sortProgress: ProgressBar = window?SortProgress
let moveButton: Button = window?MoveButton
let getVersion fn =
let (|StartsWith|_|) arg (s: string) =
if s.StartsWith arg then Some() else None
let data = Array.create 6 0uy
use fs = File.OpenRead fn
try
if fs.Read(data, 0, 6) = 6 then
match (new System.Text.ASCIIEncoding()).GetString data with
| StartsWith "MC0.0" -> "R1.0"
| StartsWith "AC1.2" -> "R1.2"
| StartsWith "AC1.40" -> "R1.4"
| StartsWith "AC1.50" -> "R2.05"
| StartsWith "AC2.10" -> "R2.10"
| StartsWith "AC2.21" -> "R2.21"
| StartsWith "AC2.22" -> "R2.22"
| StartsWith "AC1001" -> "R2.22"
| StartsWith "AC1002" -> "R2.5"
| StartsWith "AC1003" -> "R2.6"
| StartsWith "AC1004" -> "R9"
| StartsWith "AC1006" -> "R10"
| StartsWith "AC1009" -> "R11"
| StartsWith "AC1012" -> "R13"
| StartsWith "AC1014" -> "R14"
| StartsWith "AC1015" -> "2000"
| StartsWith "AC1018" -> "2004"
| StartsWith "AC1021" -> "2007"
| StartsWith "AC1024" -> "2010"
| _ -> "Unknown"
else ""
finally
fs.Close()
let messageBox (s: string) =
MessageBox.Show(s, "Sort On Drawing Version" ) |> ignore
let sortFiles move =
let numSorted = ref 0
let numSkipped = ref 0
try
if fileList.Items.Count = 0 then
"Nothing to sort!" |> messageBox
else
sortProgress.Minimum <- 0.
sortProgress.Maximum <- fileList.Items.Count - 1 |> float
sortProgress.Value <- 0.
for fn in Seq.cast fileList.Items do
let ver = getVersion fn
if not(System.String.IsNullOrEmpty ver) then
let loc = Path.Combine(folderBox.Text, ver)
if not(Directory.Exists loc) then
Directory.CreateDirectory loc |> ignore
let dest = Path.Combine(loc, Path.GetFileName fn)
if not(File.Exists dest) then
if move then
File.Move(fn, dest)
else
File.Copy(fn, dest)
incr numSorted
else
incr numSkipped
sortProgress.Value <- sortProgress.Value + 1.
System.String.Format(
"{0} file{1} {2}, {3} (already existing) file{4} skipped.",
!numSorted,
(if !numSorted = 1 then "" else "s"),
(if move then "moved" else "copied"),
!numSkipped,
(if !numSkipped = 1 then "" else "s" ) )
|> messageBox
sortProgress.Value <- 0.
fileList.ItemsSource <- null
with ex ->
"A problem was found while sorting files: " + ex.Message
|> messageBox
browseButton.Click +=
fun _ ->
let fbd =
new FolderBrowserDialog(
Description =
"Select the root folder for the DWG version sort:" )
if Directory.Exists folderBox.Text then
fbd.SelectedPath <- folderBox.Text
let dr = fbd.ShowDialog()
if dr = DialogResult.OK then
folderBox.Text <- fbd.SelectedPath
folderBox.TextChanged +=
fun e ->
let tb = e.Source :?> TextBox
listButton.IsEnabled <- Directory.Exists tb.Text
listButton.Click +=
fun _ ->
try
fileList.ItemsSource <-
Directory.GetFiles(
folderBox.Text, "*.dwg", SearchOption.AllDirectories )
with _ ->
"A problem was found accessing sub-folders in this " +
"location: will simply get the drawings in the root " +
"folder."
|> messageBox
fileList.ItemsSource <-
Directory.GetFiles(folderBox.Text, "*.dwg")
copyButton.IsEnabled <- true
moveButton.IsEnabled <- true
moveButton.Click += fun _ -> sortFiles true
copyButton.Click += fun _ -> sortFiles false
#if COMPILED
[<System.STAThread>]
[<EntryPoint>]
let main _ = (new Application()).Run window
#else
(new Application()).Run window
#endif
The simple way to run this code, for those unfamiliar with using F# in Visual Studio, is to paste it into a file (usually a .fsx file), select the contents and use Alt-Enter to load it into FSI. That should cause the dialog shown in the previous post to display, and to work in an identical manner.
Thanks again for sharing this code, Thorsten!