ฉันมีแบบฟอร์ม WPF พร้อมคอมโบบ็อกซ์และกล่องข้อความ (ทั้งคู่เชื่อมโยงกับคุณสมบัติของวัตถุ) การเปลี่ยนคอมโบบ็อกซ์หรืออินพุตกล่องข้อความจะอัปเดตคุณสมบัติของออบเจ็กต์ และการรวมข้อมูลจะเริ่มทำงานและอัปเดต UI ปัญหาคือ ฉันใช้วิธียกเลิกการเปลี่ยนแปลง ซึ่งได้ผล แต่ทำให้การอัปเดต UI เสียหาย หากฉันทำการเปลี่ยนแปลงจาก Combobox และยกเลิก Combobox จะไม่คืนค่าที่เลือกกลับไปเป็นค่าที่ควรเป็น (ผูกไว้ด้วยค่าของวัตถุ) หากฉันทำการเปลี่ยนแปลงจากกล่องข้อความและยกเลิก ทั้งกล่องข้อความและกล่องคำสั่งผสมจะแสดงข้อมูลที่เหมาะสม แต่จากนั้นโฟกัสจะถูกส่งไปที่กล่องคำสั่งผสมทันที (เมื่อควรจะอยู่ในกล่องข้อความเนื่องจากเป็นสถานที่สุดท้ายที่ฉันมี มัน). ฉันไม่แน่ใจจริงๆ ว่าจะแก้ไขปัญหานี้อย่างไรในแง่มุมทั่วไปที่เกี่ยวข้องกับการจัดการเหตุการณ์การเปลี่ยนแปลงและการตรวจสอบว่าการเปลี่ยนแปลงไม่ได้ถูกยกเลิกหลังจากนั้น (เพราะแล้วอะไรคือประเด็นของ databinding?) ...
//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<int,string>();
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<Int32, String> Rankings { get; set; }
private Int32 _rank;
public Int32 Rank
{
get
{
return _rank;
}
set
{
SetProperty<Int32>("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<T>(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 -->
<Window x:Class="ObservableDictionaryBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:MyTesting"
Title="MainWindow" Height="350" Width="525" Loaded="OnLoaded">
<Grid>
<ComboBox x:Name="RankList" Height="23" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="12,12,12,0" />
<TextBlock Height="23" Width="40" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="13,100,0,0" Text="Rank:" />
<TextBox x:Name="RankBox" Height="23" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="59,97,12,0" />
</Grid>
</Window>
//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) ? "<null>" : ea.OldValue.ToString(),
(ea.NewValue == null) ? "<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;
}
}
}
วิธีที่อัปเดต : วิธีนี้จะแก้ไขการเชื่อมโยง แต่ไม่สามารถแก้ไขปัญหาที่โฟกัสถูกขโมยโดยคอมโบบ็อกซ์เมื่อผู้ใช้พยายามเปลี่ยนค่าในกล่องข้อความแล้วยกเลิก แต่อย่างน้อย UI ก็ตรงกันในแง่ของค่าข้อมูลที่ถูกผูกไว้ ฉันพบ ลิงก์ที่ช่วยฉันได้
protected void SetProperty<T>(String propertyName, ref T property, T value)
{
if (!Object.Equals(property, value))
{
bool cancelled = OnPropertyChanging<T>(propertyName, property, value);
if (cancelled)
{
Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
OnPropertyChanged<T>(propertyName);
}),
DispatcherPriority.ContextIdle,
null
);
return;
}
T originalValue = property;
property = value;
OnPropertyChanged(propertyName, originalValue, property);
}
}