Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# how to set content on WPF Label programmatically

Tags:

wpf

f#

F# Code

open System
open System.Windows
open System.Windows.Controls

let resourceLocator = new Uri("/ConsoleApplication3;component/MainWindow.xaml", UriKind.Relative)

let lbl2 = (Application.LoadComponent(resourceLocator) :?> Window).FindName("lbl") :?> Label

let tb = (Application.LoadComponent(resourceLocator) :?> Window).FindName("tb") :?> TextBlock

let tbo = (Application.LoadComponent(resourceLocator) :?> Window).FindName("tbo") :?> TextBox

let value = "ROCK!"
let clickButton = fun _ -> 
    lbl2.Content <- "ROCK!"

let loadWindow() = 
    let resourceLocator = new Uri("/ConsoleApplication3;component/MainWindow.xaml", UriKind.Relative)
    let window = Application.LoadComponent(resourceLocator) :?> Window
    (window.FindName("clickButton") :?> Button).Click.Add(clickButton)
    window

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

My Xaml WPF Code:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="500">
    <Grid>
        <Button Name="clickButton" Content="Click  me!" Height="40" Width="150" />
        <Label Name="lbl" Content="Label" HorizontalAlignment="Left" Margin="198,40,0,0" VerticalAlignment="Top"/>
        <TextBlock Name="tb" HorizontalAlignment="Left" Margin="198,201,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
        <TextBox Name="tbo" HorizontalAlignment="Left" Height="23" Margin="340,198,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
    </Grid>
</Window>

I intend to change the content of a Label:lbl with a button click, but for some reason it is not working, but can't see why

like image 216
Chrlol Avatar asked Jun 10 '26 15:06

Chrlol


1 Answers

The problem, in this case, is that you're loading the component more than once. When you call clickButton, it's using the result of a bound value that was bound to a loaded window instance separate from the main instance.

You should be able to work around this, in this case, by using Application.Current.MainWindow to get the window that's already been displayed:

open System
open System.Windows
open System.Windows.Controls

let resourceLocator = new Uri("/ConsoleApplication3;component/MainWindow.xaml", UriKind.Relative)

// This needs to be a function, so it's not evaluated before the window is created
let getLbl2 () = Application.Current.MainWindow.FindName("lbl") :?> Label

let value = "ROCK!"
let clickButton = fun _ -> 
    let lbl = getLbl2()
    lbl.Content <- "ROCK!"

let loadWindow() = 
    let resourceLocator = new Uri("/ConsoleApplication3;component/MainWindow.xaml", UriKind.Relative)
    let window = Application.LoadComponent(resourceLocator) :?> Window
    (window.FindName("clickButton") :?> Button).Click.Add(clickButton)
    window

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

That being said, I'd really recommend using FsXaml to work with your WPF resources. The type provider will provide you type safe access to your data, and make life far simpler.

This could be rewritten using FsXaml as:

open System
open System.Windows
open System.Windows.Controls
open FsXaml

type MainWindow = XAML<"MainWindow.xaml">

let loadWindow() = 
    let window = MainWindow().CreateRoot()
    let accessor = MainWindow.Accessor window
    accessor.clickButton.Click.Add(fun _ -> accessor.lbl.Content <- "ROCK!")
    window

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

Note that when using FsXaml, you get full intellisense of your members, and you don't need the magic strings anymore.

like image 181
Reed Copsey Avatar answered Jun 12 '26 10:06

Reed Copsey