Случайно создавая модель просмотра дважды в XAML? [Дубликат]

Ответ Фабрисио на месте; но я хотел дополнить его ответ чем-то менее техническим, который фокусируется на аналогии, чтобы помочь объяснить концепцию асинхронности .


Аналогия ...

Вчера работа, которую я делал, требовала некоторой информации от коллеги. Я позвонил ему; вот как прошел разговор:

Me: Привет, Боб, мне нужно знать, как мы foo 'd bar ' d на прошлой неделе , Джим хочет сообщить об этом, и вы единственный, кто знает подробности об этом.

blockquote>

Боб: Конечно, но мне понадобится около 30 минут?

blockquote>

Я: Это замечательно Боб. Дайте мне звонок, когда у вас есть информация!

blockquote>

На этом этапе я повесил трубку. Поскольку мне нужна была информация от Боба, чтобы закончить мой отчет, я оставил отчет и пошел за кофе вместо этого, затем я догнал какой-то адрес электронной почты. 40 минут спустя (Боб медленно), Боб позвонил и дал мне информацию, в которой я нуждался. На этом этапе я возобновил свою работу с моим отчетом, так как у меня была вся информация, в которой я нуждался.


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

Me: Привет, Боб, мне нужно знать, как мы foo на панели 'd на прошлой неделе.

blockquote>

Боб: Конечно, но мне понадобится около 30 минут?

blockquote>

Я: Это замечательно Боб. Я подожду.

blockquote>

И я сидел и ждал. И ждал. И ждал. В течение 40 минут. Не делать ничего, кроме ожидания. В конце концов, Боб дал мне информацию, мы повесили трубку, и я закончил свой отчет. Но я потерял 40 минут производительности.


Это асинхронное и синхронное поведение

Это именно то, что происходит во всех примерах нашего вопроса. Загрузка изображения, загрузка файла с диска и запрос страницы через AJAX - это медленные операции (в контексте современных вычислений).

Вместо того, чтобы ожидает для выполнения этих медленных операций, JavaScript позволяет зарегистрировать функцию обратного вызова, которая будет выполнена при завершении медленной операции. Тем временем, однако, JavaScript будет продолжать выполнять другой код. Тот факт, что JavaScript выполняет другой код , ожидая завершения медленной операции, делает поведение асинхронным. Если JavaScript выполнил операцию до завершения любого другого кода, это было бы синхронным поведением.

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

В приведенном выше коде мы запрашиваем JavaScript для загрузки lolcat.png, который операция sloooow . Функция обратного вызова будет выполнена после выполнения этой медленной операции, но в то же время JavaScript будет продолжать обрабатывать следующие строки кода; т.е. alert(outerScopeVar).

Вот почему мы видим предупреждение, показывающее undefined; так как alert() обрабатывается немедленно, а не после загрузки изображения.

Чтобы исправить наш код, все, что нам нужно сделать, - переместить код alert(outerScopeVar) в функцию обратного вызова. Вследствие этого нам больше не нужна переменная outerScopeVar, объявленная как глобальная переменная.

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

Вы всегда будете видеть, что обратный вызов указан как функция , потому что это единственный способ в JavaScript определить код, но не выполнить его до конца.

Поэтому во всех наших примерах function() { /* Do something */ } является обратным вызовом; чтобы исправить все примеры, все, что нам нужно сделать, - это переместить код, который нуждается в ответе операции там!

* Технически вы можете использовать eval(), но eval() для этой цели является злом


Как мне ждать моего вызывающего абонента?

В настоящее время у вас есть код, похожий на этот:

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

Однако теперь мы знаем, что return outerScopeVar происходит немедленно; перед тем как функция обратного вызова onload обновила переменную. Это приводит к getWidthOfImage() возврату undefined и undefined.

Чтобы исправить это, нам нужно разрешить функции, вызывающей getWidthOfImage(), зарегистрировать обратный вызов, а затем переместить предупреждение ширины, которая должна находиться внутри этого обратного вызова,

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

... как и прежде, обратите внимание, что нам удалось удалить глобальные переменные (в данном случае width).

1
задан Krowi 6 September 2014 в 11:16
поделиться

1 ответ

В UserControl, как это, вы никогда не должны чрезмерно устанавливать DataContext в this или anyting else, потому что DataContext обычно устанавливается снаружи, когда вы используете UserControl где-нибудь в своем приложении. Внешне применяемый DataContext обычно является (частью) модели представления приложения.

Вместо этого вы должны изменить свои внутренние привязки, чтобы они использовали явное RelativeSource:

<TextBlock
    Text="{Binding Path=Watermark,
                   RelativeSource={RelativeSource AncestorType=UserControl},
                   FallbackValue=This prompt dissappears as you type...}"
    Visibility="{Binding ElementName=txtUserEntry,
                         Path=Text.IsEmpty,
                         Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox
    Name="txtUserEntry"
    Text="{Binding Path=Text,
                   UpdateSourceTrigger=PropertyChanged,
                   RelativeSource={RelativeSource AncestorType=UserControl}}" />

и затем удалите любое назначение DataContext из конструктора UserControl.

См. например, этот ответ (и многие другие подобные), которые подробно обсуждают этот вопрос.

3
ответ дан Community 26 August 2018 в 04:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: