如何检测绑定到Caliburn.Micro BindableCollection的WPF数据网格的单个单元格中的变化?

时间:2019-04-08 04:29:04

标签: vb.net mvvm entity-framework-6 wpfdatagrid caliburn.micro

免责声明 这个问题与我正在做的编程课程有关。您的协助将直接影响我为此作业的成绩。

背景

  • 任务是编写一个程序来协助管理兽医诊所。
  • 我们已经获得了旧版MySQL数据库,无法更改 模式。
  • 分配要求使用VB编写应用程序
  • 我正在使用Caliburn.Micro作为MVVM框架,并将其编写为WPF应用程序
  • 我正在使用Entity Framework进行数据访问,以利用内置的更改跟踪
  • 已提供屏幕布局,并且必须在分配中重复。对于“宠物管理”屏幕,信息必须显示在数据网格中,并且用户需要能够在网格内编辑记录。

当前应用程序设置

我已经使用了数据库工作流中的“实体框架代码优先”来生成要在EF上下文中公开为DBSet的类。对于DAL中生成的“ pet”类,我在UI中创建了一个对应的类,该类可复制所有“ pet”属性,但继承自Caliburn.Micro的“ Screen”并为每个“ editable”属性实现NotifyOfPropertyChange()

我还创建了一个IPet接口,该接口由生成的“ pet”类(通过我创建的局部类)和UIPet类实现。

屏幕的相关部分是: Datagrid from Manage Pet Information screen

问题描述

我试图检测单个单元格中的更改,然后激活“保存”按钮以指示网格的内容已更新但尚未保存。在我的VM中,我有一个Save()方法,该方法仅调用SaveChanges()。我已将CanSave实现为只读属性:

 Public ReadOnly Property CanSave() As Boolean
    Get
        Return pm.PetListChanged()
    End Get
End Property

当更改数据网格中的单元格时,“保存”按钮未被激活(在上面的屏幕快照中,我将CanChange属性设置为始终返回True)。

但是,如果在进行更改后单击“刷新”按钮,则会激活“保存”按钮。 (“取消”按钮的用法相同,但是我假设两个按钮都可以使用相同的修复方法,因为它们都应出于相同的原因而被激活。)

单击“保存”按钮确实将更改成功写入数据库。

我用来绑定到此数据网格的模型是:

Imports Caliburn.Micro
Imports TickedOff.DAL

Public Class PetUIModel
    Inherits Screen
    Implements IPet

    Private _customerID As Integer?
    Private _weight As Single?
    Private _gender As String
    Private _DOB As Date?
    Private _breed As String
    Private _species As String
    Private _petName As String
    Private _petID As Integer

    Public Sub New()
        bookings = New HashSet(Of Booking)()
        stays = New HashSet(Of Stay)()
    End Sub

    Public Property petID As Integer Implements IPet.petID
        Get
            Return _petID
        End Get
        Set(value As Integer)
            _petID = value
            NotifyOfPropertyChange(Function() petID)

        End Set
    End Property

    Public Property petName As String Implements IPet.petName
        Get
            Return _petName
        End Get
        Set
            _petName = Value
            NotifyOfPropertyChange(Function() petName)
        End Set
    End Property

    Public Property species As String Implements IPet.species
        Get
            Return _species
        End Get
        Set
            _species = Value
            NotifyOfPropertyChange(Function() species)

        End Set
    End Property

    Public Property breed As String Implements IPet.breed
        Get
            Return _breed
        End Get
        Set
            _breed = Value
            NotifyOfPropertyChange(Function() breed)

        End Set
    End Property

    Public Property DOB As Date? Implements IPet.DOB
        Get
            Return _DOB
        End Get
        Set
            _DOB = Value
            NotifyOfPropertyChange(Function() DOB)

        End Set
    End Property

    Public Property gender As String Implements IPet.gender
        Get
            Return _gender
        End Get
        Set
            _gender = Value
            NotifyOfPropertyChange(Function() gender)

        End Set
    End Property

    Public Property weight As Single? Implements IPet.weight
        Get
            Return _weight
        End Get
        Set
            _weight = Value
            NotifyOfPropertyChange(Function() weight)

        End Set
    End Property

    Public Property customerID As Integer? Implements IPet.customerID
        Get
            Return _customerID
        End Get
        Set
            _customerID = Value
            NotifyOfPropertyChange(Function() customerID)

        End Set
    End Property

    Public Overridable Property customer As customer Implements IPet.customer
    Public Overridable Property bookings As ICollection(Of Booking) Implements IPet.bookings
    Public Overridable Property stays As ICollection(Of Stay) Implements IPet.stays

    Public ReadOnly Property OwnerName() As String Implements IPet.OwnerName
        Get
            Return $"{Me.customer.lastName}, {Me.customer.firstName}"
        End Get
    End Property


End Class

我的ViewModel的代码是:

Imports System.Collections.ObjectModel
Imports System.Data
Imports System.Text
Imports System.Windows.Forms
Imports Caliburn.Micro
Imports TickedOff.DAL

Public Class ManagePetInfoViewModel
    Inherits Conductor(Of Object)
    Implements IHandle(Of IPet)

    Private pm As PetManager = New PetManager()
    Private _uipetList As BindableCollection(Of IPet)
    Private _selectedPet As Object 
    Private _petListPanelIsVisible As Boolean = True
    Private _addEditPanelIsVisible As Boolean = False

    Public Sub New()

        EventAggrigationProvider.TickedOffEventAggregator.Subscribe(Me)
        UIPetList = New BindableCollection(Of IPet)(pm.GetPetList)
    End Sub

    Public Property UIPetList() As BindableCollection(Of IPet)
        Get
            Return _uipetList
        End Get
        Set(ByVal value As BindableCollection(Of IPet))
            _uipetList = value

            NotifyOfPropertyChange(Function() UIPetList)
            NotifyOfPropertyChange(Function() CanDelete)
            NotifyOfPropertyChange(Function() CanSave)
            NotifyOfPropertyChange(Function() CanCancel)
        End Set
    End Property

    Public Property SelectedPet() As Object
        Get
            Return _selectedPet
        End Get
        Set(ByVal value As Object)
            _selectedPet = value
            NotifyOfPropertyChange(Function() SelectedPet)
            NotifyOfPropertyChange(Function() CanDelete)
            NotifyOfPropertyChange(Function() UIPetList)
        End Set
    End Property

    Public Property PetListPanelIsVisible() As Boolean
        Get
            Return _petListPanelIsVisible
        End Get
        Set(ByVal value As Boolean)
            _petListPanelIsVisible = value
            NotifyOfPropertyChange(Function() PetListPanelIsVisible)
            NotifyOfPropertyChange(Function() CanSave)
            NotifyOfPropertyChange(Function() UIPetList)
        End Set
    End Property

    Public Property AddEditPanelIsVisible() As Boolean
        Get
            Return _addEditPanelIsVisible
        End Get
        Set(ByVal value As Boolean)
            _addEditPanelIsVisible = value
            NotifyOfPropertyChange(Function() AddEditPanelIsVisible)
            NotifyOfPropertyChange(Function() CanSave)
            NotifyOfPropertyChange(Function() UIPetList)
        End Set
    End Property

    Public ReadOnly Property CanCancel() As Boolean
        Get
            Return pm.PetListChanged()
        End Get
    End Property

    Public ReadOnly Property CanDelete() As Boolean
        Get
            If SelectedPet IsNot Nothing Then
                Return True
            Else
                Return False
            End If
        End Get
    End Property

    Public ReadOnly Property CanSave() As Boolean
        Get
            Return pm.PetListChanged()
            'Return True
        End Get
    End Property

    Public Sub Save()
        pm.Save()
        MessageBox.Show($"Your information has been saved.", "Save Complete", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    Public Sub Cancel()
        Dim confirmCancel As DialogResult = MessageBox.Show("Are you sure you want to cancel your changes and reload the information?", "Confirm Cancel", MessageBoxButtons.YesNo)

        If confirmCancel = DialogResult.Yes Then

            'pm = New PetManager()

        End If

    End Sub

    Public Sub Add()
        ActivateItem(New AddEditPetViewModel)
        PetListPanelIsVisible = False
        AddEditPanelIsVisible = True
    End Sub

    Public Sub Delete()
        Dim confirmDelete As DialogResult = MessageBox.Show("Are you sure you want to delete the selected record?", "Confirm Delete Record", MessageBoxButtons.YesNo)

        If confirmDelete = DialogResult.Yes Then
            ' Delete the record
            UIPetList.Remove(SelectedPet)
            NotifyOfPropertyChange(Function() UIPetList)

            MessageBox.Show($"Pet record has been deleted.", "Record Deleted", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
    End Sub

    Private Sub ManagePetInfoViewModel_Deactivated(sender As Object, e As DeactivationEventArgs) Handles Me.Deactivated

        If pm.PetListChanged Then

            Dim saveChangesDialog As DialogResult = MessageBox.Show("You have unsaved changes. Would you like to save them now?", "Save Changes", MessageBoxButtons.YesNo)

            If saveChangesDialog = DialogResult.Yes Then
                pm.Save()
            End If
        End If
    End Sub

    Private Sub RefreshData()
        Refresh()
    End Sub

    Public Sub Handle(message As IPet) Implements IHandle(Of IPet).Handle
        message.petID = pm.GetNextPetID()
        message.customer = pm.GetCustomerByID(message.customerID)

        'Add the newly created Pet to the list of pets in the context (RAM)
        pm.AddPet(message)
        NotifyOfPropertyChange(Function() UIPetList)

        'Hide the Add Pet screen, and go back to the Pet Info screen
        PetListPanelIsVisible = True
        AddEditPanelIsVisible = False
    End Sub
    End Class

所引用的PetManager类的代码为:

Imports System.Collections.ObjectModel
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Windows.Forms
Imports TickedOff.DAL
Imports Caliburn.Micro

Public Class PetManager

    Private _db As TickedOffContext = New TickedOffContext()
    Private _PetList As IEnumerable(Of IPet)

    Public Sub New()
        PetList = GetPetList()
    End Sub

    Public Property PetList() As IEnumerable(Of IPet)
        Get
            Return _PetList
        End Get
        Set
            _PetList = Value
        End Set
    End Property

    Public Function GetPetList() As IEnumerable(Of IPet)
        Return _db.Pets.ToList
    End Function

    Public Function PetListChanged() As Boolean
        Return _db.ChangeTracker.HasChanges()
    End Function

    Public Function GetOwnersList() As List(Of customer)
        Return _db.Customers.ToList()
    End Function

    ''' <summary>
    ''' Returns the next available PetID to be used.
    '''
    ''' NOTE:
    ''' This function is only required because the legacy database (provided)
    ''' does NOT have the Auto-Identity attribute in the PetID field set. The database
    ''' cannot easily be changed (legacy databases should not be changed anyway)
    ''' to include the auti-increment attribute.
    ''' </summary>
    Public Function GetNextPetID() As Integer

        Dim output As Integer

        ' Get the last petID from the context
        Dim lastpet As pet = PetList.Last()

        'Increment the petID field
        output = lastpet.petID + 1

        Return output
    End Function

    Public Function GetCustomerByID(custId As Integer) As customer
        Dim output As customer
        Dim q = From c As customer In _db.Customers
                Where c.customerID = custId
                Select c
        output = q.FirstOrDefault()
        Return output
    End Function

    Public Sub AddPet(p As IPet)
        Dim newpet As pet = p
        _db.Pets.Add(p)
        MessageBox.Show("Pet Added to Context")
    End Sub

    Public Sub Save()
        _db.SaveChanges()
    End Sub

    Public Sub Delete(pet As IPet)
        _db.Pets.Remove(pet)
    End Sub

End Class

最后,我正在使用的数据网格的XAML:

<!-- Pet Management Grid -->
            <DataGrid x:Name="UIPetList"
                  Margin="15 0 15 0"
                  SelectedItem="{Binding Path=SelectedPet, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"
                  AlternatingRowBackground="AliceBlue"
                  AutoGenerateColumns="False"
                  MaxHeight="250"
                  CanUserResizeColumns="False"
                  CanUserResizeRows="False"
                  CanUserReorderColumns="False"
                  SelectionMode="Single"
                  IsSynchronizedWithCurrentItem="True">

                <DataGrid.Columns>
                    <DataGridTextColumn Header="ID"
                                    Binding="{Binding PetID, Mode=OneWay}"
                                    Width="40" />
                    <DataGridTextColumn Header="Name"
                                    Binding="{Binding petName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="150" />
                    <DataGridTextColumn Header="Species"
                                    Binding="{Binding species, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="75" />
                    <DataGridTextColumn Header="Breed"
                                    Binding="{Binding breed, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="130" />
                    <DataGridTextColumn Header="DOB"
                                    Binding="{Binding DOB, StringFormat={}{0:dd/MM/yyyy}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="100" />
                    <DataGridTextColumn Header="Gender"
                                    Binding="{Binding gender, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="60">
                        <!-- Centre the Gender in the column -->
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="TextBlock">
                                <Setter Property="HorizontalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Weight"
                                    Binding="{Binding weight, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                    Width="60">
                        <!-- Centre the Weight in the column -->
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="TextBlock">
                                <Setter Property="HorizontalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Customer"
                                    Binding="{Binding Path=OwnerName, Mode=OneWay, UpdateSourceTrigger=LostFocus}"
                                    Width="*" />
                </DataGrid.Columns>
            </DataGrid>

            <!-- Control Buttons -->
            <Grid Margin="30">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width=" 1*" />
                    <ColumnDefinition Width=" 1*" />
                    <ColumnDefinition Width=" 1*" />
                    <ColumnDefinition Width=" 1*" />
                    <ColumnDefinition Width=" 1*" />
                </Grid.ColumnDefinitions>
                <Button x:Name="Save"
                    Grid.Column="0"
                    Content="Save"
                    ToolTip="Save changes to the database"
                    Margin="5"
                    Padding="5" />

                <Button x:Name="Add"
                    Grid.Column="1"
                    Content="Add"
                    ToolTip="Add a new pet to the database"
                    Margin="5"
                    Padding="5" />
                <Button x:Name="Delete"
                    Grid.Column="2"
                    Content="Delete"
                    ToolTip="Delete selected record from the database"
                    Margin="5"
                    Padding="5" />
                <Button x:Name="Cancel"
                    Grid.Column="3"
                    Content="Cancel"
                    ToolTip="Cancel all pending changes."
                    Margin="5"
                    Padding="5" />
                <Button x:Name="Refresh"
                    Grid.Column="4"
                    Content="Refresh"
                    ToolTip="Refresh the pet list from the database, erasing any unsaved changes."
                    Margin="5"
                    Padding="5" />
            </Grid>

我已参考并尝试了以下文章中的修复程序。一切都不成功,所以我基本上删除或注释了建议的代码。

WPF DataGrid - Committing changes cell-by-cell

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

WPF Autogenerated DataGrid Cell changed event when bound to ItemSource

Caliburn.Micro and DataGrid: Reliable approach for detecting individual cell changes?

WPF DataGrid bound to entity framework doesn't update after change of data in ViewModel

如果有人可以提供一些指导或帮助来解决此问题,我将不胜感激。

谢谢。

0 个答案:

没有答案