WPF DataBinding:取消属性更改 - 组合框未对齐 [英] WPF DataBinding: Cancelled property change - Combobox misaligns
问题描述
// User.cs
using System;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Text;
命名空间MyTesting
{
public class User:AbstractEntity
{
public User()
{
Rankings = new Dictionary< ; int,string>();
Rankings.Add(1,Newbie);
Rankings.Add(10,新手);
Rankings.Add(25,Adept User);
Rankings.Add(50,Power User);
Rankings.Add(100,Admin God);
}
public Dictionary< Int32,String>排名{get;组; }
private Int32 _rank;
public Int32 Rank
{
get
{
return _rank;
}
set
{
SetProperty< Int32>(Rank,ref _rank,value);
}
}
}
}
//AbstractEntity.cs
using System;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Text;
命名空间MyTesting
{
public abstract class AbstractEntity:INotifyPropertyChanging,INotifyPropertyChanged
{
protected void SetProperty< T>(String propertyName,ref T property, T
{
if(!Object.Equals(property,value))
{
if(OnPropertyChanging(propertyName,property,value))
{
T oldValue =(T)属性;
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;
如果((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;私人集合}
public Object NewValue {get;私人集合
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;私人集合}
public Object NewValue {get;私人集合}
public Boolean IsCancelled {get;组; }
}
}
<! - MainWindow.xaml - >
< Window x:Class =ObservableDictionaryBinding.MainWindow
xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x =
xmlns:src =clr-namespace:MyTesting
Title =MainWindowHeight =350Width =525 Loaded =OnLoaded>
< Grid>
< ComboBox x:Name =RankListHeight =23HorizontalAlignment =StretchVerticalAlignment =TopMargin =12,12,12,0/>
< TextBlock Height =23Width =40HorizontalAlignment =LeftVerticalAlignment =TopMargin =13,100,0,0Text =Rank:/>
< TextBox x:Name =RankBoxHeight =23HorizontalAlignment =StretchVerticalAlignment =TopMargin =59,97,12,0/>
< / Grid>
< / Window>
//MainWindow.xaml.cs
using System;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Text;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;
使用System.Windows.Shapes;
命名空间MyTesting
{
public partial class MainWindow:Window
{
public MainWindow()
{
MyUser = new用户();
InitializeComponent();
MyUser.PropertyChanging + = new PropertyChangingEventHandler(MyUser_PropertyChanging);
}
私人用户MyUser {get;组; }
private Binding RankListBinding {get;组; }
private Binding RankBinding {get;组; }
private Binding RankListRankBinding {get;组;
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(对象发件人,PropertyChangingEventArgs e)
{
CancellablePropertyChangingEventArgs ea = e as CancellablePropertyChangingEventArgs;
String text = String.Format(您要将属性{0}从{1}更改为{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值)
{
if(!Object.Equals(property,value))
{
bool取消= OnPropertyChanging< T>(propertyName,property,value);
如果(已取消)
{
Application.Current.Dispatcher.BeginInvoke(
new Action(()=>
{
OnPropertyChanged< T>(propertyName);
}),
DispatcherPriority.ContextIdle,
null
);
return;
}
T originalValue = property;
property = value;
OnPropertyChanged(propertyName,originalValue,property);
}
}
解决UI显示正确的数据绑定数据...它只是不修复被盗的焦点问题:
protected void SetProperty< T> ;(String propertyName,ref T property,T value)
{
if(!Object.Equals(property,value))
{
bool cancelled = OnPropertyChanging< T> propertyName,property,value);
如果(已取消)
{
Application.Current.Dispatcher.BeginInvoke(
new Action(()=>
{
OnPropertyChanged< T>(propertyName);
}),
DispatcherPriority.ContextIdle,
null
);
return;
}
T originalValue = property;
property = value;
OnPropertyChanged(propertyName,originalValue,property);
}
}
I have a WPF form with a combobox and a textbox (both are databound to an Object's property). Changing the combobox or textbox input updates the Object's property and the databinding kicks in and updates the UI. The problem is, I implemented a way to cancel the change, which works, but screws up the UI updating. If I make the change from the combobox and cancel it, the combobox does not revert the selectedvalue back to what it should be (bound by the object's value). If I make the change from the textbox and cancel it, both the textbox and the combobox show the proper data, but then focus is instantly given to the combobox (when it should've stayed on the textbox since that is the last place I had it). I'm not really sure how to go about fixing this in a general aspect as apposed to handling change events and verifying the change wasn't cancelled afterwards (because then what is the point of 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;
}
}
}
Updated Method : This fixes the binding, but doesn't resolve the issue that the focus is stolen by the combobox when the user attempts to change a value in the textbox, then cancels it. But, at least the UI matches up in terms of it's databound values. I found this link which helped me out.
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);
}
}
This solves the UI displaying proper databound data ... it just doesn't fix the stolen focus issue:
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);
}
}
这篇关于WPF DataBinding:取消属性更改 - 组合框未对齐的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!