17

How would one implement INotifyPropertyChanged for use in an F# type? Thanks!

rysama
  • 1,674
  • 16
  • 28

2 Answers2

22

It's basically the same as in any other language:

open System.ComponentModel
type MyType() =
  let ev = new Event<_,_>()
  let mutable str = ""
  member x.StringProp
    with get() = str
     and set(str') =
       str <- str'
       ev.Trigger(x, PropertyChangedEventArgs("StringProp"))
  interface INotifyPropertyChanged with
    [<CLIEvent>]
    member x.PropertyChanged = ev.Publish
kvb
  • 54,864
  • 2
  • 91
  • 133
  • 16
    That is not "basically the same" as other languages :P – Cameron MacFarland Nov 08 '09 at 23:10
  • 1
    Thanks, What exactly is the [] attribute do? I can't seem to find any the documentation on it. – rysama Nov 08 '09 at 23:17
  • 5
    @RodYan - it affects the compiled form that the event takes; for interop with other .NET languages (and to implement interfaces exposing .NET events), you'll need to apply it to an IEvent value. This causes `add_` and `remove_` methods to be generated, as opposed to actually exposing a property of type `IEvent<_,_>`, as described at http://msdn.microsoft.com/en-us/library/ee370437(VS.100).aspx. – kvb Nov 08 '09 at 23:28
  • 3
    This is a very good example of implementing the interface, but does not show how to wire it up via data binding to a WPF control property, for instance. – Scott Hutchinson Dec 22 '16 at 21:24
  • 1
    @ScottHutchinson WPF automatically hooks to the event as part of the binding. – Jesse Feb 11 '17 at 05:06
3

https://github.com/Fody/PropertyChanged https://github.com/Fody/PropertyChanged/issues/280

In practice

You can use fody proeprty changed to automatically implement it

public abstract class ViewModelBase :  INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }

Then you can use it like this

type ViewModelTest() =
    inherit ViewModelBase()

    /// two way binding
    member val Hello = 6 with get, set

    /// one way binding
    member x.Yes = x.Hello + 1

The generated Decompiled codes are

using System;
using Microsoft.FSharp.Core;

namespace Shrimp.UI.Forms.FSharp
{
    // Token: 0x02000002 RID: 2
    [CompilationMapping(3)]
    [Serializable]
    public class ViewModelTest : ViewModelBase
    {
        // Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
        public ViewModelTest() : this()
        {
            this.Hello@ = 6;
        }

        // Token: 0x17000001 RID: 1
        // (get) Token: 0x06000002 RID: 2 RVA: 0x00002061 File Offset: 0x00000261
        // (set) Token: 0x06000003 RID: 3 RVA: 0x0000206C File Offset: 0x0000026C
        public int Hello
        {
            get
            {
                return this.Hello@;
            }
            set
            {
                if (this.Hello@ == value)
                {
                    return;
                }
                this.Hello@ = value;
                this.<>OnPropertyChanged(<>PropertyChangedEventArgs.Yes);
                this.<>OnPropertyChanged(<>PropertyChangedEventArgs.Hello);
            }
        }

        // Token: 0x17000002 RID: 2
        // (get) Token: 0x06000004 RID: 4 RVA: 0x000020A4 File Offset: 0x000002A4
        public int Yes
        {
            get
            {
                return this.Hello + 1;
            }
        }

        // Token: 0x04000001 RID: 1
        internal int Hello@;
    }
}
humhei
  • 31
  • 1