Form Designer adding delegates

I’ve got most of the form designer working ok now, so I’m going to start
working on the MSBuild, project & item integration. However, I’ve got a
couple of problems/questions.

  1. IronRuby doesn’t like the delegate op assign so I can’t click on a
    button

  2. I really need ‘attr_accessor’ to define a ‘field’ (it’s commented out
    below and I’ve worked round it, but it’s a pain to do). Does it exist
    yet?

  3. the references use the full qualified reference name. I can live with
    this no problem, but it really should use the short form. Is this on the
    road map?

Here’s the code for a simple form:

require ‘System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL’
require ‘System.Data, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089, processorArchitecture=x86’
require ‘System.Drawing, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL’
require ‘System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL’

class Form1123 < System::Windows::Forms::Form

#attr_accessor :button1

def button1=(v)
@button1 = v
end
def button1
@button1
end

def InitializeComponent

self.button1 = System::Windows::Forms::Button.new()
self.SuspendLayout()
#
# button1
#
self.button1.Location = System::Drawing::Point.new(220, 91)
self.button1.Name = "button1"
self.button1.Size = System::Drawing::Size.new(198, 42)
self.button1.TabIndex = 0
self.button1.Text = "button1"
self.button1.Click += System::EventHandler.new(self.button1_Click)
#
# Form1123
#
self.ClientSize = System::Drawing::Size.new(600, 400)
self.Controls.Add(self.button1)
self.Name = "Form1123"
self.ResumeLayout(false)
end

end

Apart from the ‘Click’ everything else works fine.

Thanks,

Dermot

Dermot H.:

I’ve got most of the form designer working ok now, so I’m going to
start
working on the MSBuild, project & item integration. However, I’ve got a
couple of problems/questions.

Excellent!

  1. IronRuby doesn’t like the delegate op assign so I can’t click on a
    button

We only support:

self.button1.click do |sender, args|

code

end

today.

  1. I really need ‘attr_accessor’ to define a ‘field’ (it’s commented
    out
    below and I’ve worked round it, but it’s a pain to do). Does it exist
    yet?

We’re working on this - it should be almost doable today.

  1. the references use the full qualified reference name. I can live
    with
    this no problem, but it really should use the short form. Is this on
    the
    road map?

What I’ve been doing is assembling these references into a .rb file that
I require - eg require ‘winforms’.

Thanks & great job!
-John

I battled many of these issues in developing the VS package (including
forms designer) for Ruby.NET.

You can check out a complete version from
http://rubydotnetcompiler.googlecode.com/svn/trunk/src/VisualStudioPackage/

There may be still be some bugs, but it basically works. Some of the
code in our InitializeComponent is a little un Ruby like
but it was done to achieve perfect round-trip-ability.

Perhaps we could collaborate more closely on the VS integration
challenges for Ruby?

I’m certainly happy to have one-on-one conversations with anyone working
on this part of the project.

Cheers, Wayne.

FYI, the code that we would generate in that scenario is as follows:

require ‘mscorlib.dll’
require ‘System.dll’
require ‘System.Drawing.dll’
require ‘System.Windows.Forms.dll’

class Form1 < System::Windows::Forms::Form

def initialize
self.InitializeComponent
end

attr_accessor :button1

def InitializeComponent()
@button1 = System::Windows::Forms::Button.new()
self.SuspendLayout()
#
# button1
#
@button1.set_Location(System::Drawing::Point.new(47, 54))
@button1.set_Name(‘button1’)
@button1.set_Size(System::Drawing::Size.new(75, 23))
@button1.set_TabIndex(0)
@button1.set_Text(‘button1’)
@button1.set_UseVisualStyleBackColor(true)
@button1.add_Click(System::EventHandler.new { |*args|
self.button1_Click(*args)})
#
# Form1
#
self.set_ClientSize(System::Drawing::Size.new(292, 266))
self.get_Controls().Add(@button1)
self.set_Name(‘Form1’)
self.ResumeLayout(false)
end

def button1_Click(sender,e)

end

end

  1. I think we could support += for events - seems to be quite easy to
    plug into our infrastructure.

This is a model how I think it could work:

class Form
def initialize
@click = Event.new
end

def click
puts “TRACE: Click”
@click
end

def click=(x)
puts “TRACE: Click=#{x.inspect}”
if x.instance_of? MethodHolder then
@click.add(x.method)
else
@click.set(x)
end
end
end

class MethodHolder
attr :method

def initialize(m)
@method = m
end
end

class Event
def initialize
@methods = []
end

def +(m)
puts “TRACE: +(#{m.inspect}”
MethodHolder.new(m)
end

def call
@methods.each { |m| m.call }
end

def add(m)
@methods << m
end

def set(m)
@methods = [m]
end
end

def bar
puts ‘hello’
end

def baz
puts ‘world’
end

form = Form.new
form.click += method(:bar)
form.click += method(:baz)
form.click.call

So if you guys don’t have objections, I would add it to my TODO list.

Tomas

  1. IronRuby doesn’t like the delegate op assign so I can’t click on a
    button

We only support:

self.button1.click do |sender, args|

code

end

today.

OK. The Form Designer requires that you add a delegate (at least I think
it does. I haven’t found a simple way supporting the above syntax).
Everything else C#, VB, IronPython, etc use the += syntax.

  1. I really need ‘attr_accessor’ to define a ‘field’ (it’s commented
    out
    below and I’ve worked round it, but it’s a pain to do). Does it exist
    yet?

We’re working on this - it should be almost doable today.

OK. That’s good.

  1. the references use the full qualified reference name. I can live
    with
    this no problem, but it really should use the short form. Is this on
    the
    road map?

What I’ve been doing is assembling these references into a .rb file that
I require - eg require ‘winforms’.

I can actually get the fully qualified name from the VS Add References
system, so it’s not a problem. It’s not urgent by any means - it just
looks odd.

Another minor problem is that I have to use syntax like this:

System::Windows::Forms::Application.EnableVisualStyles()
System::Windows::Forms::Application.SetCompatibleTextRenderingDefault(false);

even with the require ‘System::Windows::Forms’

Any way of shortening this?

Also (a more general question) - will IR compile to a PE? I’m assuming
that it runs similarly to MRuby right now (i.e as an interpreter), but I
need to build certain assumptions into the things MSBuild knows about.
If it’s going to compile, then I’ll make sure MSBuild knows about it.

Don’t bend your development plans to accomodate me, btw. I can see that
there’s plenty to work on before getting the finer points of the Form
Designer.

Still, I can see impressive progress!

Dermot

Actually, Ruby already has an in-place addition operator for arrays: <<.

So another way of hooking could be:

form.click << method(:bar)

We can alias/rename :add to :push and :remove to :delete for better
consistency with Ruby Array class method names.

Of course, we can provide all three ways and let the user choose
whatever she likes.

Tomas

Tomas M.:

Of course, we can provide all three ways and let the user choose
whatever she likes.

For consistency, we should also let the user remove delegates as well
via -=. But other than that, it looks good and feels rather Rubyish.

-John

Wayne K.:

require ‘mscorlib.dll’
require ‘System.dll’
require ‘System.Drawing.dll’
require ‘System.Windows.Forms.dll’

I’m a bit surprised at the use of assembly filenames vs. assembly names
here - what’s the resolution order for these assemblies? Do they need to
be on the path?

@button1.set_Location(System::Drawing::Point.new(47, 54))

Is there a reason why you’re calling the property setter method
explicitly rather than using the property name? Or is this not supported
in the Ruby.Net method binder?

Thanks,
-John

require ‘mscorlib.dll’
require ‘System.dll’
require ‘System.Drawing.dll’
require ‘System.Windows.Forms.dll’

I’m a bit surprised at the use of assembly filenames vs.
assembly names here

Don’t all ruby requires expect a file name? We haven’t changed the
lookup mechanism for loading - we just allowed .NET dlls to also be
loaded.

Do they need to be on the path?

They need to be on Ruby’s path, but we automatically add the
Microsoft.NET framework directory to that Path.

@button1.set_Location(System::Drawing::Point.new(47, 54))

Is there a reason why you’re calling the property setter
method explicitly rather than using the property name? Or is
this not supported in the Ruby.Net method binder?

Our compiler would be happy with it as a property assignment, but we did
it that way inside InitializeComponent in order to ensure round
tripping. When parsing InitializeComponent and generating the codeDOM
representation we need to distinguish between a field, a property and a
method call. Statically typed languages are expected to use type
information to distinguish. As we have no type information we use
syntactic conventions to distinguish between them. Note - these
syntactic conventions only apply within the InitializeComponent method
which is typically auto-generated. Ruby programmers are free to use the
more Rubyish syntax elsewhere in their code.

Another issue is statically distinguishing between variables such as Foo
and type references such as System::Drawing::Point - we use
wrapper/helper methods to distinguish type references (except when
invoking a new method where we implicitly assume that the constant is a
type reference). Again, this is only necessary inside
IntializeComponent.

And now a question for your VS integration experts: when adding a new
event handler - how do you arrange for the cursor to be automatically
placed inside the body of the new method?

Cheers, Wayne.

Tomas M. wrote:

Actually, Ruby already has an in-place addition operator for arrays: <<.

So another way of hooking could be:

form.click << method(:bar)

We can alias/rename :add to :push and :remove to :delete for better
consistency with Ruby Array class method names.

Of course, we can provide all three ways and let the user choose
whatever she likes.

Tomas

This looks reasonably promising in the short term. I’ll try implementing
this in Ruby. All I need is some syntax that won’t be rejected by
IronRuby and that hooks up a delegate type thing to the object.

The FormDesigner doesn’t care if it’s ‘+=’ or ‘<<’

Dermot

John L. (DLR) wrote:

For consistency, we should also let the user remove delegates as well
via -=. But other than that, it looks good and feels rather Rubyish.

Thta’s actually quite important (though not for the FormDesigner). I do
add and remove delegates, fo example, when loading assemblies. Not
often, I admit, but I do use them.

Dermot

Wayne K. wrote:

@button1.set_Location(System::Drawing::Point.new(47, 54))

Is there a reason why you’re calling the property setter
method explicitly rather than using the property name? Or is
this not supported in the Ruby.Net method binder?

Our compiler would be happy with it as a property assignment, but we did
it that way inside InitializeComponent in order to ensure round
tripping. When parsing InitializeComponent and generating the codeDOM
representation we need to distinguish between a field, a property and a
method call. Statically typed languages are expected to use type
information to distinguish. As we have no type information we use
syntactic conventions to distinguish between them. Note - these
syntactic conventions only apply within the InitializeComponent method
which is typically auto-generated. Ruby programmers are free to use the
more Rubyish syntax elsewhere in their code.

You don’t need to do this, I think. You can work out from the context
what is a field, a property and a call. I did have some initial trouble
distinguishing between a Property (System.Windows.Form.BackColor.Red)
and an enum (System.Windows.Form.BorderStyle.Sizable) - but it worked
out ok.

Another issue is statically distinguishing between variables such as Foo
and type references such as System::Drawing::Point - we use
wrapper/helper methods to distinguish type references (except when
invoking a new method where we implicitly assume that the constant is a
type reference). Again, this is only necessary inside
IntializeComponent.

I didn’t have that problem. But what I did have a problem with was
something like this (from a TreeNode control):

treeNode3 = System::Windows::Forms::TreeNode.new(‘Node0’, [treeNode1,
treeNode2])

Here, the FormDesigner needs to know the type of the array - which Ruby
of course doesn’t give you. That took a bit of time to get right.

And now a question for your VS integration experts: when adding a new
event handler - how do you arrange for the cursor to be automatically
placed inside the body of the new method?

I’m sure there are several ways to do this, but this is what I did:

  1. I have two files - the form code file and the designer file (as in
    C#). In the CodeDomProvider, I override the Parse method and before I
    parse the designer file to generate the CodeDom, I scan the form file
    looking for event handlers. I use regexps to do this - I don’t bother
    Ruby parsing the form file at all. I then build a list containing the
    names and positions of any event handlers.

  2. I then Ruby parse (using Antlr) the designer file and generate the
    CodeDom from that. When a delegate is required I do something like this:

CodeMemberMethod cmm = new CodeMemberMethod();
cmm.Parameters.Add(new
CodeParameterDeclarationExpression(typeof(object), “sender”));
cmm.Parameters.Add(new
CodeParameterDeclarationExpression(typeof(System.EventArgs), “e”));
cmm.Name = eventHandlerName;

// let the provider process adding the event handler
int line = _provider.AddEventHandler(eventHandlerName);
cmm.UserData.Add(typeof(Point), new Point(0, line + 1));

The AddEventHandler method does the business of finding any event
handler in the form code, generating it if required (again in the form
code - not the designer code) and returning its position. The last bit
is the code that lets the FormDesigner know where the event handler is
when you double click on the control in the form.

Something similar happems when you drop a new control on the form.

Splitting the form code from the designer makes life a whole lot simpler
and the coding is then clear and unambiguous. And most importantly from
my point of view, it’s easily maintainable.

Dermot