在Datagrid内部异步更新ItemsControl(或建议一种更好的方法) [英] Async Update ItemsControl Inside of a Datagrid (or suggest a better way)

查看:87
本文介绍了在Datagrid内部异步更新ItemsControl(或建议一种更好的方法)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a datagrid bound to an observable collection. Each item inside of the grid can have multiple detail lines, which are stored in an observable collection property inside of the main object.

行详细信息是从连接速度慢的链接服务器中获取的,因此我想派背景工作人员来更新行详细信息OC,但是我收到一条错误消息,指出无法更新控件在创建它的线程之外.

The line details are being fetched from a linked server with a slow connection, so I'd like to put a background worker in to update the line details OC, but I get an error saying that the control can't be updated outside of the thread that made it.

执行此操作的最佳方法是什么. 2秒的滞后时间有点长.

What's the best way to do this. The 2 second lag is a bit much.

datagrid xaml:

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

推荐答案

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

You need to update the collection on the UI thread. To do this make sure to pass the data back via the Result property on the DoWorkEventArgs.

在完成的事件中,您可以通过RunWorkerCompletedEventArgs.Result属性浏览数据,并相应地在UI线程上设置ObservableCollection.

In your completed event you can then run through the data via the RunWorkerCompletedEventArgs.Result property and set your ObservableCollection accordingly, all on the UI thread.

如果要避免使用BackgroundWorker,则可以使用Dispatcher.

If you wanted to avoid the BackgroundWorker you could use the 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线程上创建的对象.

Whatever route you go, the root cause is attempting to access an object created on the UI thread from a differing thread.

这篇关于在Datagrid内部异步更新ItemsControl(或建议一种更好的方法)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆