【发布时间】:2016-06-24 15:02:25
【问题描述】:
我会尽量做到详细,搜索了整个下午,我找不到任何与我的问题足够相似的东西来获得解决方案。
简要说明我的应用程序做了什么
我正在制作的应用程序提供了一个警报类型系统,用于在游戏 FFXIV 中收集物品何时可用。特定物品可以在游戏世界时间(Eorzea Time)内的特定时间收集。我的应用程序显示收集项目的列表、收集它们的开始和结束时间,以及 Next Spawn 计算(多久才能再次可用)
我的代码
我正在尝试尽可能地遵循 MVVM 模式。我有一个包含DataGrid 的视图。
AlarmView.XAML
<DataGrid Grid.Row="1"
Name="dgAlarms"
ItemsSource="{Binding AlarmsListCollection, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding SelectedAlarm}"
AutoGenerateColumns="False"
IsReadOnly="True"
IsSynchronizedWithCurrentItem="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="True"
SelectionMode="Single"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Item List"
Binding="{Binding Name}"
Width="auto"/>
<DataGridTextColumn Header="Next Spawn"
Binding="{Binding NextSpawn, UpdateSourceTrigger=PropertyChanged}"
Width="auto"
SortMemberPath="{Binding NextSpawn, UpdateSourceTrigger=PropertyChanged}"
SortDirection="Ascending"/>
<DataGridTextColumn Header="Start"
Binding="{Binding StartTime}"
Width="auto"/>
<DataGridTextColumn Header="End"
Binding="{Binding EndTime}"
Width="auto"/>
</DataGrid.Columns>
</DataGrid>
如您所见,DataGrid 的 ItemsSource 绑定到 AlarmsListCollection
在AlarmView的ViewModel中,我初始化了AlarmListCollection
//=========================================================
// Private Fields
//=========================================================
private ObservableCollection<Model.AlarmItem> _alarmsListCollection;
//=========================================================
// Properties
//=========================================================
public ObservableCollection<Model.AlarmItem> AlarmsListCollection
{
get { return this._alarmsListCollection; }
set
{
if (this._alarmsListCollection == value) return;
this._alarmsListCollection = value;
}
}
//=========================================================
// Constructor
//=========================================================
public AlarmsViewModel(DataGrid dgReference)
{
if (_alarmItemRepository == null)
_alarmItemRepository = new AlarmItemRepository();
// Initilize the AlarmsListCollection
this.AlarmsListCollection = new ObservableCollection<Model.AlarmItem>(_alarmItemRepository.GetAlarmItems());
}
_alarmItemRepository.GetAlarmItems() 只返回一个包含对象的List<Model.AlarmItem>。这里要知道的重要一点是Model.AlarmItem 包含一个名为NextSpawn 的属性。该属性是一个String,它存储了Model.AlarmItem 将在多长时间内产生的表示。
NextSpawn 属性字符串在 System.Timers.Timer 的 Elapsed 事件中每 1 秒更新一次
struct AlarmInfo
{
public TimeSpan StartTime;
public TimeSpan NextSpawn;
public bool Armed;
public bool IssueEarlyWarning;
}
private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// Go through each of the alarm items
foreach(Model.AlarmItem alarmItem in this.AlarmsListView)
{
// Get the current eorzea time span
TimeSpan currentEorzeaTimeSpan = this.EorzeaClock.GetEorzeaTimeSpan();
// Get info about the alarm item
AlarmInfo alarmInfo = new AlarmInfo();
TimeSpan.TryParse(alarmItem.StartTime, out alarmInfo.StartTime);
alarmInfo.Armed = alarmItem.Armed;
alarmInfo.IssueEarlyWarning = alarmItem.EarlyWarningIssued;
TimeSpan.TryParse(alarmItem.NextSpawn, out alarmInfo.NextSpawn);
#region CalculateTimeTillSpawn
// Get the time difference between the alarm time and eorzea time
TimeSpan timeDiff;
TimeSpan nextEorzeaSpawn;
if (alarmInfo.StartTime.Equals(new TimeSpan(0, 0, 0)))
{
timeDiff = (new TimeSpan(24, 0, 0)).Subtract(currentEorzeaTimeSpan);
}
else
{
timeDiff = alarmInfo.StartTime.Subtract(currentEorzeaTimeSpan);
}
if (alarmInfo.StartTime > currentEorzeaTimeSpan)
{
nextEorzeaSpawn = alarmInfo.StartTime.Subtract(currentEorzeaTimeSpan);
}
else
{
//alarm.TimeTillSpawnEorzea = ((TimeSpan)new TimeSpan(23, 59, 59)).Subtract(currentEorzeaTimeSpan.Subtract(alarm.StartTime));
nextEorzeaSpawn = ((TimeSpan)new TimeSpan(23, 59, 59)).Subtract(currentEorzeaTimeSpan.Subtract(alarmInfo.StartTime));
}
long earthTicks =nextEorzeaSpawn.Ticks / (long)Utilities.ClockController.EORZEA_MULTIPLIER;
alarmInfo.NextSpawn = new TimeSpan(earthTicks);
#endregion CalculateTimeTillSpawn
// Push the alarmInfo back into the alarmItem
alarmItem.Armed = alarmInfo.Armed;
alarmItem.EarlyWarningIssued = alarmInfo.IssueEarlyWarning;
alarmItem.NextSpawn = alarmInfo.NextSpawn.ToString(@"h\h\:m\m\:s\s", System.Globalization.CultureInfo.InvariantCulture);
}
this.UpdateTimer.Start();
}
一旦此代码运行,NextSpawn 属性更新,更新的信息将毫无问题地反映回DataGrid。我可以坐下来观察数据网格的 NextSpawn 列中的值在更新时每秒都在变化。但是,这导致了我遇到的问题。
问题
为了便于使用,我希望用户能够单击 DataGrid 的 Next Spawn 列标题并根据此列进行排序。这按预期 100% 有效。但是,随着Model.AlarmItems 的NextSpawn 属性值的更新,列的排序不会更新以反映任何更改。
我已经尝试了所有我能想到的方法并进行了详尽的搜索以找到解决方案。我已经尝试在计时器的经过事件中在 DataGrid 上使用 Dispatcher.Invoke() ,但这只会导致 UI 因调用频率而陷入困境。
我创建了一个 gif 来显示我在说什么。在这个 gif 中,NextSpawn 列按升序排序,您可以看到值正在更新。一旦它们到达 0h:0m:0s,在那之后 1 秒,它们就会更新到它们的新值,此时,应该进行排序以向上移动较小的值。
http://gfycat.com/RealisticInferiorAmericanriverotter
我们将非常感谢您对此提供任何帮助。
【问题讨论】:
-
.Net 4.5 引入了 ICollectionViewLiveShaping.IsLiveSorting (msdn.microsoft.com/en-us/library/…),如果您还没有看到的话。顺便说一句...漂亮的应用程序
-
@KornMuffin 你是我现在看到的最漂亮的人,这很完美。非常感谢
-
感谢分享您的解决方案并乐于提供帮助
标签: c# wpf xaml sorting datagrid