Archive for 2009

Windows Forms: because you're worth it

March 23rd, 2009

Ah, Windows Forms. You've changed my life. I used to think Java was the lowest of low in gui programming, but I've had to reset my lowest common denominator in several areas already. Layout is horrendous, and the WinForms threading model is unfathomable. At least it's nothing important then.

But it also comes wrapped with easter eggs for your enjoyment. Try this on for size:

  1. Create a new thread, run splash screen.
  2. Initialize main gui in default thread.
  3. Signal splash thread to end.
  4. Splash screen calls Close().
  5. Call Join() on splash thread.
  6. Run main gui.
  7. Main gui appears behind all other open windows.

What do you mean that's not what you wanted? So you think to yourself "Aha! I'm cleverer than you are, stupid dot net. I'll make you wish you never gave me a BringToFront() method!" But that, despite the fantastically promising name, doesn't do anything. Neither does Activate().

No big deal. I'm sure noone is planning to use a splash screen in .NET anyway. So after aimlessly looking out the window for an hour and downing another three cups of awful coffee, you snap out of your prolonged daydream and start scrolling through the member list for the seventh time. Hm, a TopMost property. I wonder... Ah yes, that's the Stalin button. Makes your window always on top of all others. But what if..

// <stalin>
this.TopMost = true;
// </stalin>
this.Load += delegate (object o,EventArgs a) { this.TopMost = false; };

Yup, that actually works. When you're initializing the gui you fool the stack that hates you into thinking that you're going to be a dictator, but just when the window loads you turn off god mode. And that's enough to bring it to the front.

Windows Forms is truly fantastic for the kind of people who enjoy trivia. Just remembering hundreds of facts that have no real use just for the sake of knowing what few other people know. Take threading. Now, in most runtimes you would imagine that creating a new thread in your program has semantics such that whatever you've been doing until now in your single threaded application continues to work. The new bit you have to tackle is new threads you create in addition to the main one. Not so in .NET (I bet you saw that coming, you rascal!) For inexplicable reasons, your process has a way of hanging at the end just for the heck of it. I think I figured it out though. Are you ready for it?

splashthread = new Thread(RunSplash);
splashthread.IsBackground = true;
Thread.CurrentThread.IsBackground = true; // cure for cancer?

That's right, suddenly your main thread doesn't work the same anymore. You have to make it a background thread (which it clearly isn't), otherwise it just hangs there after Close().

EDIT: No, that didn't fix it either.

What a shame I didn't discover Windows Forms 7 years ago, I could have spent all that time learning all these exciting hacks instead of wasting my time on useless and unproductive things like Python.

data Binding made of fail?

March 9th, 2009

The following nabbed from csharp-online.net.

using System;
using System.Windows.Forms;

class Student {
	private string name;
	private double gpa;
	
	public string Name {
		get { return name; }
		set { name = value; }
	}
	
	public double Gpa {
		get { return gpa; }
		set { gpa = value; }
	}
}

class Widget : Form
{
	TextBox textBox1;
	TextBox textBox2;
	Button button;
	Student student;
	
	public static void Main(string[] args)
	{
		Application.Run(new Widget());
	}
	
	public Widget()
	{
		student = new Student();
		student.Name = "Jeff";
		student.Gpa = 4.0;

		textBox1 = new TextBox();
		textBox2 = new TextBox();
		textBox1.DataBindings.Add(new Binding("Text", student, "Name"));
		textBox2.DataBindings.Add(new Binding("Text", student, "Gpa"));
		
		button = new Button();
		button.Click += new EventHandler(testButton_Click);
		
		TableLayoutPanel pan = new TableLayoutPanel();
		pan.ColumnCount = 3;
		pan.ColumnCount = 1;
		pan.Controls.Add(textBox1, 0, 0);
		pan.Controls.Add(textBox2, 1, 0);
		pan.Controls.Add(button, 2, 0);
		this.Controls.Add(pan);
	}
	
	private void testButton_Click(object sender, EventArgs e) {
		MessageBox.Show("Name: " + student.Name 
				+ " GPA: " + student.Gpa);
   }
}

Build with:

gmcs binding.cs -r:System.Windows.Forms

Looks tidy, except.. it doesn't do a damn thing. Values are extracted successfully from student, but setting them doesn't do anything. No matter how vigorously I edit the textboxes I always see the same values.

That's on Mono. On .NET I get this evilgram:

See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
System.Reflection.TargetInvocationException: Property accessor 'Name' on object 'Student' threw the following exception:'Student.get_Name()' ---> System.MethodAccessException: Student.get_Name() ---> System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
   at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(Assembly asm, PermissionSet granted, PermissionSet refused, RuntimeMethodHandle rmh, SecurityAction action, Object demand, IPermission permThatFailed)
   at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(Object assemblyOrString, PermissionSet granted, PermissionSet refused, RuntimeMethodHandle rmh, SecurityAction action, Object demand, IPermission permThatFailed)
   at System.Security.CodeAccessSecurityEngine.CheckSetHelper(PermissionSet grants, PermissionSet refused, PermissionSet demands, RuntimeMethodHandle rmh, Object assemblyOrString, SecurityAction action, Boolean throwException)
   at System.Security.PermissionSetTriple.CheckSetDemand(PermissionSet demandSet, PermissionSet& alteredDemandset, RuntimeMethodHandle rmh)
   at System.Security.PermissionListSet.CheckSetDemand(PermissionSet pset, RuntimeMethodHandle rmh)
   at System.Security.PermissionListSet.DemandFlagsOrGrantSet(Int32 flags, PermissionSet grantSet)
   at System.Threading.CompressedStack.DemandFlagsOrGrantSet(Int32 flags, PermissionSet grantSet)
   at System.Security.CodeAccessSecurityEngine.ReflectionTargetDemandHelper(Int32 permission, PermissionSet targetGrant, CompressedStack securityContext)
   at System.Security.CodeAccessSecurityEngine.ReflectionTargetDemandHelper(Int32 permission, PermissionSet targetGrant)
The action that failed was:
Demand
The type of the first permission that failed was:
System.Security.Permissions.ReflectionPermission
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBase.PerformSecurityCheck(Object obj, RuntimeMethodHandle method, IntPtr parent, UInt32 invocationFlags)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
   --- End of inner exception stack trace ---
   at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
   at System.Windows.Forms.BindToObject.GetValue()
   at System.Windows.Forms.Binding.PushData(Boolean force)
   at System.Windows.Forms.Binding.UpdateIsBinding()
   at System.Windows.Forms.Binding.CheckBinding()
   at System.Windows.Forms.Binding.SetListManager(BindingManagerBase bindingManagerBase)
   at System.Windows.Forms.ListManagerBindingsCollection.AddCore(Binding dataBinding)
   at System.Windows.Forms.BindingsCollection.Add(Binding binding)
   at System.Windows.Forms.BindingContext.UpdateBinding(BindingContext newBindingContext, Binding binding)
   at System.Windows.Forms.Control.UpdateBindings()
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.set_BindingContextInternal(BindingContext value)
   at System.Windows.Forms.ContainerControl.set_BindingContext(BindingContext value)
   at System.Windows.Forms.ContainerControl.get_BindingContext()
   at System.Windows.Forms.Control.get_BindingContextInternal()
   at System.Windows.Forms.Control.get_BindingContext()
   at System.Windows.Forms.Control.get_BindingContextInternal()
   at System.Windows.Forms.Control.get_BindingContext()
   at System.Windows.Forms.Control.UpdateBindings()
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   at System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   at System.Windows.Forms.ContainerControl.OnCreateControl()
   at System.Windows.Forms.Form.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WmShowWindow(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)


************** Loaded Assemblies **************
mscorlib
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.1433 (REDBITS.050727-1400)
    CodeBase: file:///C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
----------------------------------------
Binding
    Assembly Version: 0.0.0.0
    Win32 Version:  
    CodeBase: file:///Z:/lumiere/Binding.exe
----------------------------------------
System.Windows.Forms
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.1433 (REDBITS.050727-1400)
    CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.1433 (REDBITS.050727-1400)
    CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.1433 (REDBITS.050727-1400)
    CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
Accessibility
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.1433 (REDBITS.050727-1400)
    CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/Accessibility/2.0.0.0__b03f5f7f11d50a3a/Accessibility.dll
----------------------------------------

************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.

For example:


    


When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.

worthless plurality

March 2nd, 2009

Behold, a valid sentence in Polish:

- Je.

In context:

- Co robi? (What's he doing?)
- Je. (He's eating.)*

In Polish verbs reflect the actor, so "je" is the singular, present form of eat. This makes saying "he's eating" (On je.) unnecessary because the he is already given in the form of the verb.

Behold, the same in French:

- Il mange. (He's eating.)
- Ils mangent. (They're eating.)

Same story? Not quite. You see, in Polish you pronounce every single letter. In French you don't. Il mange and Ils mangent sound exactly alike (singular and plural). Which means it's impossible to infer from that sentence alone what the hell is happening.

Let's step back and think about that for a while. Here you have all these verb forms that change depending on the pronoun. But they're pronounced the same anyway. So what the hell is the point? Ils mangent is no more insightful in speech than Ils mange would have been. It's only in writing that it makes a difference. And in writing there's obviously no need to have the special form because the pronoun is sitting right next to it!

Let's try the first sentence again:

- Mange. (Pronounced the same whether it's je, tu, il/elleils/elles.)

Now, despite the fact that you have all these different forms, the only thing you can rule out is that it isn't nous or vous. Very insightful, isn't it?

Incroyable!

* Can also be "she" or "it".

alphabet quirks

February 24th, 2009

Have you noticed that every language seems to deamonize a particular letter of the alphabet? Users of the language either refuse to pronounce it, or they pronounce it as a different sound, or they banish it altogether.

Polish

Polish has excommunicated the V. This is really strange, because all its linguistic neighbors use the V all the time. All words get rewritten with Ws instead.

Norwegian

Norwegian vowels are heavy, industrial strength. Somehow this has made the O into a Polish U or an English OO. To compensate for this lacking, the Å was invented as a makeshift O.

English

English has caught onto the fact that V and W are really the same sound, and have co-opted the W for a completely different sound. Polish has a ready made letter for this sound: the Ł.

English also disfavors the J, and uses the Y as a J when need be.

Needless to say, the R was mutated into a sound that defies definition. This is lost on many English natives who plainly assume that the crazy English R is the standard for all languages.

French

French refuses to pronounce the H, yet it keeps using it in written form.

French also uses the J as a Polish Ż.

The R, of course, is the most eccentric of them. It was made into a gargling sound that stings the throat.

Dutch

Dutch will pronounce the G only as an H. And the H.. er.. also as an H. It may be that the G and the H are slightly different in speech, but if so it still eludes me how.

Spanish

Spanish doesn't like the J. In its place it improvised the LL (but also the Y is used for this). The J is used as an H, in place of the real H, which refuses to be pronounced. Sounds pretty obsessive, doesn't it?

And the V becomes a B, depending on who you ask.

Italian

Like all its latin friends, Italian pretends the H doesn't exist, but still keeps writing it.

the cliché of "bad music"

February 20th, 2009

One thing that has been pretty much a constant in my life is the regular attempts I observe at cultural stigma by condemning someone to be a fan of "bad music". Of course, this type of cultural stigma is no different from other kinds of stigma based mechanisms that play a part in the daily social power struggles and hustles over group membership.

But it's interesting to me that it works despite how flawed it is. Obviously, the premise for the "bad music" stigma is that we all agree on some common standard for what makes good music. This may have been possible in the past, with three radio stations to choose from and little selection in a record store. But today, with the amount of choice we have, and especially among people who discover music online, it just isn't.

You just can't make a meaningful statement about bad music when just about every person you meet has something in their collection that's just awful. And half the people (or more) consider this something a cornerstone of their collection.

As far as I'm concerned, metal was invented to help us agree on the definition of bad music. But that's actually not the whole story. It turns out that if you take groups like Metallica and Linkin Park, and make them stfu, they do sometimes produce interesting instrumental music. It's just that all the yelling gets in the way.

There are subtler examples. Take Katie Melua. She has a nice voice, her melodies are pleasant, nothing wrong with her it would seem. But then you hear the lyrics. And I often don't even notice (or care) about the lyrics, but hers are so simple minded and annoying that I can't stand it. Same goes for Maria Mena.

And so forth.

But the practical impossibility of a consensus is not even the biggest problem with the "bad music" category. The more serious problem is that we still don't know why or how music affects the brain. In the future, perhaps, we will know why particular harmonies or rhythms induce a positive response. And composers over the ages have surely understood this intuitively, using precisely those "atoms" of composition that please us. But noone has been able to explain why those. So for the time being, musical taste can only be a purely subjective matter. And "bad music" continues to be a contradiction in terms.