开发者

Time flies like an arrow demo in WinForms

Looking at the Reactive Extensions for javascript demo on Jeff Van Gogh's blog, I thought I'd give it a try in C#/Winforms, but it doesn't seem to work so well.

I just threw this into the constructor of a form (with the Rx framework installed and referenced):

Observable.Context = SynchronizationContext.Current;
var mousemove = Observable.FromEvent<MouseEventArgs>(this, "MouseMove");
var message = "Time flies like an arrow".ToCharArray();

for(int i = 0; i < message.Length; i++)
{
    var l = new Label() 
            { 
                Text = message[i].ToString(), 
                AutoSize = true, 
                TextAlign = ContentAlignment.MiddleCenter 
            };
    int closure = i;
    mousemove
        .Delay(closur开发者_运维百科e * 150)
        .Subscribe(e => 
            {
                l.Left = e.EventArgs.X + closure * 15 + 10;
                l.Top = e.EventArgs.Y;
                //Debug.WriteLine(l.Text);
            });
    Controls.Add(l);
}

When I move the mouse, the letters seem to get moved in a random order, and if I uncomment the Debug line, I see multiple events for the same letter...

Any ideas? I've tried Throttle, but it doesn't seem to make any difference. Am I just asking too much of WinForms to move all those labels around?

(Cross posted on Rx Forum)


Answer cross posted back from Rx Forum (if the author is here and wants to claim it, that's fine by me):

Problem #1: You are using old bits. Observable.Context was gone about 4 versions ago. Problem #2: Javascript has no concept of threads, and Rx likes to put stuff on other threads for you.

So, using the latest bits, the solution looks something like this:

void Form1_Load(object sender, EventArgs e)
{
   Reactive("Time flies like an arrow");
}

private void Reactive(string msg)
{
    var mousemove = Observable.FromEvent<MouseEventArgs>(this, "MouseMove");
    var message = msg.ToCharArray();

    for(int i = 0; i < message.Length; i++)
    {
        var l = new Label()
        { 
            Text = message[i].ToString(), 
            AutoSize = true, 
            TextAlign = ContentAlignment.MiddleCenter 
        };
        int closure = i;
        mousemove
            .Delay(TimeSpan.FromSeconds(0.07 * i), Scheduler.Dispatcher)
            .Subscribe(e =>
            {
                l.Left = e.EventArgs.X + closure * 12 - 5;
                l.Top = e.EventArgs.Y + 15;
            });
        Controls.Add(l);
    }
}

Notice the ObserveOnDispatcher and SubscribeOnDispatcher. This brings us close to the Javascript version, but again threading is the real issue here.

UPDATE, added correction from Jeff Van Gogh in above code


Just for fun, here is a vague rendering of the same thing without Rx:

private void Unreactive(string msg)
{
    var message = msg.ToCharArray();

    for(int i = 0; i < message.Length; i++)
    {
        var l = new Label()
        { 
            Text = message[i].ToString(), 
            AutoSize = true, 
            TextAlign = ContentAlignment.MiddleCenter 
        };
        Controls.Add(l);
        int closure = i;
        this.MouseMove += (s, e) => LabelDelayMove(closure, e, l);
    }
}

private void LabelDelayMove(int i, MouseEventArgs e, Label l)
{
    Point p = new Point(e.X + i * 12 - 5, e.Y - 15);
    var timer = new System.Threading.Timer((_) => LabelMove(l, p), null, i * 70, System.Threading.Timeout.Infinite);
}

private void LabelMove(Label l, Point location)
{
    this.BeginInvoke(new Action(() => l.Location = location));
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜