我有一个
ComboBox
,这是
ItemsSource
设置为继承的对象
ObservableCollection
因为有时会有大量的新数据,所以我不使用
Add
可观测集合
,但我使用以下代码:
For Each itm In MyNewItems
Items.Add(itm)
Next
MyBase.OnPropertyChanged(New PropertyChangedEventArgs("Count"))
OnPropertyChanged(New PropertyChangedEventArgs("Items[]"))
'下一行导致问题'
OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
Text
的
组合框
如果我删除了那一行,那么问题就解决了,但是这些项显示了旧数据,因为
组合框
不知道有新的数据
赞赏地
嗨,根据要求,我把相关代码贴在这里
1:Xaml,非常简单:
<Window x:Class="dlgTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mch="clr-namespace:Machshevet.Windows;assembly=Machshevet" >
<StackPanel>
<TextBlock Text="{Binding CaseID}"/>
<mch:TestPick Name="cmbTest" SelectedValuePath="ID" DisplayMemberPath="Name" SelectedValue="{Binding CaseID}" IsEditable="True" IsTextSearchEnabled="False" />
</StackPanel>
</Window>
Public Class TestPick
Inherits ComboBox
Dim usertyped As Boolean
Function Query() As IQueryable
Dim txt = ""
Dispatcher.Invoke(Sub() txt = Text)
Dim ret = GetSlimContext.Query("viwCase").Select("new (ID,Name,ClientName,SubjectName)")
If txt <> "" AndAlso usertyped Then ret = ret.TextFiltered(txt)
Return ret
End Function
Private Sub EntityPick_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Dim qs = New QuerySource(Function() Query())
Me.ItemsSource = qs
qs.Control = Me
qs.ShouldRefresh = Function() True
End Sub
Private Sub EntityPick_PreviewTextInput(sender As Object, e As TextCompositionEventArgs) Handles Me.PreviewTextInput
usertyped = True
End Sub
Private Sub TestPick_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles Me.SelectionChanged
If e.AddedItems.None Then
Dim a = 1
End If
End Sub
End Class
3:执行所有繁重工作的QuerySource类
Public Class QuerySource
Inherits ObjectModel.ObservableCollection(Of Object)
Event Refreshed(sender As QuerySource, e As EventArgs)
Property RefreshSpan As TimeSpan = TimeSpan.FromSeconds(3)
Property CheckProperties As Boolean = True
Property Control As ItemsControl
Dim Timer As Threading.Timer = New Threading.Timer(Sub() TimerTick(), Nothing, 0, 600)
Dim _lastRefresh As Date?
Dim Query As Func(Of IQueryable)
Dim workingon As Date?
Sub New(Query As Func(Of IQueryable), Control As ItemsControl)
Me.Control = Control
Me.Query = Query
End Sub
Async Sub TimerTick()
Try
If Now - _lastRefresh.GetValueOrDefault < RefreshSpan Then Exit Sub
If GetLastInputTime() > 60 * 15 Then Exit Sub
Dim isvis = False
Await Control.Dispatcher.BeginInvoke(Sub() isvis = Control.IsUserVisible())
If Not isvis Then Exit Sub
If workingon.HasValue AndAlso workingon.Value > Now.AddSeconds(-15) Then Exit Sub 'if wasnt working for 15 seconds, probaly err or something
workingon = Now
Dim fq = Query.Invoke
Dim itmtype = fq.ElementType
Dim props = itmtype.CachedProperties.Where(Function(x) x.CanWrite AndAlso x.IsScalar(True)).ToList
Dim keyprops = itmtype.CachedKeyProperties.ToList
Dim newData = fq.ToObjectList
If newData Is Nothing Then Exit Sub
Dim keySelector As Func(Of Object, Object)
Dim diff As CollectionDiff(Of Object)
If itmtype.IsScalar Then 'list of strings..
keySelector = Function(x) x
Else
If keyprops.Count <> 1 Then DevError("?")
Dim kp = keyprops.FirstOrDefault
keySelector = Function(x) kp.GetValue(x)
End If
diff = CollectionDiff(Me, newData, keySelector, props, CheckProperties)
Dim toPreserve As Object
ExecIfType(Of Primitives.Selector)(Control, Sub(x) toPreserve = x.Dispatcher.Invoke(Function() x.SelectedItem))
If toPreserve IsNot Nothing Then diff.ToPreserve = {toPreserve}.ToDictionary(Function(x) x, Function(x) Nothing)
diff.PreserveOnDelete = True
If diff.ModificationCount > 400 Or diff.ClearOld Then
CheckReentrancy()
If diff.ClearOld Then
Items.Clear()
Else
For Each pair In diff.ToReplaceByIndex
Control.Dispatcher.Invoke(Sub() Items(pair.Key) = pair.Value)
Next
For Each idx In diff.GetIndexesToDelete
Items.RemoveAt(idx)
Next
End If
For Each itm In diff.ToAdd 'for mem optimization im not using addrange
Items.Add(itm)
Next
MyBase.OnPropertyChanged(New PropertyChangedEventArgs("Count"))
OnPropertyChanged(New PropertyChangedEventArgs("Items[]"))
Control.Dispatcher.Invoke(Sub() OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)))
Else
Dim preservIdx = diff.ToPreserve?.Select(Function(x) Items.IndexOf(x.Key))?.ToHashSet
For Each pair In diff.ToReplaceByIndex
Control.Dispatcher.Invoke(Sub() Me(pair.Key) = pair.Value)
Next
For Each idx In diff.GetIndexesToDelete
If diff.PreserveOnDelete AndAlso preservIdx IsNot Nothing AndAlso preservIdx.Contains(idx) Then Continue For
Control.Dispatcher.Invoke(Sub() RemoveAt(idx))
Next
'don't use addrange - will cause a reset
Await Control.Dispatcher.BeginInvoke(Sub() diff.ToAdd.ForEach(Sub(x) Add(x)))
End If
_lastRefresh = Now
workingon = Nothing
Control.Dispatcher.Invoke(Sub()
Dim cvs = System.Windows.Data.CollectionViewSource.GetDefaultView(Me)
If cvs.SortDescriptions.None Then
Dim defsorts = {KVP("Name", False), KVP(NameOf(RecordBase.LastEditedOn), True), KVP(NameOf(LiteRecordBase.ID), True)}
For Each defsort In defsorts
If itmtype.HasProperty(defsort.Key) Then
cvs.SortDescriptions.Add(New SortDescription(defsort.Key, If(defsort.Value, ListSortDirection.Descending, ListSortDirection.Ascending)))
Exit For
End If
Next
End If
End Sub)
RaiseEvent Refreshed(Me, Nothing)
Catch ex As Exception
Control.Dispatcher.BeginInvoke(Sub() ex.Rethrow)
End Try
End Sub
End Class