WPF использует Dispatcher, поэтому все работает в одном потоке пользовательского интерфейса, но имеет параллелизм по типу переключателя. И когда вы обновляете некоторую опору объекта зависимости, он фактически публикует новое задание в очереди диспетчера. Когда это задание выполняется, оно выполняется в потоке пользовательского интерфейса и правильно обновляет элементы управления.
Но если вы попытаетесь получить доступ к элементам управления непосредственно из фонового потока, вы получите исключение.
Есть некоторые крайние случаи из-за этой реализации. Например. даже если вы обновите опору в потоке пользовательского интерфейса, вы не можете ожидать, что элемент управления автоматически обновит его сам, чтобы отразить ваши изменения. Итак, если у вас есть xaml как это:
<TextBox x:name="tb" Text="{Binding Text"}/>
И такой код:
var model = new { Text = "aaa" };
tb.DataContext = model;
model.Text = "bbb";
Debug.Assert(tb.Text == "bbb");
Утверждение может быть неудачным, потому что обновление может пройти через диспетчер в следующей задаче. И он не будет обновляться, пока этот текущий вызов не будет завершен и управление не будет возвращено диспетчеру.