Reader Level:

How to implement a Colorizer application in WPF using F#

By Dea Saddler on Aug 26, 2011
This article is a demonstration regarding how you can edit more color in a colorizer in WPF using FSharp. Take a quick review to learn

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 =
            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)
             |> (fun s -> let [|r1; g1; b1; r2; g2;b2|] = s.Split[|','|] |> byte
         with e ->
            [|  // greyscale colors
     // why the SolidColorBrush below?
    // to have a DependencyObject to data-bind... there is probably a cleaner way, but oh well
    let edgeColors = colors |> (fun (r,g,b,_,_,_) -> SolidColorBrush(Color.FromRgb(r,g,b)))
     let mainColors = colors |> (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)
         { 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))
     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
        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
    (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.


colorizer output1

colorizer output2

colorizer output3

colorizer output4

colorizer output5

colorizer output6


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