在Datagrid内部异步更新ItemsControl(或建议更好的方法)

时间:2011-12-08 19:11:00

标签: wpf backgroundworker observablecollection

我有一个绑定到可观察集合的数据网格。网格内的每个项目都可以有多个细节线,这些细节线存储在主对象内部的可观察集合属性中。

正在从连接速度较慢的链接服务器获取行详细信息,因此我想让后台工作人员更新行详细信息OC,但是我收到一条错误消息,指出控件无法更新在制作它的线程之外。

最好的方法是什么? 2秒的延迟有点多了。

datagrid 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 个答案:

答案 0 :(得分:1)

您需要在UI线程上更新集合。为此,请确保通过Result上的DoWorkEventArgs属性传回数据。

在完成的事件中,您可以通过RunWorkerCompletedEventArgs.Result属性运行数据,并相应地设置ObservableCollection,所有这些都在UI线程上。

如果您想避开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
}

无论你走哪条路,根本原因是试图从不同的线程访问在UI线程上创建的对象。