2012-12-12 20 views
7

tl; dr: Zorlanmış değerler, veri bağlamaları genelinde yayılmaz. Kod arkası, bağlamanın diğer tarafını bilmediğinde, güncellemeyi veri bağlama genelinde nasıl zorlayabilirim?Zorunlu Değerlerin Yayılımı


WPF bağımlılık özelliği üzerinde CoerceValueCallback kullanıyorum ve bağlamaları üzerine yayılır alamadım değerleri dayatılmış konusu sıkıştım.

Window1.xaml.cs

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Media; 

namespace CoerceValueTest 
{ 
    public class SomeControl : UserControl 
    { 
     public SomeControl() 
     { 
      StackPanel sp = new StackPanel(); 

      Button bUp = new Button(); 
      bUp.Content = "+"; 
      bUp.Click += delegate(object sender, RoutedEventArgs e) { 
       Value += 2; 
      }; 

      Button bDown = new Button(); 
      bDown.Content = "-"; 
      bDown.Click += delegate(object sender, RoutedEventArgs e) { 
       Value -= 2; 
      }; 

      TextBlock tbValue = new TextBlock(); 
      tbValue.SetBinding(TextBlock.TextProperty, 
           new Binding("Value") { 
           Source = this 
           }); 

      sp.Children.Add(bUp); 
      sp.Children.Add(tbValue); 
      sp.Children.Add(bDown); 

      this.Content = sp; 
     } 

     public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", 
                           typeof(int), 
                           typeof(SomeControl), 
                           new PropertyMetadata(0, ProcessValueChanged, CoerceValue)); 

     private static object CoerceValue(DependencyObject d, object baseValue) 
     { 
      if ((int)baseValue % 2 == 0) { 
       return baseValue; 
      } else { 
       return DependencyProperty.UnsetValue; 
      } 
     } 

     private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e) 
     { 
      ((SomeControl)source).ProcessValueChanged(e); 
     } 

     private void ProcessValueChanged(DependencyPropertyChangedEventArgs e) 
     { 
      OnValueChanged(EventArgs.Empty); 
     } 

     protected virtual void OnValueChanged(EventArgs e) 
     { 
      if (e == null) { 
       throw new ArgumentNullException("e"); 
      } 

      if (ValueChanged != null) { 
       ValueChanged(this, e); 
      } 
     } 

     public event EventHandler ValueChanged; 

     public int Value { 
      get { 
       return (int)GetValue(ValueProperty); 
      } 
      set { 
       SetValue(ValueProperty, value); 
      } 
     } 
    } 

    public class SomeBiggerControl : UserControl 
    { 
     public SomeBiggerControl() 
     { 
      Border parent = new Border(); 
      parent.BorderThickness = new Thickness(2); 
      parent.Margin = new Thickness(2); 
      parent.Padding = new Thickness(3); 
      parent.BorderBrush = Brushes.DarkRed; 

      SomeControl ctl = new SomeControl(); 
      ctl.SetBinding(SomeControl.ValueProperty, 
          new Binding("Value") { 
          Source = this, 
          Mode = BindingMode.TwoWay 
          }); 
      parent.Child = ctl; 

      this.Content = parent; 
     } 

     public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", 
                           typeof(int), 
                           typeof(SomeBiggerControl), 
                           new PropertyMetadata(0)); 

     public int Value { 
      get { 
       return (int)GetValue(ValueProperty); 
      } 
      set { 
       SetValue(ValueProperty, value); 
      } 
     } 
    } 

    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

Window1.xaml

<Window x:Class="CoerceValueTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="CoerceValueTest" Height="300" Width="300" 
    xmlns:local="clr-namespace:CoerceValueTest" 
    > 
    <StackPanel> 
     <local:SomeBiggerControl x:Name="sc"/> 
     <TextBox Text="{Binding Value, ElementName=sc, Mode=TwoWay}" Name="tb"/> 
     <Button Content=" "/> 
    </StackPanel> 
</Window> 

yani iki kullanıcı kontrolleri, diğer iç içe bir, ve pencerenin bulunanların dış biri . İç kullanıcı denetimi, dış kontrolün bir Value bağımlılık özelliğine bağlı olan bir Value bağımlılık özelliğine sahiptir. Pencerede, bir TextBox.Text özelliği dış denetimin Value özelliğine bağlıdır.

İç denetim, Value özelliğine sahip bir CoerceValueCallback özelliğine sahiptir, bunun etkisi bu Value özelliğinin yalnızca çift sayılarla atanabilmesidir.

Bu kodun gösterim amacıyla basitleştirildiğini unutmayın. Gerçek sürüm kurucudaki hiçbir şeyi başlatmaz; İki kontrol aslında burada ilgili kurucularda yapılan her şeyi yapan kontrol şablonlarına sahiptir. Yani, gerçek kodda, dış kontrol iç kontrolü bilmiyor.

metin kutusuna çift sayıda yazma ve (metin kutusunun altındaki kukla butonuna odaklanarak mesela) odak değiştirirken, hem Value özellikleri usulüne güncellenmektedir. Ancak, metin kutusuna tek bir sayı yazarken, iç denetimin Value özelliği değişmez, dış denetimin Value özelliği ve TextBox.Text özelliği tek sayıyı gösterir.

Soruma soru: Metin kutusunda bir güncelleştirmeyi nasıl zorlayabilirim (ve ideal olarak dış denetimin Value özelliğindeyken, biz de varız)?

an SO question on the same problem'u buldum, ama gerçekten bir çözüm sunmuyor. Değeri sıfırlamak için bir özellik değiştirilen olay işleyicisini kullanmaya itiraz eder, ancak görebildiğim kadarıyla, bu, değerlendirme kodunun dış denetime çoğaltılması anlamına gelir. Temelde bilgi sadece iç kontrolde bilinir (fazla çaba gerektirmez).

Üstelik this blogpost yukarıda ima olarak, ilk CoerceValueCallback yılında TextBox.Text bağlanma, ancak üzerinde UpdateTarget çağırma önerir içimdeki kontrolü muhtemelen metin kutusu hakkında herhangi bir bilgiye sahip olamaz ve ikinci, muhtemelen ilk UpdateSource aramak zorunda kalacaktı iç kontrolün Value özelliğinin bağlanması üzerine.CoerceValue yönteminde olduğu gibi, bunun nerede yapıldığını göremiyorum, kodlanmış değer henüz ayarlanmadı (bu yüzden bağlamayı güncellemek için çok erken), değer CoerceValue tarafından sıfırlanırsa, özellik değeri, yalnızca ne olduğu kalmaya devam edecektir, dolayısıyla bir özellik değiştirilen geri arama çağrılmayacaktır (this discussion'da da belirtildiği gibi). Ben geleneksel bir özellik ve INotifyPropertyChanged uygulaması ile SomeControl yılında bağımlılık özelliği yerine geçecekti düşünmüştü

olası bir geçici çözüm (yani elle değeri zorla olsa bile PropertyChanged olayı tetikleyebilir). Ancak, bu, artık bu mülk üzerinde bir bağlayıcılık beyan edemeyeceğim anlamına geliyor, bu yüzden gerçekten yararlı bir çözüm değil.

+2

Bunun için bir cevap bulursan bana haber ver =) –

cevap

3

Bu oldukça kötü bir yanıta bir süre kendim yanıt arıyorum.

  • sizin CoerceValue geri arama çıkarın: bağlantılarındaki bir UpdateTarget zorlamak gerek kalmadan, bunu yapmak için bir yolu budur.CoerceValue geri bildiriminin mantığını Geri DöngüDeğiştirme geri çağırır. (Sayısı tek olduğunda)
  • Sen geri arama iki kez vurulduktan ama sona erecek zorla değeri etkili bir şekilde itti ProcessValueChanged ile sona erecek zaman uygulanabilir
  • sizin Değer mülk, sizin zorla değeri atama Bağlantınıza kodunuzu

Baz, sizin bağımlılık özelliği bildirisinin bu olacaktı:

public static readonly DependencyProperty ValueProperty = 
         DependencyProperty.Register("Value", 
                typeof(int), 
                typeof(SomeControl), 
                new PropertyMetadata(0, ProcessValueChanged, null)); 

Ve sonra, sizin bu olacaktı ProcessValueChanged: Biraz mantık modifiye

private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e) 
    { 
     int baseValue = (int) e.NewValue; 
     SomeControl someControl = source as SomeControl; 
     if (baseValue % 2 != 0) 
     { 
      someControl.Value = DependencyProperty.UnsetValue; 
     } 
     else 
     { 
      someControl.ProcessValueChanged(e); 
     } 
    } 

, değerin zorlanması gerektiğinde olayın yükseltilmesini önlemek için. Daha önce bahsedildiği gibi, someControl.Value'a atanan zorlanan değer, ProcessValueChanged'un art arda iki defa çağrılmasına neden olur. Diğer ifadeyi koymak yalnızca olayları geçerli değerler ile bir kez yükseltecektir.

Umarım bu yardımcı olur!

+0

Umut verici - Bunu biraz kontrol edip cevabı kabul edip edemeyeceğime karar vereceğim, ama o zamana kadar biraz zaman alabileceğinden, şimdilik +1 . –

+1

Bunu denedim ve benim için işe yaramadı. –

İlgili konular