将 IsDirty 与 ICommand 结合使用
我试图在对象编辑期间使用 IsDirty 标志来控制 CanExecute 和导航控件。
问题是,为了使其工作,我认为我必须对 IsDirty 方法使用 onPropertyChanged ,以便我的控件获得更改通知。(我希望当我的对象 IsDirty 时禁用某些控件)不幸的是,我遇到了令人讨厌的堆栈溢出,因为它陷入 IsDirty 的可怕循环...呵呵...
有人能够让类似的东西起作用吗?我所做的就是在 OnPropertyChanged 方法中将 IsDirty 设置为 true。然后在我的 canExecute 方法中,我查看它是否设置为 true,但随后在我的控件上我需要对其进行数据绑定...这导致了所有问题。
有谁知道如何实现这样的事情?
这是我的解决方案
:: In ViewModelBase
Private _isdirty As Boolean = False
Protected Property IsDirty As Boolean
Get
Return _isdirty
End Get
Set(ByVal value As Boolean)
If _isdirty = Not value Then
_isdirty = value
If _isdirty = True Then
DisableNavigation()
Else
EnableNavigation()
End If
End If
End Set
End Property
Private _haschanges As Boolean
Public Property HasChanges As Boolean
Get
Return _haschanges
End Get
Set(ByVal value As Boolean)
If value = Not _haschanges Then
_haschanges = value
OnPropertyChanged("HasChanges")
End If
End Set
End Property
Protected Sub EnableNavigation()
'Keep from firing multiple onPropertyChanged events
If HasChanges = True Then
HasChanges = False
End If
GetEvent(Of DisableNavigationEvent).Publish(False)
End Sub
Protected Sub DisableNavigation()
'Keep from firing multiple onPropertyChanged events
If HasChanges = False Then
HasChanges = True
End If
GetEvent(Of DisableNavigationEvent).Publish(True)
End Sub
::In EditViewModelBase 派生自ViewModelBase。
Protected Overrides Sub OnPropertyChanged(ByVal strPropertyName As String)
MyBase.OnPropertyChanged(strPropertyName)
If SetsIsDirty(strPropertyName) Then
If isLoading = False Then
IsDirty = True
Else
IsDirty = False
End If
End If
End Sub
''' <summary>
''' Helps prevent stackoverflows by filtering what gets checked for isDirty
''' </summary>
''' <param name="str"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Function SetsIsDirty(ByVal str As String) As Boolean
If str = "CurrentVisualState" Then Return False
If str = "TabsEnabled" Then Return False
If str = "IsLoading" Then Return False
If str = "EnableOfficeSelection" Then Return False
Return True
End Function
:: 在我看来模型
Public ReadOnly Property SaveCommand() As ICommand
Get
If _cmdSave Is Nothing Then
_cmdSave = New RelayCommand(Of DoctorOffice)(AddressOf SaveExecute, Function() CanSaveExecute())
End If
Return _cmdSave
End Get
End Property
Private Function CanSaveExecute() As Boolean
'if the object is dirty you want to be able to save it.
Return IsDirty
End Function
Private Sub SaveExecute(ByVal param As DoctorOffice)
BeginWait()
GetService(Of Services.IDoctorOfficesService).Update(SelectedDoctorOffice, False)
EndWait()
End Sub
I am trying to use an IsDirty flag to control CanExecute and Navigational controls during an object edit.
The problem is that in order for this to work I think I have to use onPropertyChanged for my IsDirty method so that my controls get change notification.(I want some controls to be disabled when my object IsDirty) Unfortunately I get a nasty stackoverflow because it spirals into a horrible loop of IsDirty...hehe..
has anyone been able to get something similar to this to work? All I am doing is setting IsDirty to true in my OnPropertyChanged method. Then in my canExecute methods I am seeing if it is set to true, but then on my controls I need to Databind to it...which is causing all the issue.
does anyone know how to implement something like this?
this is my solution
:: In ViewModelBase
Private _isdirty As Boolean = False
Protected Property IsDirty As Boolean
Get
Return _isdirty
End Get
Set(ByVal value As Boolean)
If _isdirty = Not value Then
_isdirty = value
If _isdirty = True Then
DisableNavigation()
Else
EnableNavigation()
End If
End If
End Set
End Property
Private _haschanges As Boolean
Public Property HasChanges As Boolean
Get
Return _haschanges
End Get
Set(ByVal value As Boolean)
If value = Not _haschanges Then
_haschanges = value
OnPropertyChanged("HasChanges")
End If
End Set
End Property
Protected Sub EnableNavigation()
'Keep from firing multiple onPropertyChanged events
If HasChanges = True Then
HasChanges = False
End If
GetEvent(Of DisableNavigationEvent).Publish(False)
End Sub
Protected Sub DisableNavigation()
'Keep from firing multiple onPropertyChanged events
If HasChanges = False Then
HasChanges = True
End If
GetEvent(Of DisableNavigationEvent).Publish(True)
End Sub
::In EditViewModelBase that Derives from ViewModelBase.
Protected Overrides Sub OnPropertyChanged(ByVal strPropertyName As String)
MyBase.OnPropertyChanged(strPropertyName)
If SetsIsDirty(strPropertyName) Then
If isLoading = False Then
IsDirty = True
Else
IsDirty = False
End If
End If
End Sub
''' <summary>
''' Helps prevent stackoverflows by filtering what gets checked for isDirty
''' </summary>
''' <param name="str"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Function SetsIsDirty(ByVal str As String) As Boolean
If str = "CurrentVisualState" Then Return False
If str = "TabsEnabled" Then Return False
If str = "IsLoading" Then Return False
If str = "EnableOfficeSelection" Then Return False
Return True
End Function
:: In my viewModel
Public ReadOnly Property SaveCommand() As ICommand
Get
If _cmdSave Is Nothing Then
_cmdSave = New RelayCommand(Of DoctorOffice)(AddressOf SaveExecute, Function() CanSaveExecute())
End If
Return _cmdSave
End Get
End Property
Private Function CanSaveExecute() As Boolean
'if the object is dirty you want to be able to save it.
Return IsDirty
End Function
Private Sub SaveExecute(ByVal param As DoctorOffice)
BeginWait()
GetService(Of Services.IDoctorOfficesService).Update(SelectedDoctorOffice, False)
EndWait()
End Sub
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
避免堆栈溢出的最简单方法是为 IsDirty 属性设置器添加一个保护子句:
不幸的是,如果您尝试设置 IsDirty = false,那么这样做仍然会遇到问题,因为它会通过 PropertyChanged 方法重置回 true 。为了避免这种情况,您应该检查该方法中的属性名称,如果更改的属性名称是“IsDirty”,则跳过 IsDirty 的设置。
The simplest way to avoid the stack overflow is to a a guard clause to your IsDirty property setter:
Unfortunately just doing this you'll still have an issue if you try to set IsDirty = false because it will get reset back to true by your PropertyChanged method. To avoid this you should be checking the property name in that method and skipping the setting of IsDirty if the changed property name is "IsDirty".
只需让 ICommand 的 CanExecute 谓词包含 IsDirty 属性
即可,例如
或者,如果 CanSave 仅引用 IsDirty,您可以将 ICommand 设置为:
只要
RelayCommand
对CanExecuteChanged
事件使用CommandManager.RequerySuggested
只要 ViewModel 中的任何绑定值发生更改,就会重新查询CanSave
谓词。这是一个重要的点,因为如果没有 CommandManager.RequerySuggested,WPF 将不知道要更新 UI。这可能会变得有点昂贵,因为每次视图模型中的任何值发生更改时,都会重新查询所有
RelayCommand
。但只要您的 CanExecute 谓词是一个简单的计算,它就可能可以忽略不计,即,如果您在 CanExecute 谓词中调用数据库或 Web 服务,则预计会出现一些严重的性能问题:)just have your CanExecute predicate for you ICommand to include the IsDirty property
e.g.
or if CanSave is only referencing IsDirty you could set your ICommand to be this:
As long as
RelayCommand
usesCommandManager.RequerySuggested
for theCanExecuteChanged
event theCanSave
predicate will be requeried anytime any binding value changes in the ViewModel.And this is a big point because without
CommandManager.RequerySuggested
WPF won't know to update the UI. This can become sort of expensive because everytime any value is changed in the viewmodel all of yourRelayCommand
s get requeried. But it's probably negligible as long as your CanExecute predicate is a simple calculation, i.e. if you are calling a database or web service in your CanExecute predicate expect some serious performance problems :)您不需要通知 IsDirty 已更改。只需将其设置为普通属性或字段,它就应该可以正常工作(并且没有无限循环)。
假设您使用的是每个人似乎都使用的 RelayCommand(有充分的理由),来自 MSDN 杂志中 Josh Smith 的文章。
You don't need to notify that IsDirty has changed. Just make it a plain property or field and it should work fine (and no infinite loop).
This is assuming you are using the RelayCommand that everyone seems to use (for good reason) from Josh Smith's article in MSDN Magazine.