Interop customization
It would be a really nice interop feature to allow the customization of interop through something like a [CustomInteropAttribute].
Problem Description
Dojo
For a Dojo Script# wrapper, I would want to be able to transform the C# code
widget.Disabled = true;
bool d = widget.Disabled;
to
widget.set('disabled', true)
var d = widget.get('disabled');
KnockoutJS
To use Script# with Knockout view models, a nice way to handle observables would be to transform
model.PropertyName = "PropertyValue";
string pn = model.PropertyName;
to
model.propertyName('PropertyValue');
var pn = model.propertyName();
JQuery
(This is not as important as the other ones since there is already a perfecly valid option, and one could argue that it is not even a good idea. However, I'm doing code generation and currently I have to handle JQuery events differently from all other events because of the different syntax, so it would definitely help me). JQuery event subscription could be made more .net-like by allowing the transform of
elem.Click += myClickHandler;
elem.Click -= myClickHandler;
to
elem.click(myClickHandler);
elem.unbind('click', myClickHandler);
Proposed feature
The [CustomInteropAttribute] could be used like this:
[Imported, Record]
public class MyKoJSViewModel {
public string MyProperty {
[CustomInterop("{this}.myProperty()")]
get { return null; }
[CustomInterop("{this}.myProperty({value})", Chainable = false)]
set {}
}
[CustomInterop("{this}.myProperty.subscribe({value})")]
public IDisposable OnMyPropertyChanged(Action<string> handler) {}
}
[Imported, Record]
public class MyDojoWidget {
public string MyProperty {
[CustomInterop("{this}.get('myProperty')")]
get { return null; }
[CustomInterop("{this}.set('myProperty', {value})", Chainable = false)]
set {}
}
}
The Chainable property would be required because the setter does not return a value, so
string x = object.MyProperty = "x"
would not work out of the box, and preferrably the compiler should either rewrite this, or issue an error.
Even better would be to be able to write a plugin for the code generation process so I could write a plugin that would enable me to say:
[Imported, Record]
public class MyKoJSViewModel {
[KoJSObservable]
public string MyProperty { get { return null; } set {} }
[KoJSSubscription("MyProperty")]
public IDisposable OnMyPropertyChanged(Action<string> handler) {}
}
[Imported]
public class MyDojoWidget {
[DojoProperty]
public string MyProperty { get { return null; } set {} }
}
or even
[Imported, Record, KoJSViewModel]
public class MyKoJSViewModel {
public string MyProperty { get { return null; } set {} }
public IDisposable OnMyPropertyChanged(Action<string> handler) {}
}
The latter option (KoJSObservable / DojoProperty) would be very useful. The situation in which you would need to annotate every getter and setter with an interop attribute specifying the exact interop required (which can be fragile because you'd need to keep everything in sync) would be less desirable.
@Roy: I agree, but if you are writing an import library, you might want to customize the usage of some really quirky property in that way. However, if writing a view model for KnockoutJS, I could not agree more.
@erik-kallen: Certainly I agree with supporting both and I don't even have a preference for this specific syntax. But I definitely would love to be able to just mark a viewmodel as a KoJSViewModel and have Script# interpret properties and events in a certain way. Probably INotifyPropertyChanged needs to be shoehorned in as well :)
This sounds like a great idea! I have been struggling with writing mappings that will support Knockout, and have had to settle on GetValue and SetValue utility functions because I can't use the properties directly. Being able to finely tune the script output would be a godsend
I've partially written an Import Library for Knockout, and a code snippet to help with property definition. Here's a sample view model, that lets me program against it nicely:
public class MyViewModel
{
protected Function firstName = Ko.Observable( "Paul" );
public string FirstName { get { return (string)firstName.Call( this ); } set { firstName.Call( this, value ); } }
protected Function lastName = Ko.Observable( "Hester" );
public string LastName { get { return (string)lastName.Call( this ); } set { lastName.Call( this, value ); } }
protected Function fullName;
public string FullName { get { return (string)fullName.Call( this ); } set { fullName.Call( this, value ); } }
protected void z_FullNameDependentObservable()
{
fullName = Ko.DependentObservable(
delegate()
{
return this.FirstName + " " + this.LastName;
},
this,
new Dictionary( "write", new Action<string>( delegate( string value )
{
this.FirstName = value;
this.LastName = "";
} ) )
);
}
public MyViewModel()
{
z_FullNameDependentObservable();
}
}
I'm also about 1/8th through writing an import library for Dojo, if anyone is interested.