Dispatcher.beginInvoke выполняется только в последний раз через цикл? - PullRequest
0 голосов
/ 19 января 2011

У меня есть фрагмент кода в коде Silverlight, который выглядит примерно так:

foreach (MapLocation loc in e.Result)
        {

            testDict[loc.ElemId] = loc.ToString();

            this.Dispatcher.BeginInvoke(delegate()
            {
                Image icon = new Image();
                icon.SetValue(Image.SourceProperty, nurseIconSource);
                Canvas.SetLeft(icon, (double)loc.X * MAP_SCALE);
                Canvas.SetTop(icon, MAP_HEIGHT - (double)loc.Y * MAP_SCALE);
                icons[loc.ElemId] = icon;
                MainCanvas.Children.Add(icon);
            });
        }
    }

Этот цикл выполняется 25 раз в потоке, отдельном от потока пользовательского интерфейса. Объект testDict заканчивается всеми 25 записями после выполнения метода, в то время как словарь значков хранит запись только для 25-го (последнего) элемента.

Я впервые использую Dispatcher. Разве это не должно называться быстрым огнем, как это? Все, о чем я могу думать, это то, что первый раз, когда делегат вызывается, происходит после последнего выполнения цикла, поэтому объект loc всегда один и тот же элемент. Это точно?

Ответы [ 2 ]

5 голосов
/ 19 января 2011

Это старая проблема «не перехватывать переменную цикла».Это ловит многих людей.

Обычно, когда делегат исполняется, он всегда использует current значение loc ... и если вы уже закончили цикл довыполнение делегатов, это будет означать, что последнее значение loc отображается много раз, в основном.

Решение состоит в том, чтобы взять копию переменной цикла:

foreach (MapLocation loc in e.Result)
{
    MapLocation copy = loc;
    testDict[loc.ElemId] = loc.ToString();

    this.Dispatcher.BeginInvoke(delegate()
    {
        Image icon = new Image();
        icon.SetValue(Image.SourceProperty, nurseIconSource);
        Canvas.SetLeft(icon, (double)copy.X * MAP_SCALE);
        Canvas.SetTop(icon, MAP_HEIGHT - (double)copy.Y * MAP_SCALE);
        icons[copy.ElemId] = icon;
        MainCanvas.Children.Add(icon);
    });
}

Обратите внимание наиспользование «copy» вместо «loc» внутри анонимного метода.

Подробнее читайте в блоге Эрика Липперта на тему «Закрытие переменной цикла, считающейся вредной» - part one ; часть вторая .

1 голос
/ 19 января 2011

Вы закрываете переменную итерации.Решение состоит в том, чтобы назначить loc временной переменной внутри цикла.

    foreach (MapLocation location in e.Result)
    {
        //assign temp variable here.
        MapLocation loc = location;
        //...
    }
...