One of the things that’s always bugged me when using Windows UI libraries like WinForms and WPF is the sheer number of members that pop up in intellisense for a control. The Button type in WPF has around 300 members and a total of 9 levels of inheritance making it hard to find the useful members. Or to put it another way the inherited members occlude the useful members:
The picture above shows what you actually see in the code completion box in the editor, however for a button you’re probably more interested in the Click event, but that’s several pages away.
Disinherit Type Provider
As a thought experiment I’ve implemented an F# Type Provider that hides type members after a specified level of inheritance:
In the picture above the members now almost fit in a single page. The last property, not visible here, is an extra property added by the type provider that provides all the hidden members for the instance:
Type Provider Implementation
Underneath the covers the type provider takes an assembly name as a static parameter and optionally the level of inheritance to expose.
[<Literal>]
let name = @"PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
type WPF = Disinherited< name , level=1 >
let button = WPF.Button()
The provider then reflects over the types in the assembly and creates a proxy type as a provided type and exposes members that are declared within the specified inheritance level.
To provide the member we simply create a corresponding provided member with an expression that evaluates to the underlying type’s member:
let addMember (def:ProvidedTypeDefinition) ty (mem:MemberInfo) =
match mem.MemberType with
| MemberTypes.Constructor ->
let ci = mem :?> ConstructorInfo
let c = ProvidedConstructor(toParams ci)
c.InvokeCode <- fun args -> Expr.Coerce(Expr.NewObject(ci,args), typeof<obj>)
def.AddMember(c)
| MemberTypes.Field ->
let fi = mem :?> FieldInfo
let field = ProvidedField(mem.Name, fi.FieldType)
def.AddMember(field)
| MemberTypes.Property ->
let pi = mem :?> PropertyInfo
let prop = ProvidedProperty(mem.Name, pi.PropertyType)
prop.GetterCode <- fun args -> Expr.PropertyGet(Expr.Coerce(args.[0],ty), pi)
def.AddMember(prop)
| MemberTypes.Event ->
let ei = mem :?> EventInfo
let ev = ProvidedEvent(mem.Name, ei.EventHandlerType)
ev.AdderCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty),ei.GetAddMethod(), args.Tail)
ev.RemoverCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), ei.GetRemoveMethod(), args.Tail)
def.AddMember(ev)
| MemberTypes.Method ->
let mi = mem :?> MethodInfo
if not mi.IsSpecialName then
let m = ProvidedMethod(mi.Name, toParams mi, mi.ReturnType)
m.InvokeCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), mi, args.Tail)
def.AddMember(m)
| _ -> ()
The entire provider is implemented in around 100 lines of code, I’d be interested to hear if anybody else finds it useful :)
Source code
The source is available on GitHub: https://github.com/ptrelford/Disinherit