当我从一个单独的线程使用Dispatcher
时,我有一个问题,我的一些属性没有更新。当我检查数组中的值时,它们都是0。
private string[] _years;
public string[] Years
{
get { return _years; }
private set
{
if (_years == value)
{
return;
}
_years = value;
OnPropertyChanged("Years");
}
}
private int[] _yearCounts;
public int[] YearCounts
{
get { return _yearCounts; }
private set
{
if (_yearCounts == value)
{
return;
}
_yearCounts = value;
OnPropertyChanged("YearCounts");
}
}
private ObservableCollection<RecordModel> _missingCollection;
public ObservableCollection<RecordModel> MissingCollection
{
get { return _missingCollection; }
private set
{
if (_missingCollection == value)
{
return;
}
_missingCollection = value;
OnPropertyChanged("MissingCollection");
}
}
构造函数:public MissingReportsViewModel()
{
YearCounts = new int[4];
Years = new string[4];
Initialize();
}
方法:
private void Initialize()
{
SetYears();
Task task = new Task(() =>
{
MissingCollection = new AccessWorker().GetMissingReports();
});
task.ContinueWith((result) =>
{
SetYearCounts();
});
task.Start();
}
private void SetYears()
{
for (int i = 0; i < 4; i++)
{
Years[i] = DateTime.Now.AddYears(-i).Year.ToString();
}
}
private void SetYearCounts()
{
for (int i = 0; i < 4; i++)
{
int n = i;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => YearCounts[n] = MissingCollection.Where(item =>
item.RepNum.Substring(0, 4).Equals(Years[n])).ToList().Count()));
}
}
INotifyPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
问题在于运行后,YearsCount
的各指标设为0
。如果我去掉Task.Run()和Dispatcher
,程序在漫长的操作过程中会冻结一点,但一切都显示正常。所以,我搞砸了某个地方,未能正确更新YearsCount
属性。
编辑(解决方案?):
非常感谢Garry Vass,他通过我的两个冗长的问题挂在那里,Jon Skeet(从第一篇文章开始)和Shoe。我能够使它启动并运行,没有任何问题,像这样:
XAML
四年:<TextBlock TextWrapping="Wrap"
Style="{DynamicResource SectionBodyTextStyle}">
<Run Text="{Binding YearlyStats[0].Year}"/>
<Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="1"
Style="{DynamicResource SectionBodyTextStyle}">
<Run Text="{Binding YearlyStats[1].Year}"/>
<Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="2"
Style="{DynamicResource SectionBodyTextStyle}">
<Run Text="{Binding YearlyStats[2].Year}"/>
<Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="3"
Style="{DynamicResource SectionBodyTextStyle}">
<Run Text="{Binding YearlyStats[3].Year}"/>
<Run Text=":"/>
</TextBlock>
四项统计(每年一次):
<TextBlock Text="{Binding YearlyStats[0].Count}" Grid.Column="1"
Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[1].Count}" Grid.Column="1" Grid.Row="1"
Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[2].Count}" Grid.Column="1" Grid.Row="2"
Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[3].Count}" Grid.Column="1" Grid.Row="3"
Margin="10,0,0,0"/>
c#代码
数据对象:public class MissingReportInfoModel : INotifyPropertyChanged
{
private string _year;
public string Year
{
get { return _year; }
set
{
if (_year == value)
{
return;
}
_year = value;
OnPropertyChanged("Year");
}
}
private int _count;
public int Count
{
get { return _count; }
set
{
if (_count == value)
{
return;
}
_count = value;
OnPropertyChanged("Count");
}
}
public MissingReportInfoModel()
{
Year = "Not Set";
Count = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
ViewModel属性:
private ObservableCollection<MissingReportInfoModel> _yearlyStats;
public ObservableCollection<MissingReportInfoModel> YearlyStats
{
get { return _yearlyStats; }
private set
{
if (_yearlyStats == value)
{
return;
}
_yearlyStats = value;
OnPropertyChanged("YearlyStats");
}
}
private ObservableCollection<RecordModel> _missingCollection;
public ObservableCollection<RecordModel> MissingCollection
{
get { return _missingCollection; }
private set
{
if (_missingCollection == value)
{
return;
}
_missingCollection = value;
OnPropertyChanged("MissingCollection");
}
}
视图模型构造函数:
public MissingReportsViewModel()
{
YearlyStats = new ObservableCollection<MissingReportInfoModel>();
Initialize();
}
视图模型方法:
private void Initialize()
{
SetYears();
Task task = new Task(() =>
{
MissingCollection = new AccessWorker().GetMissingReports();
});
task.ContinueWith((result) =>
{
SetCounts();
});
task.Start();
}
private void SetYears()
{
for (int i = 0; i < 4; i++)
{
var info = new MissingReportInfoModel();
info.Year = DateTime.Now.AddYears(-i).Year.ToString();
YearlyStats.Add(info);
}
}
private void SetCounts()
{
for (int i = 0; i < 4; i++)
{
int n = i;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() =>
{
YearlyStats[n].Count = MissingCollection.Where(item =>
item.RepNum.Substring(0, 4).Equals(YearlyStats[n].Year)).ToList().Count();
}));
}
}
当你展示一个项目集合时,有几件事要记住,其中包括…
- 将关系保持在单个对象下。你原来的设计使用并行数组,这更容易失去同步;
- 集合本身需要一个公共getter,以便绑定引擎可以找到它,并且它需要给出更改通知。ObservableCollection of T给出了
- 元素中的每个属性也需要提供更改通知。从INPC继承类可以实现这一点。
- 对属性的更改由WPF绑定引擎自动编组到正确的线程中,但是对T的ObservableCollection (add, remove)的更改必须由View Model编组。
这是一个典型的类为您的丢失的报告…
public class MissingReportInfo : INotifyPropertyChanged
{
private string _year;
public string Year
{
[DebuggerStepThrough]
get { return _year; }
[DebuggerStepThrough]
set
{
if (value != _year)
{
_year = value;
OnPropertyChanged("Year");
}
}
}
private int _count;
public int Count
{
[DebuggerStepThrough]
get { return _count; }
[DebuggerStepThrough]
set
{
if (value != _count)
{
_count = value;
OnPropertyChanged("Count");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
请注意,它在Interlocked类上使用了'CompareExchange',以避免潜在的竞争条件。
集合的声明如下所示:
public class ViewModel
{
public ObservableCollection<MissingReportInfo> MissingReportInfos { get; set; }
public void Initialize()
{
MissingReportInfos = new ObservableCollection<MissingReportInfo>();
}
}
最后,Xaml使用预期固定集合的文本块。这是尴尬的。相反,请尝试其中一个收集容器。这里是一个示例项目控件设置来显示丢失的报告…
<ItemsControl ItemsSource="{Binding MissingReportInfos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="{Binding Year}"/>
<TextBlock Text=":"/>
<Rectangle Width="10"/>
<TextBlock Text="{Binding Count, StringFormat='###,###,##0'}" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
酌情添加样式和其他内容。现在,Xaml不再关心集合的物理维度,更重要的是,必须维护代码的开发人员将非常感谢一个干净的实现。