代码之家  ›  专栏  ›  技术社区  ›  Yisroel M. Olewski

NotifyCollectionChangedAction.Reset清空组合框文本

  •  0
  • Yisroel M. Olewski  · 技术社区  · 5 年前

    我有一个 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
    
    0 回复  |  直到 5 年前
        1
  •  0
  •   Yisroel M. Olewski    5 年前