【发布时间】:2019-12-16 08:51:39
【问题描述】:
我在多个表单上使用标签,这些表单显示从 WCF 服务调用的天气数据。我希望每分钟更新一次,以显示更新后的天气数据,而不会干扰用户交互。
我收到以下错误:
“必须在与 DependencyObject 相同的线程上创建 DependencySource。”
我有一个视图模型,用于异步获取天气数据,它继承自 ViewModelBase 以处理属性更改事件。 ViewModel 的属性绑定到标签
天气视图模型
public class WeatherDataVM : ViewModelBase
{
private string _windString;
private SolidColorBrush _windState;
private DispatcherTimer _timer;
public WeatherDataVM()
{
_timer = new DispatcherTimer(DispatcherPriority.Render);
_timer.Interval = TimeSpan.FromSeconds(10);
_timer.Tick += async (sender, args) => {await Task.Run(() => GetWindAsync()); };
//_timer.Tick += _timer_Tick;
_timer.Start();
GetWind();
}
private void GetWind()
{
var weatherFromService = Services.Instance.EmptyStackService.GetWeather();
var windSpeed = Convert.ToDouble(weatherFromService.Windspeed);
var maxGust = Convert.ToDouble(weatherFromService.Max_Gust_In_Last_Min);
var windSpeedMPH = Math.Round(windSpeed * 1.15078, 1);
var maxGustMPH = Math.Round(maxGust * 1.15078, 1);
var windString = $"W/S: {windSpeedMPH}({maxGustMPH})";
var windState = new Color();
if (windSpeed >= 40)
windState = Color.FromRgb(255, 64, 64);
else if (windSpeed >= 24)
windState = Color.FromRgb(255, 212, 128);
else
windState = Color.FromRgb(0, 255, 0);
_windState = new SolidColorBrush(windState);
_windString = windString;
}
private async Task GetWindAsync()
{
var weatherFromService = Services.Instance.EmptyStackService.GetWeather();
var windSpeed = Convert.ToDouble(weatherFromService.Windspeed);
var maxGust = Convert.ToDouble(weatherFromService.Max_Gust_In_Last_Min);
var windSpeedMPH = Math.Round(windSpeed * 1.15078, 1);
var maxGustMPH = Math.Round(maxGust * 1.15078, 1);
var windString = $"W/S: {windSpeedMPH}({maxGustMPH})";
var windState = new Color();
if (windSpeed >= 40)
windState = Color.FromRgb(255, 64, 64);
else if (windSpeed >= 24)
windState = Color.FromRgb(255, 212, 128);
else
windState = Color.FromRgb(0, 255, 0);
WindState = new SolidColorBrush(windState);
WindString = windString;
}
public string WindString
{
get { return _windString; }
set
{
if (_windString == value)
return;
_windString = value;
OnPropertyChanged("WindString");
}
}
public SolidColorBrush WindState
{
get { return _windState; }
set
{
if (_windState == value)
return;
_windState = value;
OnPropertyChanged("WindState");
}
}
}
视图模型库
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
查看标签的 Xaml
<Label x:Name="lblWeather" Content="{Binding WindString}" Foreground="black" Background="{Binding WindState}" Style="{DynamicResource SmallLabel}" />
构造函数中视图后面的代码
lblWeather.DataContext = new WeatherDataVM();
天气标签应在每次计时器滴答时发生变化。相反,它会引发错误。
【问题讨论】:
-
看起来您正在尝试在非 UI 线程上更新 UI,这会出错,因为 UI 具有线程关联性。您应该等待一个 Task lt t gt ,其中 t 包含 UI 所需的数据。然后更新 ui,回到 ui 线程。
-
您应该从您的
ViewModel中删除SolidColorBrush并改用数字或字符串,然后通过样式或转换器应用颜色。然后,您可以根据需要检索信息。这就是为什么在 ViewModel 中包含 UI 元素是一个坏主意,它总是会反击! -
SolidColorBrush 不是 UI 元素。它是一个 Freezable,因此明确设计为在 UI 线程以外的线程中创建。在视图模型中使用它完全没有问题。
标签: c# wpf mvvm async-await