Reputation: 5384
in F#, I have the event defined as:
type ProgressNoteEvent() =
let event1 = new Event<string>()
let standardDotNetEventArgsEvent = new Event<EventHandler, EventArgs>()
[<CLIEvent>]
member this.Event1 = event1.Publish
[<CLIEvent>]
member this.StandardDotNetEventArgsEvent = standardDotNetEventArgsEvent.Publish
member this.TestEvent(arg) =
event1.Trigger(arg)
member this.TestStandardDotNetEventArgsEvent() =
standardDotNetEventArgsEvent.Trigger(this, EventArgs.Empty)
In the C# usercontrol code-behind, I have:
public ProgressNoteEvent progressNoteEvent;
progressNoteEvent = new ProgressNoteEvent();
progressNoteEvent.Event1 += ProgressNoteEvent_Event1;
progressNoteEvent.StandardDotNetEventArgsEvent += ProgressNoteEvent_StandardDotNetEventArgsEvent;
These events are raised by F# (in the DataContext for the WPF usercontrol):
let classWithEvent = new ProgressNoteEvent()
classWithEvent.Event1.Add(fun arg ->printfn "Event1 occurred! Object data: %s" arg)
classWithEvent.TestEvent("Hello World!")
classWithEvent.TestStandardDotNetEventArgsEvent()
I'm clearly missing something...The C# code in the code-behind never receives the raised event. What am I missing?
TIA
Upvotes: 0
Views: 73
Reputation: 5384
@TomasPetricek is 100% correct.
Here is what I ended up doing (for any newbie like me :)
The reason behind this is I needed a way for my F# backend code to modify the wpf usercontrol being displayed to the user--without depending on a simple property update and without wanting to constantly change a dependency property in the C# UserControl. So briefly, please make note of the following:
My solution is a simple implementation of the Mediator Pattern (in C#).
Here are the snippets needed in the XAML:
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<i:Interaction.Triggers>
<i:EventTrigger EventName="SubscribeToEventMediator">
<i:InvokeCommandAction PassEventArgsToCommand="True" Command="{Binding SubscribeToEventMediator}" CommandParameter="{Binding EventMediator, ElementName=MyProgressNoteWriter}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In the Code-Behind:
In the constructor of the usercontrol:
this.Loaded += ProgressNoteWriter_Loaded;
(Sending the EventMediator from the constructor did not work). Then,
private void ProgressNoteWriter_Loaded(object sender, RoutedEventArgs e)
{
EventMediator = new ProgressNoteEvent();
EventMediator.Event1 += ProgressNoteEvent_Event1;
EventMediator.StandardDotNetEventArgsEvent += ProgressNoteEvent_StandardDotNetEventArgsEvent;
((FrameworkElement)sender).RaiseEvent(new RoutedEventArgs(SubscribeToEventMediatorEvent));
}
private void ProgressNoteEvent_StandardDotNetEventArgsEvent(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void ProgressNoteEvent_Event1(object sender, string args)
{
throw new NotImplementedException();
}
public static readonly RoutedEvent SubscribeToEventMediatorEvent = EventManager.RegisterRoutedEvent(
name: "SubscribeToEventMediator",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(ProgressNoteWriter));
public event RoutedEventHandler SubscribeToEventMediator
{
add { AddHandler(SubscribeToEventMediatorEvent, value); }
remove { RemoveHandler(SubscribeToEventMediatorEvent, value); }
}
// the EventMediator will be passed to F#. MUST USE THE SAME INSTANCE FOR EVENT COMMUNICATION.
public ProgressNoteEvent EventMediator
{
get { return (ProgressNoteEvent)GetValue(EventMediatorProperty); }
set { SetValue(EventMediatorProperty, value); }
}
// Using a DependencyProperty as the backing store for EventMediator. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EventMediatorProperty =
DependencyProperty.Register("EventMediator", typeof(ProgressNoteEvent), typeof(ProgressNoteWriter), new PropertyMetadata(null));
Then in F# (via Elemish.WPF)
"SubscribeToEventMediator" |> Binding.cmdParam (fun p m ->SubscribeToEventMediator (p:?> ProgressNoteEvent ))
let update msg m =
match msg with
| SubscribeToEventMediator e -> {m with EventMediator = Some e}, Cmd.none
At this point, the same instance of the EventMediator is now in both the C# Usercontrol and F#. Raising the events as first written works correctly:
let SendEvent =
match m.EventMediator with
| None -> ()
| Some mediator -> mediator.Event1.Add(fun arg -> printfn "Event1 occurred! Object data: %s" arg)
mediator.TestEvent("Hello World!")
mediator.TestStandardDotNetEventArgsEvent()
SendEvent
I hope this helps somebody :)
Upvotes: 0
Reputation: 243051
From your code sample, it seems you are creating two separate instances of the ProgressNoteEvent
class:
progressNoteEvent = new ProgressNoteEvent()
)let classWithEvent = new ProgressNoteEvent()
)As those are two separate instances, triggering events in one would not cause events in the other. For this to work, the C# code needs to access the same instance as the F# code. Without knowing more about the rest of your project, it is hard to say how to best do this - but you need to create just one instance and pass it to the other project (probably create one in C# and pass it to the F# code). You could also make this global or the event static, but that is probably not going to make your code very elegant.
Upvotes: 3