免责声明 这个问题与我正在做的编程课程有关。您的协助将直接影响我为此作业的成绩。
背景
当前应用程序设置
我已经使用了数据库工作流中的“实体框架代码优先”来生成要在EF上下文中公开为DBSet的类。对于DAL中生成的“ pet”类,我在UI中创建了一个对应的类,该类可复制所有“ pet”属性,但继承自Caliburn.Micro的“ Screen”并为每个“ editable”属性实现NotifyOfPropertyChange()
我还创建了一个IPet接口,该接口由生成的“ pet”类(通过我创建的局部类)和UIPet类实现。
问题描述
我试图检测单个单元格中的更改,然后激活“保存”按钮以指示网格的内容已更新但尚未保存。在我的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
如果有人可以提供一些指导或帮助来解决此问题,我将不胜感激。
谢谢。