WPF DataBinding:取消属性更改 - 组合框未对齐 [英] WPF DataBinding: Cancelled property change - Combobox misaligns

查看:119
本文介绍了WPF DataBinding:取消属性更改 - 组合框未对齐的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有组合框和一个文本框的WPF窗体(两者都是数据绑定到一个Object的属性)。更改组合框或文本框输入会更新对象的属性,并且数据绑定会启动并更新UI。问题是,我实施了一种取消更改的方法,这样做有效,但是更新了UI。如果我从组合框中进行更改并取消它,则组合框不会将所选值还原到应该被对象(由对象的值绑定)。如果我从文本框中进行更改并取消它,则文本框和组合框都会显示适当的数据,但随后将焦点立即提供给组合框(当它应该保留在文本框上时,因为这是我拥有的最后一个位置它)。我不太确定如何在一般方面解决这个问题,就像处理变更事件一样,验证变更没有被取消(因为那么数据绑定是什么意思?)...

  // 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 =23Horizo​​ntalAlignment =StretchVerticalAlignment =TopMargin =12,12,12,0/>

< TextBlock Height =23Width =40Horizo​​ntalAlignment =LeftVerticalAlignment =TopMargin =13,100,0,0Text =Rank:/>
< TextBox x:Name =RankBoxHeight =23Horizo​​ntalAlignment =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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆