Skip to content

Instantly share code, notes, and snippets.

@FuNkNaN
Created September 6, 2016 12:44
Show Gist options
  • Save FuNkNaN/09edf61d922ef9295f3c5c05614079b3 to your computer and use it in GitHub Desktop.
Save FuNkNaN/09edf61d922ef9295f3c5c05614079b3 to your computer and use it in GitHub Desktop.
F# Event Handler using XAML markup
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:funk="clr-namespace:funk;assembly=wpfApp"
xmlns:local="clr-namespace:ViewModels;assembly=wpfApp"
xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
Height="300" Width="400" Background="LightGray"
Title="Handling Routed Events">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Grid">
<Setter Property="funk:Tap.Handler" Value="{Binding TapHandler}"/>
</Style>
<Style TargetType="Button">
<Setter Property="funk:Click.Handler" Value="{Binding ClickHandler}"/>
<Setter Property="Margin" Value="10"/>
</Style>
</Window.Resources>
<Grid Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Clicking on the first or last Button"
TextWrapping="WrapWithOverflow" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="1" Text="switches the Tap Event handler"
TextWrapping="WrapWithOverflow" HorizontalAlignment="Center"/>
<Button Grid.Row="2" Content="I'm here for starters"/>
<funk:MyButtonSimple Grid.Row="3" Content="Raise Tap Event" Width="100"/>
<Button Grid.Row="4" Content="I'm here for closure"/>
</Grid>
</Window>
namespace ViewModels
open FsXaml
open System.Windows
open FSharp.ViewModule
type MainView = XAML<"MainWindow.xaml">
type MainViewModel() as self =
inherit ViewModelBase()
let onTapped1 =
let onTap sender (args : RoutedEventArgs) =
MessageBox.Show (sprintf "%A -> Bubble Up -> %A" args.OriginalSource sender) |> ignore
RoutedEventHandler(onTap)
let onTapped2 =
let onTap _ _ = MessageBox.Show "Not in the mood for bubbles" |> ignore
RoutedEventHandler(onTap)
let tapHandler = self.Factory.Backing(<@ self.TapHandler @>, onTapped1)
let toggle = ref false
let onClicked =
let onClick _ _ =
match !toggle with
| true -> tapHandler.Value <- onTapped1
| false -> tapHandler.Value <- onTapped2
toggle := not !toggle
MessageBox.Show "Handler switched" |> ignore
RoutedEventHandler(onClick)
member __.TapHandler
with get() = tapHandler.Value
and set(v) =
tapHandler.Value <- v
member __.ClickHandler = onClicked
namespace funk
open System.Windows
open System.Windows.Controls
// http://stackoverflow.com/questions/39276618/custom-routed-event-in-f
type MyButtonSimple() as self =
inherit Button()
static let tapEvent =
EventManager.RegisterRoutedEvent
( "Tap", RoutingStrategy.Bubble,
typeof<RoutedEventHandler>, typeof<MyButtonSimple>)
// Create a custom event so you can override AddHandler/RemoveHandler behavior
let tapEvent =
{ new IDelegateEvent<RoutedEventHandler> with
member this.AddHandler del = self.AddHandler(MyButtonSimple.TapEvent, del)
member this.RemoveHandler del = self.RemoveHandler(MyButtonSimple.TapEvent, del) }
// Raise via routed eventing strategy
let raiseTapEvent() =
let newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent)
self.RaiseEvent newEventArgs
// This isn't exactly the same, but public static fields aren't allowed in F#, and
// this works for WPF
static member TapEvent with get() = tapEvent
[<CLIEvent>]
member x.Tap = tapEvent
// For demonstration purposes we raise the event when clicked
override self.OnClick() =
raiseTapEvent()
//http://stackoverflow.com/a/38817055/4838058
type Click() =
inherit DependencyObject()
// For easy exchange
static let routedEvent = Button.ClickEvent
static let HandlerProperty =
DependencyProperty.RegisterAttached
( "Handler", typeof<RoutedEventHandler>,
typeof<Click>, new PropertyMetadata(null))
static let OnEvent (sender : obj) args =
let control = sender :?> UIElement
let handler = control.GetValue(HandlerProperty) :?> RoutedEventHandler
if not <| ((handler, null) ||> LanguagePrimitives.PhysicalEquality) then
handler.Invoke(sender, args)
static do EventManager.RegisterClassHandler(
typeof<FrameworkElement>, routedEvent,
RoutedEventHandler(OnEvent))
static member GetHandler (element: UIElement) : RoutedEventHandler =
element.GetValue(HandlerProperty) :?> _
static member SetHandler (element: UIElement, value : RoutedEventHandler) =
element.SetValue(HandlerProperty, value)
type Tap() =
inherit DependencyObject()
// For easy exchange
static let routedEvent = MyButtonSimple.TapEvent
static let HandlerProperty =
DependencyProperty.RegisterAttached
( "Handler", typeof<RoutedEventHandler>,
typeof<Tap>, new PropertyMetadata(null))
static let OnEvent (sender : obj) args =
let control = sender :?> UIElement
let handler = control.GetValue(HandlerProperty) :?> RoutedEventHandler
if not <| ((handler, null) ||> LanguagePrimitives.PhysicalEquality) then
handler.Invoke(sender, args)
static do EventManager.RegisterClassHandler(
typeof<FrameworkElement>, routedEvent,
RoutedEventHandler(OnEvent))
static member GetHandler (element: UIElement) : RoutedEventHandler =
element.GetValue(HandlerProperty) :?> _
static member SetHandler (element: UIElement, value : RoutedEventHandler) =
element.SetValue(HandlerProperty, value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment