这个问题已经引发了一段时间的麻烦,并且阻止了项目的进展。考虑一个WPF XAML表单,其中包含绑定到ViewModel的控件。 (我正在使用Caliburn.Micro MVVM框架和Entity Framework来获取数据)。 shell调用Initialize()
方法从数据库加载表单的数据并设置PropertyChanged事件处理程序。有一个IsDirty
标志可以跟踪表单中是否有更改的数据。有一个“保存”按钮绑定到IsDirty
属性,以便在数据更改时启用它。
// Sample code; forms have many controls....
// this is the property that the controls are bound to
public Entity BoundData { get; set; }
public void Initialize()
{
// this is an example line where I query the database from the Entity Framework ObjectContext...
BoundData = objectContext.DataTable.Where(entity => entity.ID == 1).SingleOrDefault();
// this is to cause the form bindings to retrieve data from the BoundData entity
NotifyOfPropertyChange("BoundData");
// wire up the PropertyChanged event handler
BoundData.PropertyChanged += BoundData_PropertyChanged;
IsDirty = false;
}
void BoundData_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
IsDirty = true;
}
// implementation of the IsDirty flag
public bool IsDirty
{
get
{
return _isDirty;
}
set
{
_isDirty = value;
NotifyOfPropertyChange("IsDirty");
}
}
问题是BoundData_PropertyChanged
事件处理程序是由于在Initialize()
方法完成后从数据库初始化表单而被命中的。因此,IsDirty
标志设置为true,并且启用了“保存”按钮,即使表单刚刚加载且用户未更改任何内容。我错过了什么?当然这是一个常见的问题,但我一直无法找到一个好的解决方案。这是我的第一个MVVM项目,所以我完全有可能错过一些基本概念。
更新:为了澄清,我认为问题是我需要能够挂钩一个事件或回调,当所有绑定都完成更新时会触发,所以我可以接线PropertyChanged事件处理程序。
答案 0 :(得分:0)
您可以做的一件事可能有帮助,就是设置触发更改的属性,如下所示:
public virtual bool Prop1
{
get
{
return _prop1;
}
set
{
if (_prop1 != value)
{
_prop1 = value;
NotifyOfPropertyChange("IsDirty");
}
}
这样,只有在实际更改了值时才触发事件,而不仅仅是冗余设置。这当然假设在您的情况下该值实际上没有变化。
答案 1 :(得分:0)
我建议你设计一个通用的方法调用。即类似ValueChanged
事件处理程序(自定义事件处理程序),它又调用BoundData_PropertyChanged。这意味着,只要控件发生更改,就可以调用此ValueChanged方法/处理程序。
例如
void ValueChanged(object sender, EventArgs e)
{
BoundData_PropertyChanged();
}
textBox.TextChanged += new System.EventHandler(ValueChanged);
语法可能不准确,但您可以猜到我在这里提出的建议。
答案 2 :(得分:0)
这个解决方案可能不是绝对可靠的,但在我使用的有限测试用例中它对我有用。
在第一次输入时,EntityKey值将为null。检查一下。
/// <summary>
/// Log the invoice status change
/// </summary>
/// <param name="value">The value that the invoice status is changing to</param>
partial void OnInvoiceStatusValueChanging(string value)
{
var newStatus = ConvertInvoiceStatus(value);
if (this.EntityKey != null && InvoiceStatus != newStatus)
{
AddNewNote(string.Format("Invoice status changing from [{0}] to [{1}]", InvoiceStatus.GetDescription(), newStatus.GetDescription()));
}
}
/// <summary>
/// Log the invoice status change
/// </summary>
partial void OnInvoiceStatusValueChanged()
{
if (this.EntityKey != null)
AddNewNote(string.Format("Invoice status changed to [{0}]", InvoiceStatus.GetDescription()));
}
答案 3 :(得分:0)
我知道这个问题很古老,但我遇到了同样的问题,很难搞清楚。我对WPF / MVVM很陌生,可能并不知道谷歌要求的东西。这是我的解决方案。希望它可以帮助某人。
我的IsDirty标志几乎与原帖中的标志相同。唯一的区别是我在视图完成渲染时添加了一个命令将其重置为false。基本想法来自Notify ViewModel when View is rendered/instantiated
在视图中触发已加载的事件:
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Path=OnLoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
然后在视图模型中,在事件触发时将IsDirty标志设置为false。
public ICommand OnLoadedCommand { get; private set; }
// Constructor
public MyUserControlViewModel()
{
OnLoadedCommand = new DelegateCommand(OnLoaded);
}
public void OnLoaded()
{
// Ignore any PropertyChanged events that fire
// before the UserControl is rendered.
IsDirty = false;
}