WPF DataBinding:取消了属性更改 – Combobox未对齐

我有一个带有combobox和文本框的WPF表单(两者都是数据绑定到Object的属性)。 更改combobox或文本框输入会更新Object的属性,数据绑定会启动并更新UI。 问题是,我实现了一种取消更改的方法,这种方式有效,但却搞砸了UI更新。 如果我从combobox中进行更改并取消它,combobox不会将选定的值恢复为应该的值(由对象的值绑定)。 如果我从文本框进行更改并取消它,文本框和combobox都显示正确的数据,但然后焦点立即被给予combobox(当它应该留在文本框上,因为这是我有的最后一个地方它)。 我不确定如何在一般方面解决这个问题,因为它决定处理变更事件并validation之后没有取消变更(因为那时数据绑定的重点是什么?)……

//User.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace MyTesting { public class User : AbstractEntity { public User() { Rankings = new Dictionary(); Rankings.Add(1, "Newbie"); Rankings.Add(10, "Novice"); Rankings.Add(25, "Adept User"); Rankings.Add(50, "Power User"); Rankings.Add(100, "Admin God"); } public Dictionary Rankings { get; set; } private Int32 _rank; public Int32 Rank { get { return _rank; } set { SetProperty("Rank", ref _rank, value); } } } } //AbstractEntity.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace MyTesting { public abstract class AbstractEntity : INotifyPropertyChanging, INotifyPropertyChanged { protected void SetProperty(String propertyName, ref T property, T value) { if (!Object.Equals(property, value)) { if (OnPropertyChanging(propertyName, property, value)) { T oldValue = (T)property; property = value; OnPropertyChanged(propertyName, property, value); } } } [field: NonSerialized] public event PropertyChangingEventHandler PropertyChanging; protected virtual Boolean OnPropertyChanging(String propertyName, Object oldValue = null, Object newValue = null) { CancellablePropertyChangingEventArgs e; if ((oldValue != null) || (newValue != null)) e = new CancellablePropertyChangingEventArgs(propertyName, oldValue, newValue); else e = new CancellablePropertyChangingEventArgs(propertyName); return OnPropertyChanging(e); } protected virtual Boolean OnPropertyChanging(CancellablePropertyChangingEventArgs e) { if (PropertyChanging != null) PropertyChanging(this, e as PropertyChangingEventArgs); return !e.IsCancelled; } [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(String propertyName, Object oldValue = null, Object newValue = null) { ExtendedPropertyChangedEventArgs e; if ((oldValue != null) || (newValue != null)) e = new ExtendedPropertyChangedEventArgs(propertyName, oldValue, newValue); else e = new ExtendedPropertyChangedEventArgs(propertyName); OnPropertyChanged(e); } protected virtual void OnPropertyChanged(ExtendedPropertyChangedEventArgs e) { if (PropertyChanged != null) PropertyChanged(this, e as PropertyChangedEventArgs); } } public class ExtendedPropertyChangedEventArgs : PropertyChangedEventArgs { public ExtendedPropertyChangedEventArgs(String propertyName) : base(propertyName) { } public ExtendedPropertyChangedEventArgs(String propertyName, Object oldValue, Object newValue) : base(propertyName) { OldValue = oldValue; NewValue = newValue; } public Object OldValue { get; private set; } public Object NewValue { get; private set; } } public class CancellablePropertyChangingEventArgs : PropertyChangingEventArgs { public CancellablePropertyChangingEventArgs(String propertyName, Boolean cancel = false) : base(propertyName) { IsCancelled = cancel; } public CancellablePropertyChangingEventArgs(String propertyName, Object oldValue, Object newValue, Boolean cancel = false) : base(propertyName) { OldValue = oldValue; NewValue = newValue; IsCancelled = cancel; } public Object OldValue { get; private set; } public Object NewValue { get; private set; } public Boolean IsCancelled { get; set; } } }         //MainWindow.xaml.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace MyTesting { public partial class MainWindow : Window { public MainWindow() { MyUser = new User(); InitializeComponent(); MyUser.PropertyChanging += new PropertyChangingEventHandler(MyUser_PropertyChanging); } private User MyUser { get; set; } private Binding RankListBinding { get; set; } private Binding RankBinding { get; set; } private Binding RankListRankBinding { get; set; } private void OnLoaded(object sender, EventArgs e) { DataContext = MyUser; RankListBinding = new Binding("Rankings"); RankListBinding.Source = MyUser; RankList.SetBinding(ComboBox.ItemsSourceProperty, RankListBinding); RankList.SelectedValuePath = "Key"; RankList.DisplayMemberPath = "Value"; RankBinding = new Binding("Rank"); RankBinding.Source = MyUser; RankBox.SetBinding(TextBox.TextProperty, RankBinding); RankListRankBinding = new Binding("Rank"); RankListRankBinding.Source = MyUser; RankList.SetBinding(ComboBox.SelectedValueProperty, RankListRankBinding); } private void MyUser_PropertyChanging(Object sender, PropertyChangingEventArgs e) { CancellablePropertyChangingEventArgs ea = e as CancellablePropertyChangingEventArgs; String text = String.Format("Would you like to change the property '{0}' from '{1}' to '{2}'?", e.PropertyName, (ea.OldValue == null) ? "" : ea.OldValue.ToString(), (ea.NewValue == null) ? "" : ea.NewValue.ToString() ); MessageBoxResult result = MessageBox.Show(this, text, "Property Changed", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes); if (result == MessageBoxResult.No) ea.IsCancelled = true; } } } 

更新的方法:这修复了绑定,但没有解决当用户尝试更改文本框中的值然后取消它时combobox被焦点窃取的问题。 但是,至少UI与其数据绑定值相匹配。 我找到了这个帮助我的链接 。

 protected void SetProperty(String propertyName, ref T property, T value) { if (!Object.Equals(property, value)) { bool cancelled = OnPropertyChanging(propertyName, property, value); if (cancelled) { Application.Current.Dispatcher.BeginInvoke( new Action(() => { OnPropertyChanged(propertyName); }), DispatcherPriority.ContextIdle, null ); return; } T originalValue = property; property = value; OnPropertyChanged(propertyName, originalValue, property); } } 

这解决了UI显示正确的数据绑定数据……它只是没有修复被盗的焦点问题:

 protected void SetProperty(String propertyName, ref T property, T value) { if (!Object.Equals(property, value)) { bool cancelled = OnPropertyChanging(propertyName, property, value); if (cancelled) { Application.Current.Dispatcher.BeginInvoke( new Action(() => { OnPropertyChanged(propertyName); }), DispatcherPriority.ContextIdle, null ); return; } T originalValue = property; property = value; OnPropertyChanged(propertyName, originalValue, property); } } 

当用户取消属性更改时,您仍应使用旧值发布INotifyPropertyChanged.PropertyChanged。 如果您的绑定是双向的,则用户更改的任何控件都将更改回来。