How to implement a Colorizer application in WPF using F#

Today I am discussing about the WPF colorizer using F#. The application will show that how you can change color of rectangles by editing edge colors and main colors in combination of Red, Green, Blue (r,g,b). You can add more colors to the F# colorizer by this application.Steps for edit colors in colorizer are given below.

Step 1: Firstly open a new project in F# using Visual Studio 2011. Select F# WPF application template and give name to the project like below Image.

New Project Dialog Box

Step 2: Now add below define References to the Project by right clicking on the Project in Solution Explorer.

  • PresentationCore

  • PresentationFramework

  • System

  • System.Xml

  • UIAutomationTypes

  • WindowsBase

Step 3: When you add all these references your solution explorer will look like below image.

Solution Explorer

Step 4: Now click on Programe.fs file and write below Code in that like below image.

 colorizer code part1

 colorizer code part1.1

 colorizer code part1.1.1

 colorizer code part1.1.1.1

 open System.Windows
 
open System.Windows.Controls
 
open System.Windows.Data
 
open System.Windows.Media
 
open System.Windows.Shapes
 
open Microsoft.Win32
  
 
type IIndexable<'a> =
     abstract member Item : int -> 'a
  
 
let makeBinding(sourceObj:obj, sourcePath:string, converter:'T->'U,
                 targetObj:DependencyObject, targetDependencyProperty:DependencyProperty) =
     let b = new System.Windows.Data.Binding()
     b.Source <- sourceObj
     b.Path <- new System.Windows.PropertyPath(sourcePath)
     b.Converter <-
         { new System.Windows.Data.IValueConverter with
 
            member this.Convert(v, targetType, param, culInfo) =
                 box(converter (unbox v))
             member this.ConvertBack(v, targetType, param, culInfo) =
                 raise <| new System.NotImplementedException() }
     BindingOperations.SetBinding(targetObj, targetDependencyProperty, b) |> ignore
  
 
type MyWindow() as this =
     inherit Window()
  
     static let selectedRectangleNumber =
         DependencyProperty.Register("SelectedRectangleNumber", typeof<int>, typeof<MyWindow>,
             new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender,
                 new PropertyChangedCallback(fun depObj ea ->
                     match box depObj, box ea.NewValue with
                     | (:? MyWindow as w), (:? int as x) -> w.TriggerSelectedRectangleNumberChanged(x)
                     | _ -> ())))
  
     let selectedChanged = new Event<Handler<int>,int>()
     let getNum() = this.GetValue(MyWindow.SelectedRectangleNumber) :?> int
     let setNum(n) = this.SetValue(MyWindow.SelectedRectangleNumber, n)
  
     let colors =
         try
 
            let key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\VisualStudio\10.0\Text Editor\FSharpDepthColorizer")
             Array.init 10 (fun i -> key.GetValue(sprintf "Depth%d" i) :?> string)
             |> Array.map (fun s -> let [|r1; g1; b1; r2; g2;b2|] = s.Split[|','|] |> Array.map byte
                                    r1,g1,b1,r2,g2,b2)
         with e ->
 
            [|  // greyscale colors
 
            190uy,190uy,190uy,230uy,230uy,230uy
             170uy,170uy,170uy,210uy,210uy,210uy
             184uy,184uy,184uy,224uy,224uy,224uy
             164uy,164uy,164uy,204uy,204uy,204uy
             178uy,178uy,178uy,218uy,218uy,218uy
             158uy,158uy,158uy,198uy,198uy,198uy
             172uy,172uy,172uy,212uy,212uy,212uy
             152uy,152uy,152uy,192uy,192uy,192uy
             166uy,166uy,166uy,206uy,206uy,206uy
             146uy,146uy,146uy,186uy,186uy,186uy
             |]
     // why the SolidColorBrush below?
 
    // to have a DependencyObject to data-bind... there is probably a cleaner way, but oh well
 
    let edgeColors = colors |> Array.map (fun (r,g,b,_,_,_) -> SolidColorBrush(Color.FromRgb(r,g,b)))
     let mainColors = colors |> Array.map (fun (_,_,_,r,g,b) -> SolidColorBrush(Color.FromRgb(r,g,b)))
     let brushes =
         let makeGradientStop(b:SolidColorBrush, pct) =
             let gs = new GradientStop(b.Color, pct)
             makeBinding(b, "Color", (fun (c:Color) -> c), gs, GradientStop.ColorProperty)
             gs
         { new IIndexable<Brush> with
             member this.Item depth =
                 upcast new LinearGradientBrush(
                     new GradientStopCollection(
                         [|
                         makeGradientStop(edgeColors.[depth], 0.0)
                         makeGradientStop(mainColors.[depth], 0.01)
                         makeGradientStop(mainColors.[depth], 1.0)
                         |] ), new Point(0.0, 0.5), new Point(1.0, 0.5)) }
  
     let printCurrentSettings() =
         printfn "Windows Registry Editor Version 5.00"
 
        printfn ""
 
        printfn "[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor\FSharpDepthColorizer]"
 
        for i in 0..9 do
 
            let ec = edgeColors.[i].Color
             let mc = mainColors.[i].Color
             printfn "\"Depth%d\"=\"%d,%d,%d,%d,%d,%d\"" i ec.R ec.G ec.B mc.R mc.G mc.B
  
     let mkButton(colors:SolidColorBrush[], text, dr, dg, db) =
         let b = new Button()
         b.Content <- text
         b.Click.Add(fun _ -> colors.[getNum()].Color <-
                                 Color.FromRgb(colors.[getNum()].Color.R + byte dr,
                                               colors.[getNum()].Color.G + byte dg,
                                               colors.[getNum()].Color.B + byte db))
         b
  
     let mkButtonPanel(colors:SolidColorBrush[],text,dr,dg,db,f) =
         let panel = new StackPanel(Orientation = Orientation.Horizontal)
         let tb = new TextBlock()
         selectedChanged.Publish.Add(fun _ ->
 
            makeBinding(colors.[getNum()], "Color", (fun (c:Color) -> f(c).ToString()),
                         tb, System.Windows.Controls.TextBlock.TextProperty))
         panel.Children.Add(mkButton(colors, text+" down",-dr,-dg,-db)) |> ignore
         panel.Children.Add(tb) |> ignore
         panel.Children.Add(mkButton(colors, text+" up",dr,dg,db)) |> ignore
         panel
  
     do
 
        let leftPanel = new StackPanel(Orientation = Orientation.Vertical)
         leftPanel.Children.Add(mkButtonPanel(edgeColors,"edge R",2,0,0,(fun c->c.R))) |> ignore
         leftPanel.Children.Add(mkButtonPanel(edgeColors,"edge G",0,2,0,(fun c->c.G))) |> ignore
         leftPanel.Children.Add(mkButtonPanel(edgeColors,"edge B",0,0,2,(fun c->c.B))) |> ignore
         leftPanel.Children.Add(mkButtonPanel(mainColors,"main R",2,0,0,(fun c->c.R))) |> ignore
         leftPanel.Children.Add(mkButtonPanel(mainColors,"main G",0,2,0,(fun c->c.G))) |> ignore
         leftPanel.Children.Add(mkButtonPanel(mainColors,"main B",0,0,2,(fun c->c.B))) |> ignore
         //let printButton = new Button(Content="Print current settings to console")
 
        //printButton.Click.Add(fun _ -> printCurrentSettings())
 
        //leftPanel.Children.Add(printButton) |> ignore
 
        let mainPanel = new StackPanel(Orientation = Orientation.Horizontal)
         mainPanel.Children.Add(leftPanel) |> ignore
         let canvas = new Canvas()
         for i in 0..mainColors.Length-1 do
 
            let rect = new Rectangle(Width=600., Height=500., Fill = brushes.[i])
             rect.MouseDown.Add(fun _ -> setNum(i))
             Canvas.SetLeft(rect, (float i)*30.)
             Canvas.SetTop(rect, (float i)*30.)
             Canvas.SetZIndex(rect, i)
             canvas.Children.Add(rect) |> ignore
         let rect = new Rectangle(Width=600., Height=40.)
         selectedChanged.Publish.Add(fun _ -> rect.Fill <- brushes.[getNum()])
         Canvas.SetLeft(rect, 0.)
         Canvas.SetTop(rect, (float mainColors.Length)*30. + 20.)
         Canvas.SetZIndex(rect, mainColors.Length)
         canvas.Children.Add(rect) |> ignore
         mainPanel.Children.Add(canvas) |> ignore
         this.Content <- mainPanel
  
 
     member private this.TriggerSelectedRectangleNumberChanged(x:int) = selectedChanged.Trigger(this, x)
 

 
    static member SelectedRectangleNumber = selectedRectangleNumber
  
 [<System.STAThread>]
 
do
 
    (new Application()).Run(MyWindow()) |> ignore 

Step 5: Now press F5 to execute the code and your application is ready.

Firstly click on any of the rectangle than select edge color or main color your color will be edited.

Output

colorizer output1

colorizer output2

colorizer output3

colorizer output4

colorizer output5

colorizer output6

Summary

In this article I have discussed that how can you create a colorizer in WPF using F#.


Similar Articles