2010-07-19 37 views
5

Tôi có một mẫu WPF với một combobox và một hộp văn bản (cả hai là databound đến một tài sản của đối tượng). Việc thay đổi đầu vào combobox hoặc hộp văn bản sẽ cập nhật thuộc tính của đối tượng và khởi động dữ liệu vào và cập nhật giao diện người dùng. Vấn đề là, tôi đã thực hiện một cách để hủy bỏ thay đổi, hoạt động, nhưng vít lên cập nhật giao diện người dùng. Nếu tôi thực hiện thay đổi từ combobox và hủy bỏ nó, combobox không hoàn nguyên giá trị đã chọn trở về giá trị của nó (ràng buộc bởi giá trị của đối tượng). Nếu tôi thực hiện thay đổi từ hộp văn bản và hủy nó, cả hộp văn bản và hộp tổ hợp đều hiển thị dữ liệu thích hợp, nhưng sau đó tiêu điểm sẽ được đưa ngay cho combobox (khi nó nên ở trên hộp văn bản vì đó là nơi cuối cùng tôi có nó). Tôi không thực sự chắc chắn làm thế nào để đi về sửa chữa này trong một khía cạnh chung như apposed để xử lý các sự kiện thay đổi và xác minh sự thay đổi không bị hủy bỏ sau đó (vì sau đó là điểm của databinding?) Là gì? ...WPF DataBinding: Hủy bỏ tài sản thay đổi - Combobox misaligns

//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; 
     } 
    } 
} 

Phương thức cập nhật: Điều này khắc phục sự ràng buộc, nhưng không giải quyết được vấn đề trọng tâm bị đánh cắp bởi combobox khi người dùng cố gắng thay đổi giá trị trong hộp văn bản, sau đó hủy bỏ nó. Tuy nhiên, ít nhất giao diện người dùng phù hợp với các giá trị databound của nó. Tôi tìm thấy số này link đã giúp tôi.

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); 
    } 
} 

Trả lời

1

Điều này giải quyết UI hiển thị dữ liệu databound đúng ... nó chỉ không khắc phục vấn đề tập trung đánh cắp:

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); 
    } 
} 
0

Khi người dùng hủy thay đổi thuộc tính bạn vẫn nên đăng INOTifyPropertyChanged.PropertyThay đổi với giá trị cũ. Nếu các ràng buộc của bạn là thứ hai, bất kỳ điều khiển nào đã được thay đổi bởi người dùng sẽ thay đổi trở lại.

+1

Tôi đã cố gắng sửa chữa nó bằng cách thiết lập tài sản và sau đó đặt nó trở lại giá trị cũ, mỗi lần gọi OnPropertyChanged, làm tăng 2 sự kiện riêng biệt (như mong đợi với 2 cuộc gọi khác nhau), nhưng ở cuối Combobox vẫn không cập nhật đúng khi bị hủy. (Oh, và tôi đã cập nhật các generics cho các phương pháp khác nữa, không phải là nó ảnh hưởng đến bất cứ điều gì). –