Время летит, как стрелка демо в WinForms - PullRequest
3 голосов
/ 18 марта 2010

Глядя на демонстрацию Reactive Extensions для javascript на блог Джеффа Ван Гога , я решил попробовать его в C # / Winforms, но, похоже, он не работает так хорошо. *

Я просто бросил это в конструктор формы (с установленной платформой Rx и ссылкой на нее):

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(closure * 150)
        .Subscribe(e => 
            {
                l.Left = e.EventArgs.X + closure * 15 + 10;
                l.Top = e.EventArgs.Y;
                //Debug.WriteLine(l.Text);
            });
    Controls.Add(l);
}

Когда я перемещаю мышь, кажется, что буквы перемещаются в случайном порядке, и если я раскомментирую строку отладки, я вижу несколько событий для одной и той же буквы ...

Есть идеи? Я попробовал газ, но это, похоже, не имеет никакого значения. Я просто прошу слишком много WinForms, чтобы переместить все эти ярлыки?

(Крест опубликован на Rx Forum )

1 Ответ

2 голосов
/ 18 марта 2010

Ответ кросс-пост от Rx Forum (если автор здесь и хочет это сделать, это нормально для меня):

Проблема № 1: Вы используете старые биты. Observable.Context пропал около 4-х версий назад. Проблема № 2: Javascript не имеет понятия о потоках, а Rx любит помещать вещи в другие потоки для вас.

Итак, используя последние биты, решение выглядит примерно так:

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);
    }
}

Обратите внимание на ObserveOnDispatcher и SubscribeOnDispatcher. Это приближает нас к версии Javascript, но, опять же, реальная проблема - это реальная проблема.

ОБНОВЛЕНИЕ , добавлено исправление от Джеффа Ван Гога в приведенном выше коде


Просто для забавы, вот расплывчатый рендеринг того же самого без 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));
}
...