如何使用 MVVM 同时绑定 Progressbar 和 Datagrid?

How to bind Progressbar and Datagrid at the same time using MVVM?

我正在尝试使用 MVVM 构建进度条。基本上,我有一个主 xaml 和 2 个用户控件,1 个用于进度条,1 个用于数据网格。 我有点新,我遵循了 this 问题和答案,但我没有取得任何成功。下面是我的 ViewModel 代码和 Xaml 代码。基本上我有两个问题,

1-如何绑定 CustomerModel 或者甚至可能绑定 CustomerViewModel?我尝试将 Itemsource 绑定直接与 ObservableCollection 结合使用,我正在用我的 delegateCommand 填充它,它与 backgroundworker 一起运行但没有成功。我试过没有delegate和backgroundworker,简单地使用如下。

Me.myLoadCommand = 新 Commands.LoadCustomerModels()

我做错了什么?

<UserControl.Resources>    
        <vm:CustomerModelsVM x:Key="Customerobj"></vm:CustomerModelsVM>
    </UserControl.Resources>
<Grid >
 <DataGrid x:Name="grdData"  ItemsSource="{Binding Path=CustomerModels}"/>
</Grid>

2-如何绑定CurrentProgressBar?我试图以同样的方式绑定进度条状态,但我相信我的 ViewModel 和 Xaml 不知何故没有联系。

<UserControl x:Class="ucProgressBar"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
                    >
<Grid>
    <ProgressBar Value="{Binding CurrentProgress, Mode=OneWay}"  Visibility="{Binding ProgressVisibility}"></ProgressBar>
    <TextBlock Text="{Binding ElementName=myProgressBar, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

命名空间视图模型

Public Class CustomerModelsVM
    Implements ICustomerModelsVM
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
     Implements INotifyPropertyChanged.PropertyChanged

    Private ReadOnly worker As BackgroundWorker
    Private m_currentProgress As Integer
    Private _CustomerModels As New ObservableCollection(Of Models.CustomerModel)
    Private mySaveCommand As ICommand
    Private myLoadCommand As ICommand

    Public Sub New()
        Me.worker = New BackgroundWorker()
        Me.myLoadCommand = New DelegateCommand(Sub() Me.worker.RunWorkerAsync(), AddressOf Progressisbusy)
       ' _CustomerModels = getCustomerModels()
        Me.worker = New BackgroundWorker()
        AddHandler Me.worker.DoWork, AddressOf Me.DoWork
        AddHandler Me.worker.ProgressChanged, AddressOf Me.ProgressChanged

    End Sub

    Private Sub ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
        Me.CurrentProgress = e.ProgressPercentage
    End Sub

    Private Function Progressisbusy() As Boolean
        Return Not Me.worker.IsBusy
    End Function

    Private Sub OnPropertyChanged(Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Public ReadOnly Property CustomerModels() As ObservableCollection(Of Models.CustomerModel)
        Get
            Return _CustomerModels
        End Get
    End Property

    Public ReadOnly Property btnClick() As ICommand
        Get
            Return myLoadCommand
        End Get
    End Property

    Public Property CurrentProgress() As Integer
        Get
            Return Me.m_currentProgress
        End Get
        Private Set(value As Integer)
            If Me.m_currentProgress <> value Then
                Me.m_currentProgress = value
                OnPropertyChanged(Me.CurrentProgress)
            End If
        End Set
    End Property

    Private Sub DoWork(sender As Object, e As DoWorkEventArgs)
            _CustomerModels = getCustomerModels()
    End Sub


    Function getCustomerModels() As ObservableCollection(Of Models.CustomerModel) Implements ICustomerModelsVM.GetCustomerModels

        If _CustomerModels Is Nothing OrElse _CustomerModels.Count = 0 Then myLoadCommand.Execute(_CustomerModels)

        Return _CustomerModels
    End Function

您可以将视图模型添加为包含两个用户控件的主 window 的 DataContext。请参考以下代码。

<UserControl x:Class="UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <DataGrid x:Name="grdData" Height="200"  ItemsSource="{Binding Path=CustomerModels}"/>
    </Grid>
</UserControl>

<UserControl x:Class="UserControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
    <StackPanel>
        <Button Command="{Binding LoadCommand}">Test</Button>
        <ProgressBar Value="{Binding CurrentProgress, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Height="20" Width="200"
                     Visibility="{Binding ProgressVisibility}"></ProgressBar>        
    </StackPanel>
</UserControl>

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StakOveflw"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <local:UserControl1/>
            <local:UserControl2/>
        </StackPanel>
    </Grid>
</Window>

Class MainWindow 
    Public Sub New()
        InitializeComponent()
        Me.DataContext = New CustomerModelsVM()
    End Sub
End Class

Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports Microsoft.Practices.Prism.Commands
Imports System.Threading

Public Class CustomerModelsVM
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
     Implements INotifyPropertyChanged.PropertyChanged

    Private ReadOnly worker As BackgroundWorker
    Private m_currentProgress As Integer

    Private mySaveCommand As ICommand


    Private myLoadCommand As ICommand
    Public Property LoadCommand() As ICommand
        Get
            Return myLoadCommand
        End Get
        Set(ByVal value As ICommand)
            myLoadCommand = value
        End Set
    End Property


    Public Sub New()
        Me.worker = New BackgroundWorker()

        _CustomerModels = New ObservableCollection(Of CustomerModel)()
        AddHandler Me.worker.DoWork, AddressOf Me.DoWork
        AddHandler Me.worker.ProgressChanged, AddressOf Me.ProgressChanged
        Me.worker.WorkerReportsProgress = True
        myLoadCommand = New DelegateCommand(AddressOf LoadClick)

        ' _CustomerModels = getCustomerModels()
    End Sub
    Private Sub LoadClick()
        Me.worker.RunWorkerAsync()
    End Sub
    Private Sub ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
        CurrentProgress = e.ProgressPercentage

    End Sub

    Private Function Progressisbusy() As Boolean
        Return Not Me.worker.IsBusy
    End Function
    Private Function CalculateProgress(total As Integer, complete As Integer) As Integer
        ' avoid divide by zero error
        If total = 0 Then
            Return 0
        End If
        ' calculate percentage complete
        Dim result = CDbl(complete) / CDbl(total)
        Dim percentage = result * 100.0
        ' make sure result is within bounds and return as integer;
        Return Math.Max(0, Math.Min(100, CInt(Math.Truncate(percentage))))
    End Function

    Private Sub OnPropertyChanged(Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private _CustomerModels As ObservableCollection(Of CustomerModel)
    Public Property CustomerModels() As ObservableCollection(Of CustomerModel)
        Get
            Return _CustomerModels
        End Get
        Set(ByVal value As ObservableCollection(Of CustomerModel))
            _CustomerModels = value
        End Set
    End Property

    Public Sub GetCustomers()
        Dim total As Integer
        total = 10000
        For index = 1 To total
            Dim a As CustomerModel = New CustomerModel()
            a.NewProperty = "test" + index.ToString()

            Application.Current.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Background, Function()
                                                                                                       _CustomerModels.Add(a)
                                                                                                   End Function)
            worker.ReportProgress(CalculateProgress(total, index))
        Next
    End Sub

    Public ReadOnly Property btnClick() As ICommand
        Get
            Return myLoadCommand
        End Get
    End Property

    Public Property CurrentProgress() As Integer
        Get
            Return Me.m_currentProgress
        End Get
        Private Set(value As Integer)

            Me.m_currentProgress = value
            OnPropertyChanged("CurrentProgress")

        End Set
    End Property

    Private Sub DoWork(sender As Object, e As DoWorkEventArgs)
        _CustomerModels = getCustomerModels()
    End Sub


    Function getCustomerModels() As ObservableCollection(Of CustomerModel)
        GetCustomers()

        'Application.Current.Dispatcher.BeginInvoke(Windows.Threading.DispatcherPriority.Normal, New Action(Of Integer)(AddressOf GetCustomers), 3)
        Return _CustomerModels
    End Function
End Class

Public Class CustomerModel
    Private newPropertyValue As String
    Public Property NewProperty() As String
        Get
            Return newPropertyValue
        End Get
        Set(ByVal value As String)
            newPropertyValue = value
        End Set
    End Property

End Class

我想用一个有效的解决方案来回答我的问题。在我的例子中,问题很简单,我不得不使用调度程序来清除我的可观察集合。所以我的 Do_work 函数如下所示。在开始绑定之前,我没有清除可观察集合。添加这个简单的行使我的代码工作。

    Private Sub DoWork(sender As Object, e As DoWorkEventArgs)
        Application.Current.Dispatcher.BeginInvoke(Sub() Me.CustomerModels.Clear())

        ' Me.CustomerModels.Clear()
        For index = 1 To 100

            Dim CustomerModel As New CustomerModel With { _
                .age = 30 + index, _
                .name = "testName" & index, _
                .surname = "testSurname" & index, _
                .Id = index}

            Application.Current.Dispatcher.BeginInvoke(Sub() CustomerModels.Add(CustomerModel))
            '  CustomerModels.Add(CustomerModel)
            Thread.Sleep(100)
            worker.ReportProgress(CalculateProgress(100, index))
        Next


    End Sub