How to implement lazy loading in listview wpf VB.net - wpf

I'm newbie, I've a XAML file and code behind's, its load all data but I will lazy loading. I binding data in listview. I tried searching but did not produce the expected results, so there is no way that can be done.
code behind:
Public Sub MappingThumbnailSearchResult(thumbSearchResult As VI_ThumbnailSearchResultResponse, token As CancellationToken, ByRef dataDic As Dictionary(Of String, ObservableCollection(Of Object)))
Dim data As List(Of Object) = New List(Of Object)
Try
If thumbSearchResult.result_body Is Nothing OrElse thumbSearchResult.result_body.search_result Is Nothing Then Exit Sub
For Each item In thumbSearchResult.result_body.search_result
Try
If token.IsCancellationRequested Then Exit Sub
Dim thumbnailKey As String = ""
If Not item.thumbnail_key.Contains("\\") Then
thumbnailKey = item.thumbnail_key.ToString().Replace("\", "\\")
Else
thumbnailKey = item.thumbnail_key
End If
Dim imageResult = RequestAPI.GetInstance.GetThumbnailBase64(New VI_ThumbnailRequest With {.ThumbnailKey = thumbnailKey})
Dim hasThumb As Boolean = False
If imageResult IsNot Nothing Then
If imageResult.result_body.thumbnail IsNot Nothing Then
hasThumb = True
Dim base64Str As String = imageResult.result_body.thumbnail
item.thumbnail_base64 = imageResult.result_body.thumbnail
item.thumbnail_bitmap = BaseViewModel.BitmapFromSource(BaseViewModel.Base64ToImage(base64Str))
End If
End If
If Not hasThumb Then
'default image if have error when get thumbnail image
If item.system_type.Equals(EnumType.SystemType.face.ToString()) Then
item.thumbnail_base64 = BaseViewModel.ConvertBitmapToBase64(My.Resources.gender_unknown_2)
item.thumbnail_bitmap = My.Resources.gender_unknown_2
ElseIf item.system_type.Equals(EnumType.SystemType.people.ToString()) Then
item.thumbnail_base64 = BaseViewModel.ConvertBitmapToBase64(My.Resources.People)
item.thumbnail_bitmap = My.Resources.People
End If
End If
Dim tmpDate As DateTime
Dim shotDate As Date
If DateTime.TryParseExact(item.shot_date_time, RESPONSE_DATE_FORMAT2, Nothing, DateTimeStyles.AssumeLocal, tmpDate) Then
shotDate = tmpDate
Else
shotDate = DateTime.Now
End If
Dim dateKey As String = shotDate.ToString(PRIVATE_DATE_FORMAT)
If Not dataDic.ContainsKey(dateKey) Then
Dim thumbList = New ObservableCollection(Of Object)
Dim objThumb As Object = MappingVIThumbnailFromResponse(item, shotDate)
thumbList.Add(objThumb)
dataDic.Add(dateKey, thumbList)
Else
Dim objThumb As Object = MappingVIThumbnailFromResponse(item, shotDate)
dataDic.Item(dateKey).Add(objThumb)
End If
Catch ex As Exception
Dim b As Integer = 0
End Try
Next
Catch ex As Exception
Dim c As Integer = 0
End Try
End Sub
After, I binding data as below
Dim result As ObservableCollection(Of ListViewThumbnailViewModel) = New ObservableCollection(Of ListViewThumbnailViewModel)
If dataDic.Count > 0 Then
If token.IsCancellationRequested Then Return data
For Each pair As KeyValuePair(Of String, ObservableCollection(Of Object)) In dataDic
Dim _date As DateTime = DateTime.ParseExact(pair.Key, PRIVATE_DATE_FORMAT, CultureInfo.InvariantCulture)
result.Add(New ListViewThumbnailViewModel() With {.DateItem = _date, .ListThumbnail = pair.Value})
_viewModel.ResultSearch.TotalData += pair.Value.Count
Next
_viewModel.ResultSearch.ListResult = result
_viewModel.ResultSearch.ListResultActual = result
_viewModel.ResultSearch.PageSize = PageHelper.GetPageSizeWithZoom(_viewModel.ResultSearch.ZoomImage * 10, _viewModel.ResultSearch.PageSizeStart, _viewModel.ResultSearch.NumberChange)
_viewModel.ResultSearch.PageCurrent = 0
_viewModel.ResultSearch.TotalPage = PageHelper.GetTotalPageByPageSize(result.Count, _viewModel.ResultSearch.PageSize)
Else
_viewModel.ResultSearch.ListResult = Nothing
_viewModel.ResultSearch.ListResultActual = Nothing
End If
Catch ex As Exception
Throw New AIMatchingException(ex)
End Try
Return data
What should I do? please help me, thanks!!!

Related

NotifyCollectionChangedAction.Reset Empties ComboBox Text

I have a ComboBox, which its ItemsSource is set to an object which inherits ObservableCollection.
The object gets refreshed with new data on a timer.
Since sometimes there is a large set of new data, I don't use the Add method on the ObservableCollection, but rather I use the following code:
For Each itm In MyNewItems
Items.Add(itm)
Next
MyBase.OnPropertyChanged(New PropertyChangedEventArgs("Count"))
OnPropertyChanged(New PropertyChangedEventArgs("Items[]"))
'NEXT LINE CAUSES ISSUE
OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
The problem is that when the last line runs, the Text of the ComboBox gets reset to an empty string.
If I remove that line, then the issue is resolved, but the Items show old data, since the ComboBox doesn't know that new data came in
Please advise
With appreciation
UPDATE
Hi, as requested, I'm posting the relevant code here
1: The Xaml, Pretty Simple:
<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>
2: The TestPick class, not too complex either:
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: The QuerySource class which does all the heavy lifting
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
Okay
Thanks all for chipping in, in the end it seems like my answer is actually here
ObservableCollection : calling OnCollectionChanged with multiple new items
Works like a charm, and thank you all again for your time and patience

BackgroundWorker.ReportProgress doesn't display changes in my UI

I have a compare button which starts a background worker.
Private Sub btnCompare_Click(sender As Object, e As EventArgs) Handles btnCompare.Click
m_ProgressBar = New ProgressBar
m_ProgressBar.Show()
m_ProgressBar.txtBlockMainProgress.Dispatcher.BeginInvoke(Sub()
m_ProgressBar.txtBlockMainProgress.Text = "Comparing excel file(s)... Please wait. This might take a while."
End Sub)
Me.backgroundWorker = New BackgroundWorker
Me.backgroundWorker.WorkerReportsProgress = True
Me.backgroundWorker.WorkerSupportsCancellation = True
AddHandler Me.backgroundWorker.DoWork, AddressOf worker_DoWork
AddHandler Me.backgroundWorker.ProgressChanged, AddressOf worker_ProgressChanged
AddHandler Me.backgroundWorker.RunWorkerCompleted, AddressOf worker_RunWorkerCompleted
Me.backgroundWorker.RunWorkerAsync()
TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Normal
End Sub
The DoWork Event of my backgroundworker initialize a class Compare that has a method named Compare which accept a backgroundworker as parameter
Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
blnCompareDone = True
objExcelCompare = New Compare
With objExcelCompare
.SetThreshold = dblThreshold
.CompareToBestMatchData = blnBestMatchFlg
.CompareMerge = blnCompareMerge
.CompareTextWrap = blnCompareTextWrap
.CompareTextAlign = blnCompareTextAlign
.CompareOrientation = blnCompareOrientation
.CompareBorder = blnCompareBorder
.CompareBackColor = blnCompareBackColor
.CompareFont = blnCompareFont
.NoOfPages = intNoOfPages
.Page_Location_1 = objLocation_1
.Page_Location_2 = objLocation_2
.RemovedColumn = objRemoveCol
.RemovedRow = objRemoveRow
.AddedColumn = objAddCol
.AddedRow = objAddRow
.DataChange = objChangeData
.Compare(objWorksheet_1, objWorksheet_2, Me.backgroundWorker, e)
objEquivalentColumns = .EquivalentColumns
objEquivalentRows = .EquivalentRows
objValueResult_1 = .ValueResult_1
objValueResult_2 = .ValueResult_2
objFormatResult = .FormatResult
End With
End Sub
Inside the compare method i use ReportProgress to update my UI after comparing each page of excel file. There are total of 2 pages so the updating of progress bar will be 50% then 100%.
Public Sub Compare(ByRef p_objWorkSheet_1 As Worksheet, ByRef p_objWorkSheet_2 As Worksheet, ByRef p_backgroundWorker As BackgroundWorker, ByRef e As System.ComponentModel.DoWorkEventArgs)
If p_objWorkSheet_1 Is Nothing OrElse p_objWorkSheet_2 Is Nothing Then
'Error when no instance on either worksheets was found
Throw New Exception("No instances of worksheet is found.")
Exit Sub
End If
'********************Start of Comparison*********************
objExcelData_1 = New Dictionary(Of Integer, Dictionary(Of Tuple(Of Integer, Integer), Range))
objExcelData_2 = New Dictionary(Of Integer, Dictionary(Of Tuple(Of Integer, Integer), Range))
objEquivalentColumns = New Dictionary(Of Integer, Dictionary(Of Integer, Integer))
objEquivalentRows = New Dictionary(Of Integer, Dictionary(Of Integer, Integer))
objValueResult_1 = New Dictionary(Of Integer, Dictionary(Of Tuple(Of Integer, Integer), List(Of ValueError)))
objValueResult_2 = New Dictionary(Of Integer, Dictionary(Of Tuple(Of Integer, Integer), List(Of ValueError)))
objFormatResult = New Dictionary(Of Integer, Dictionary(Of Tuple(Of Integer, Integer), List(Of FormatError)))
'Loop through all pages
For w_intCtr_1 As Integer = 1 To intNoOfPages
If p_backgroundWorker.CancellationPending = True Then
e.Cancel = True
Return
End If
Dim w_intCurrentStep As Integer = 1
GetExcelData(p_objWorkSheet_1, objExcelData_1, objLocation_1, w_intCtr_1)
GetExcelData(p_objWorkSheet_2, objExcelData_2, objLocation_2, w_intCtr_1)
If objExcelData_1 Is Nothing OrElse objExcelData_2 Is Nothing Then
'No data to compare
Exit Sub
End If
objCompareByData = New Compare_Data
'Compare value of excelsheets
With objCompareByData
'Set threshold, excel data, and location of page to compare
.SetThreshold = dblThreshold
.CompareToBestMatchData = blnBestMatchFlg
.SetExcelData_1 = objExcelData_1(w_intCtr_1)
.SetLocation_1 = objLocation_1(w_intCtr_1)
.SetExcelData_2 = objExcelData_2(w_intCtr_1)
.SetLocation_2 = objLocation_2(w_intCtr_1)
If objRemoveCol Is Nothing = False AndAlso objRemoveCol.ContainsKey(w_intCtr_1) Then
.SetRemovedColumn = objRemoveCol(w_intCtr_1)
End If
If objRemoveRow Is Nothing = False AndAlso objRemoveRow.ContainsKey(w_intCtr_1) Then
.SetRemovedRow = objRemoveRow(w_intCtr_1)
End If
If objAddCol Is Nothing = False AndAlso objAddCol.ContainsKey(w_intCtr_1) Then
.SetAddedColumn = objAddCol(w_intCtr_1)
End If
If objAddRow Is Nothing = False AndAlso objAddRow.ContainsKey(w_intCtr_1) Then
.SetAddedRow = objAddRow(w_intCtr_1)
End If
If objChangeData Is Nothing = False AndAlso objChangeData.ContainsKey(w_intCtr_1) Then
.SetDataChange = objChangeData(w_intCtr_1)
End If
If p_backgroundWorker.CancellationPending = True Then
e.Cancel = True
Return
End If
'Proceed to compare
.Compare()
objEquivalentColumns.Add(w_intCtr_1, .EquivalentColumns)
objEquivalentRows.Add(w_intCtr_1, .EquivalentRows)
objValueResult_1.Add(w_intCtr_1, .ExcelData_Result_1)
objValueResult_2.Add(w_intCtr_1, .ExcelData_Result_2)
End With
If blnCompareMerge OrElse blnCompareTextWrap OrElse blnCompareTextAlign OrElse blnCompareOrientation OrElse blnCompareBorder OrElse blnCompareBackColor OrElse blnCompareFont Then
objCompareByFormat = New Compare_Format
'Compare format of excelsheets
With objCompareByFormat
'Set excel data to compare
.SetExcelFormat_1 = objExcelData_1(w_intCtr_1)
.SetExcelFormat_2 = objExcelData_2(w_intCtr_1)
'Set equivalent columns of page retrieved from comparing values of both excel sheets
'Set equivalent rows of page retrieved from comparing values of both excel sheets
If objEquivalentColumns Is Nothing = False AndAlso objEquivalentColumns.ContainsKey(w_intCtr_1) Then
.SetEquivalentColumns = objEquivalentColumns(w_intCtr_1)
End If
If objEquivalentRows Is Nothing = False AndAlso objEquivalentRows.ContainsKey(w_intCtr_1) Then
.SetEquivalentRows = objEquivalentRows(w_intCtr_1)
End If
.CompareMerge = blnCompareMerge
.CompareTextWrap = blnCompareTextWrap
.CompareTextAlign = blnCompareTextAlign
.CompareOrientation = blnCompareOrientation
.CompareBorder = blnCompareBorder
.CompareBackColor = blnCompareBackColor
.CompareFont = blnCompareFont
If p_backgroundWorker.CancellationPending = True Then
e.Cancel = True
Return
End If
.Compare()
'Set comparison result of page to collection
objFormatResult.Add(w_intCtr_1, .ExcelFormat_Result)
End With
End If
If p_backgroundWorker.CancellationPending = True Then
e.Cancel = True
Return
End If
'Set result to excel sheets
AddValueResultToWorkSheet(p_objWorkSheet_1, p_objWorkSheet_2, w_intCtr_1)
If p_backgroundWorker.CancellationPending = True Then
e.Cancel = True
Return
End If
'Set result to excel sheets
AddFormatResultToWorkSheet(p_objWorkSheet_2, w_intCtr_1)
p_backgroundWorker.ReportProgress((100 / intNoOfPages) * w_intCtr_1, (100 / intNoOfPages) * w_intCtr_1 & "% Completed " & w_intCtr_1 & " out of " & intNoOfPages & " pages")
Thread.Sleep(3000)
Next
End Sub
It works fine if i put Thread.Sleep(3000) after every ReportProgress. This is my ProgressChanged event handler
Private Sub worker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
m_ProgressBar.pbStatusMain.Dispatcher.BeginInvoke(Sub()
m_ProgressBar.pbStatusMain.IsIndeterminate = False
m_ProgressBar.pbStatusMain.Value = e.ProgressPercentage
End Sub)
m_ProgressBar.txtBlockMainProgress.Dispatcher.BeginInvoke(Sub()
m_ProgressBar.txtBlockMainProgress.Text = e.UserState
End Sub)
TaskbarItemInfo.ProgressValue = e.ProgressPercentage / 100
End Sub
I wonder why sometimes it work and sometimes it doesnt. Based on my research the UI thread is being flooded with message causing it to be unresponsive or I used ReportProgress to frequent which causing the UI thread to ignore the next request. What am i doing wrong? Why the changes doesnt apply in my UI?

WPF ListBox values from another selected ListBox item then move up and down

We have a series of ListBoxes - when an item in the main ListBox is selected the relevant values are displayed in the sub ListBox. This works as intended...
We also have the ability to move items up or down, and this works as intended...
When the main ListBox has the SelectionChanged event wired up the ability to move items up and down in the sub list box stops working. Comment that out and up/down works again in the sub ListBox... I must have overlooked something glaringly obvious but after numerous changes still can't get it to work
Main ListBox SelectionChanged
Private Sub Reports_CashFlow_ListBox_IndexChanged(ByVal MainLB As String, ByVal NominalLB As String, ByVal NomDT As DataTable)
Try
Dim LB As LBx = ReportCashFlow_Grid.FindName(MainLB)
If LB.SelectedIndex = -1 Then
Exit Sub
End If
Dim NomLB As LBx = ReportCashFlow_Grid.FindName(NominalLB)
If NomLB Is Nothing Then
Exit Sub
End If
If LB.SelectedValue Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name <> "DataRowView" Then
Dim CatID As Integer = LB.SelectedValue
Dim DV As New DataView(NomDT)
DV.RowFilter = "CatID = " & CatID & " AND FormID = " & Form_ID
DV.Sort = "Position"
With NomLB
.ItemsSource = DV
.Items.Refresh()
End With
LB.ScrollIntoView(LB.SelectedItem)
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Move items up
Private Sub Reports_BalanceSheet_ListBoxMoveUp(LB As ListBox, DT As DataTable, DisplayName As String, Optional MasterListBox As ListBox = Nothing)
Try
Dim StartIndex As Integer = LB.SelectedIndex
If StartIndex = -1 Then
AppBoxValidation("You have not selected an item to move up!")
Exit Sub
End If
If Not StartIndex = 0 Then
Dim CatID As Integer = 0
If DisplayName = "NomName" Then
CatID = MasterListBox.SelectedValue
End If
Dim vSelected As DataRow = DT.Rows(StartIndex)
Dim vNew As DataRow = DT.NewRow()
vNew.ItemArray = vSelected.ItemArray
DT.Rows.Remove(vSelected)
DT.Rows.InsertAt(vNew, StartIndex - 1)
DT.AcceptChanges()
LB.SelectedIndex = StartIndex - 1
Dim vPos As Integer = 0
For Each Row As DataRow In DT.Rows
If Not CatID = 0 Then
If Row("CatID") = CatID Then
Row("Position") = vPos
vPos += 1
End If
Else
Row("Position") = vPos
vPos += 1
End If
Next
LB.Items.Refresh()
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Turns out the issue related to subsets of data in the DataView - so needed to find the correct index for the selected item and the replacement index in the entire back-end table
Private Sub Reports_BalanceSheet_ListBoxMoveUp(LB As ListBox, DT As DataTable, DisplayName As String, Optional MasterListBox As ListBox = Nothing)
Try
Dim StartIndex As Integer = LB.SelectedIndex
If StartIndex = -1 Then
AppBoxValidation("You have not selected an item to move up!")
Exit Sub
End If
If Not StartIndex = 0 Then
Dim CatID As Integer = 0
If DisplayName = "NomName" Then
CatID = MasterListBox.SelectedValue
'As the view could be a subset of data we need to find the actual back end DB index
Dim SelectedID As Integer = LB.SelectedValue
Dim DR() As DataRow = DT.Select("ID = " & SelectedID, Nothing)
Dim vIndex As Integer = DT.Rows.IndexOf(DR(0))
Dim vCurrentPos As Integer = DR(0)("Position")
'Find the index of the one above in the grid
Dim DR2() As DataRow = DT.Select("CatID = " & CatID & " AND Position = " & vCurrentPos - 1, Nothing)
Dim vIndex2 As Integer = DT.Rows.IndexOf(DR2(0))
Dim vSelected As DataRow = DT.Rows(vIndex)
Dim vNew As DataRow = DT.NewRow()
vNew.ItemArray = vSelected.ItemArray
DT.Rows.Remove(vSelected)
DT.Rows.InsertAt(vNew, vIndex2)
DT.AcceptChanges()
Else
Dim vSelected As DataRow = DT.Rows(StartIndex)
Dim vNew As DataRow = DT.NewRow()
vNew.ItemArray = vSelected.ItemArray
DT.Rows.Remove(vSelected)
DT.Rows.InsertAt(vNew, StartIndex - 1)
DT.AcceptChanges()
End If
Dim vPos As Integer = 0
For Each Row As DataRow In DT.Rows
If Not CatID = 0 Then
If Row("CatID") = CatID Then
Row("Position") = vPos
vPos += 1
End If
Else
Row("Position") = vPos
vPos += 1
End If
Next
LB.SelectedIndex = StartIndex - 1
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub

WPF ListBox - refresh

I have a ListBox populated by a DataTable - adding items, moving items all work but delete doesn't - it reflects in the DataTable but clears all items from the ListBox unless it is reloaded as part of a SelectionChanged event.
I have tried Listbox.Items.Refresh and setting the ItemsSource to Nothing and re-assigning back to the DataTable.
Any ideas?
Thanks
Private Sub Reports_BalanceSheet_NominalListBox_Delete(NomLB As String, DT As DataTable)
Try
Dim LB As ListBox = Reports_BalanceSheet_Grid.FindName(NomLB)
If LB.SelectedIndex = -1 Then
AppBoxValidation("No item has been selected for deletion!")
Exit Sub
End If
Dim FR() As DataRow = DT.Select("ID = " & LB.SelectedValue, Nothing)
Dim CatID As Integer = 0
For Each row As DataRow In FR
CatID = row("CatID")
row.Delete()
Next
DT.AcceptChanges()
Dim vDV As New DataView(DT)
vDV.RowFilter = "FormID = " & FormID & " AND CatID = " & CatID
vDV.Sort = "Position"
DT = vDV.ToTable
vDV = Nothing
Dim i As Integer = 0
For Each row As DataRow In DT.Rows
row("Position") = i
i += 1
Next
With LB
.ItemsSource = DT.DefaultView
.DisplayMemberPath = "Name"
.SelectedValuePath = "ID"
End With
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Turns out the clue was 'unless it is reloaded as part of the SelectionChanged event'
added this to the end and it all works perfectly :-)
Have you ever noticed that you can spend hours going round in circles and the moment you post the problem you figure it out?
Dim MainLB As String = NomLB.Replace("Nominal", "")
Reports_BalanceSheet_ListBox_IndexChanged(MainLB, NomLB, DT)
and that runs
Private Sub Reports_BalanceSheet_ListBox_IndexChanged(ByVal MainLB As String, ByVal NominalLB As String, ByVal NomDT As DataTable)
Try
Dim LB As ListBox = Reports_BalanceSheet_Grid.FindName(MainLB)
If LB.SelectedIndex = -1 Then
Exit Sub
End If
Dim NomLB As ListBox = Reports_BalanceSheet_Grid.FindName(NominalLB)
If NomLB Is Nothing Then
Exit Sub
End If
If LB.SelectedValue Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name <> "DataRowView" Then
Dim CatID As Integer = LB.SelectedValue
Dim DT As DataTable = NomDT.Copy()
Dim vDV As New DataView(DT)
vDV.RowFilter = "CatID = " & CatID & " AND FormID = " & FormID
vDV.Sort = "Position"
DT = vDV.ToTable
vDV = Nothing
With NomLB
.ItemsSource = DT.DefaultView
.SelectedValuePath = "ID"
.DisplayMemberPath = "NomName"
.Items.Refresh()
End With
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub

WPF Move Caret position after inserting list

I am inserting lists into a RichTextBox like this - but how do I get the Caret to move to the first list item?
Private Sub TextEditor_BulletListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Disc
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Private Sub TextEditor_NumberListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Decimal
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
The easy part is setting the position of the caret... the tricky part is finding the pointer of the place that you want to set it to (unless that is simply the beginning or end of the document):
RichTextBox rtb = new RichTextBox(flowDoc);
// Get the current caret position.
TextPointer caretPos = rtb.CaretPosition;
// Set the TextPointer to the end of the current document.
caretPos = caretPos.DocumentEnd; // <<< You need to find the correct position here
// Specify the new caret position at the end of the current document.
rtb.CaretPosition = caretPos;
From RichTextBox.CaretPosition Property on MSDN.
Turns out the answer was a lot simpler that I thought :-)
Dim vMove As TextPointer = curCaret.GetNextInsertionPosition(LogicalDirection.Forward)
If Not vMove Is Nothing Then
vEditor.CaretPosition = vMove
End If
Complete
Private Sub TextEditor_BulletListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Disc
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Dim vMove As TextPointer = curCaret.GetNextInsertionPosition(LogicalDirection.Forward)
If Not vMove Is Nothing Then
vEditor.CaretPosition = vMove
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub

Resources