Async Update ItemsControl Внутри Datagrid (или предложить лучший способ) - PullRequest
0 голосов
/ 08 декабря 2011

У меня есть сетка данных, связанная с наблюдаемой коллекцией. Каждый элемент в сетке может иметь несколько подробных линий, которые хранятся в наблюдаемом свойстве коллекции внутри основного объекта.

Сведения о строке извлекаются из связанного сервера с медленным подключением, поэтому я хотел бы добавить фонового работника для обновления OC сведений о строке, но я получаю сообщение об ошибке, сообщающее, что элемент управления не может быть обновлен вне нити, которая сделала это.

Какой лучший способ сделать это. 2-секундное отставание немного больше.

датагрид xaml:

    <DataGrid AutoGenerateColumns="False" Name="dgROList" ItemsSource="{Binding ElementName=MainWindow, Path=cROInfo}" CanUserDeleteRows="True" CanUserReorderColumns="False" GridLinesVisibility="Horizontal" Margin="0,112,0,0" Grid.ColumnSpan="2" AlternatingRowBackground="#FFFFE776">
        <DataGrid.Columns>
            <DataGridTextColumn Header="RO Number" Width="Auto" Binding="{Binding RONum}" />
            <DataGridTemplateColumn x:Name="roDetails" Header="RO Details" Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ItemsControl Name="LineDetails" ItemsSource="{Binding LineInfo}" Width="Auto">
                            <ItemsControl.Template>
                                <ControlTemplate TargetType="ItemsControl">
                                    <ItemsPresenter />
                                </ControlTemplate>
                            </ItemsControl.Template>
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Vertical" />
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Label Content="{Binding Line}" />
                                        <Label Content="{Binding Status}" />
                                        <Label Content="{Binding PaidAmount}" />
                                        <Label Content="{Binding SDate}" />
                                    </StackPanel>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <!--<DataGridTextColumn Header="RO Details" Width="*" Binding="{Binding RODetails}" />-->
        </DataGrid.Columns>
    </DataGrid>

OC класс:

Imports System.ComponentModel
Imports System.Collections.ObjectModel

Public Class ocROInformation
    Implements INotifyPropertyChanged

    Private _RONum As String

    Private _LineInfo As ObservableCollection(Of ocROLineInformation)

    Private _Changed As Boolean

    Private _RONumChanged As Boolean
    Private _RODetailsChanged As Boolean

    Private WithEvents bgworker As BackgroundWorker

    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Public Sub New(ronum As String)
        _RONum = ronum

        _LineInfo = New ObservableCollection(Of ocROLineInformation)

        bgworker = New BackgroundWorker
        bgworker.RunWorkerAsync()

        ' GetData() ' If I call this directly it works, just lags out while the query runs for a little bit.
    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
        If Not Propertyname.Contains("Changed") Then
            Changed = True
        End If
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
    End Sub

    Sub GetData() Handles bgworker.DoWork
        Dim DCodes As String = DealerCodes
        Dim rSelect As New ADODB.Recordset
        Dim sSql As String = "SELECT DISTINCT * FROM IGlobal WHERE RMAJBC = " & _RONum"

        Dim line As Integer
        Dim status As String = ""
        Dim sdate As Date
        Dim paidamount As Double
        Dim tdate As String

        With rSelect
            .Open(sSql, MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
            If .EOF Then
                status = "Never Received"
            End If
            Do While Not .EOF
                line = .Fields!LineNum.Value

                status = NZ(.Fields!Stat6.Value, "")

                paidamount = NZ(.Fields!PaidAmount.Value, 0)
                tdate = NZ(.Fields!SDate.Value, "")

                If Not tdate = "" And Not tdate = "0" Then
                    sdate = Date.ParseExact(tdate, "yyyyMMdd", System.Globalization.DateTimeFormatInfo.InvariantInfo)
                    _LineInfo.Add(New ocROLineInformation(line, status, sdate, paidamount))
                Else
                    _LineInfo.Add(New ocROLineInformation(line, status))
                End If

                .MoveNext()
            Loop
            .Close()
        End With
        OnPropertyChanged("RODetails")
    End Sub

    Public Property Changed() As Boolean
        Get
            Return _Changed
        End Get
        Set(ByVal value As Boolean)
            If _Changed <> value Then
                _Changed = value
                OnPropertyChanged("Changed")
            End If
        End Set
    End Property

    Public Property RONum() As String
        Get
            Return _RONum
        End Get
        Set(value As String)
            If _RONum <> value Then
                _RONum = value
                RONumChanged = True
                OnPropertyChanged("RONum")
                GetData()
            End If
        End Set
    End Property

    Public ReadOnly Property RODetails As String
        Get
            Dim output As String = ""
            For Each l As ocROLineInformation In _LineInfo
                output &= l.Print & " "
            Next

            Return output '"This is a test: " & _RONum
        End Get
    End Property

    Public ReadOnly Property LineInfo As ObservableCollection(Of ocROLineInformation)
        Get
            Return _LineInfo
        End Get
    End Property

    Public Property RODetailsChanged As Boolean
        Get
            Return _RODetailsChanged
        End Get
        Set(value As Boolean)
            If _RODetailsChanged <> value Then
                _RODetailsChanged = value
                OnPropertyChanged("RODetailsChanged")
            End If
        End Set
    End Property

    Public Property RONumChanged() As Boolean
        Get
            Return _RONumChanged
        End Get
        Set(value As Boolean)
            If _RONumChanged <> value Then
                _RONumChanged = value
                OnPropertyChanged("RONumChanged")
            End If
        End Set
    End Property

End Class

Public Class ocROLineInformation
    Implements INotifyPropertyChanged

    Private _Line As Integer
    Private _Status As String
    Private _SDate As Date
    Private _PaidAmount As Double

    Private _Changed As Boolean

    Private _LineChanged As Boolean
    Private _StatusChanged As Boolean
    Private _SDateChanged As Boolean
    Private _PaidAmountChanged As Boolean

    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Public Sub New(line As Integer, status As String, sdate As Date, paidamount As Double)
        _Line = line
        _Status = status
        _SDate = sdate
        _PaidAmount = paidamount
    End Sub

    Public Sub New(line As Integer, status As String)
        _Line = line
        _Status = status
    End Sub

    Public ReadOnly Property Print() As String
        Get
            Return "Line: " & _Line & ", Status: " & _Status & ", Amount: " & _PaidAmount & ", Date: " & _SDate.ToShortDateString
        End Get
    End Property

    Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
        If Not Propertyname.Contains("Changed") Then
            Changed = True
        End If
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
    End Sub

    Public Property Changed() As Boolean
        Get
            Return _Changed
        End Get
        Set(ByVal value As Boolean)
            If _Changed <> value Then
                _Changed = value
                OnPropertyChanged("Changed")
            End If
        End Set
    End Property

    Public Property Line() As Integer
        Get
            Return _Line
        End Get
        Set(value As Integer)
            If _Line <> value Then
                _Line = value
                LineChanged = True
                OnPropertyChanged("Line")
            End If
        End Set
    End Property

    Public Property Status() As String
        Get
            Return _Status
        End Get
        Set(value As String)
            If _Status <> value Then
                _Status = value
                StatusChanged = True
                OnPropertyChanged("Status")
            End If
        End Set
    End Property

    Public Property SDate() As Date
        Get
            Return _SDate
        End Get
        Set(value As Date)
            If _SDate <> value Then
                _SDate = value
                SDateChanged = True
                OnPropertyChanged("SDate")
            End If
        End Set
    End Property

    Private Property PaidAmount() As Double
        Get
            Return _PaidAmount
        End Get
        Set(value As Double)
            If _PaidAmount <> value Then
                _PaidAmount = value
                PaidAmountChanged = True
                OnPropertyChanged("PaidAmount")
            End If
        End Set
    End Property

    Public Property LineChanged() As Boolean
        Get
            Return _LineChanged
        End Get
        Set(value As Boolean)
            If _LineChanged <> value Then
                _LineChanged = value
                OnPropertyChanged("LineChanged")
            End If
        End Set
    End Property

    Public Property StatusChanged() As Boolean
        Get
            Return _StatusChanged
        End Get
        Set(value As Boolean)
            If _StatusChanged <> value Then
                _StatusChanged = value
                OnPropertyChanged("StatusChanged")
            End If
        End Set
    End Property

    Public Property SDateChanged() As Boolean
        Get
            Return _SDateChanged
        End Get
        Set(value As Boolean)
            If _SDateChanged <> value Then
                _SDateChanged = value
                OnPropertyChanged("SDateChanged")
            End If
        End Set
    End Property

    Public Property PaidAmountChanged() As Boolean
        Get
            Return _PaidAmountChanged
        End Get
        Set(value As Boolean)
            If _PaidAmountChanged <> value Then
                _PaidAmountChanged = value
                OnPropertyChanged("PaidAmountChanged")
            End If
        End Set
    End Property
End Class

1 Ответ

1 голос
/ 08 декабря 2011

Вам необходимо обновить коллекцию в потоке пользовательского интерфейса.Для этого обязательно передайте данные обратно через свойство Result в DoWorkEventArgs.

В вашем завершенном мероприятии вы можете затем просмотреть данные через свойство RunWorkerCompletedEventArgs.Result и соответственно установить свои ObservableCollection, все в потоке пользовательского интерфейса.

Если вы хотите избежать BackgroundWorker вы можете использовать Dispatcher.

ThreadStart start = delegate()
{
  // make your calls to the db

  Dispatcher.Invoke(DispatcherPriority.Normal, 
                    new Action<object>(UpdateCollection), 
                    new object[] { myData });
};
new Thread(start).Start();

private void UpdateCollection(object data)
{
   //iterate your collection and add the data as needed
}

Каким бы ни был ваш маршрут, основная причина - попытка получить доступ к объекту, созданному в потоке пользовательского интерфейса, из другого потока.

...