2012-04-26 21 views
18

benim WPF uygulamasında ValidationRules kullanmaya başladı ama oldukça karışık alıyorum güncellenir. aşağıdaki gibi ValidationRule BindingExpression ile gördü yerine değerini

class RequiredRule : ValidationRule 
{ 
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     if (String.IsNullOrWhiteSpace(value as string)) 
     { 
      return new ValidationResult(false, "Must not be empty"); 
     } 
     else 
     { 
      return new ValidationResult(true, null); 
     } 

    } 
} 

XAML Kullanılan:

<TextBox> 
    <TextBox.Text> 
     <Binding Path="Identity.Name"> 
      <Binding.ValidationRules> 
       <validation:RequiredRule/> 
      </Binding.ValidationRules> 
     </Binding> 
    </TextBox.Text> 
</TextBox> 

Bu çoğunlukla eser ben beklediğiniz gibi

Aşağıdaki basit kuralı var. Kaynak mülkümün (Identity.Name) ayarlanmadığını görmek beni şaşırttı; Değişimi hiçbir zaman görmeyen bir geri alma işlevim var ve yeniden yazmanın dışında bir değeri geri döndürmenin bir yolu yok (iyi değil).

Microsoft'un Data Binding Overview çok iyi bu davranışı açıklar altına yakın doğrulama işlemini tarif etmektedir. Buna dayanarak, ValidationStepUpdatedValue olarak ayarlanmış olmasını isterim.

<validation:RequiredRule ValidationStep="UpdatedValue"/> 

Bu, işlerin benim için tuhaf olduğu yer. Validate() nesne değeri ile ayarlanmış olan özellik değeri (yani bir dize) olarak adlandırılmak yerine, bir System.Windows.Data.BindingExpression olsun! Microsoft'un bu davranışını açıklayan belgelerinde hiçbir şey görmüyorum.

Hata ayıklayıcıda, kaynak nesneyi (TextBox'un DataContext) görebiliyor, özellik yoluna gidip değerin ayarlandığını görebiliyorum. Bununla birlikte, doğrulama kuralı içinde doğru mülke ulaşmak için iyi bir yol göremiyorum.

Not: ValidationStepConvertedProposedValue ile girilen dizeyi (kullanmış olduğum bir dönüştürücüm yok) elde ediyorum, ancak beklendiği gibi doğrulama başarısız olduğunda kaynak özellik güncelleştirmesini de engeller. CommittedValue ile, dize yerine BindingExpression olsun. pek çok soru var burada

şunlardır:

  1. Neden ValidationStep ayarına göre doğrula geçirilen tutarsız bir argüman türü() alabilirim?

  2. Ben BindingExpression gelen gerçek değeri nasıl alabilirim?

  3. Alternatif olarak, kullanıcı önceki (geçerli) durumuna TextBox dönmek için izin vermek için iyi bir yol var? (Ben de belirtildiği gibi, kendi geri alma fonksiyonu değişikliğini hiç görmez.)

+0

Herhangi bir geri bildirim göremedim, özellikle de bu durumdan beri. Bana göre bu, aksi takdirde oldukça mükemmel ve sezgisel görünüyor ValidationRule yaklaşımını kullanarak bir gösterici gibi görünüyor. Karşılaştırmada tıknaz gibi görünse de, IDataErrorInfo kullanmak daha mı iyi? – mbmcavoy

cevap

13

bir küçük kısıtlama ile BindingExpression değeri çıkarma problemini çözdük.

Birincisi, biraz daha komple XAML: ikincisi ValidationStep="UpdatedValue" kullanır, ancak her ikisi de aynı doğrulama kuralını kullanırken ilk TextBox, ValidationStep="RawProposedValue" (varsayılan) kullandığı

<Window x:Class="ValidationRuleTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:ValidationRuleTest" 
     Title="MainWindow" Height="100" Width="525"> 
    <Window.DataContext> 
     <local:MainWindowViewModel/> 
    </Window.DataContext> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="50"/> 
      <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <TextBlock Text="String 1"/> 
     <TextBox Grid.Column="1"> 
      <TextBox.Text> 
       <Binding Path="String1" UpdateSourceTrigger="PropertyChanged"> 
        <Binding.ValidationRules> 
         <local:RequiredRule ValidationStep="RawProposedValue"/> 
        </Binding.ValidationRules> 
       </Binding> 
      </TextBox.Text> 
     </TextBox> 
     <TextBlock Text="String 2" Grid.Row="1"/> 
     <TextBox Grid.Column="1" Grid.Row="1"> 
      <TextBox.Text> 
       <Binding Path="String2" UpdateSourceTrigger="PropertyChanged"> 
        <Binding.ValidationRules> 
         <local:RequiredRule ValidationStep="UpdatedValue"/> 
        </Binding.ValidationRules> 
       </Binding> 
      </TextBox.Text> 
     </TextBox> 
    </Grid> 
</Window> 

Not.

class MainWindowViewModel 
{ 
    public string String1 
    { get; set; } 

    public string String2 
    { get; set; } 
} 

Ve nihayet, yeni RequiredRule:

Basit ViewModel (INPC ve diğer yararlı şeyler ihmal)

class RequiredRule : ValidationRule 
{ 
    public override ValidationResult Validate(object value, 
     System.Globalization.CultureInfo cultureInfo) 
    { 
     // Get and convert the value 
     string stringValue = GetBoundValue(value) as string; 

     // Specific ValidationRule implementation... 
     if (String.IsNullOrWhiteSpace(stringValue)) 
     { 
      return new ValidationResult(false, "Must not be empty"); 
     } 
     else 
     { 
      return new ValidationResult(true, null); 
     } 
    } 

    private object GetBoundValue(object value) 
    { 
     if (value is BindingExpression) 
     { 
      // ValidationStep was UpdatedValue or CommittedValue (Validate after setting) 
      // Need to pull the value out of the BindingExpression. 
      BindingExpression binding = (BindingExpression)value; 

      // Get the bound object and name of the property 
      object dataItem = binding.DataItem; 
      string propertyName = binding.ParentBinding.Path.Path; 

      // Extract the value of the property. 
      object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null); 

      // This is what we want. 
      return propertyValue; 
     } 
     else 
     { 
      // ValidationStep was RawProposedValue or ConvertedProposedValue 
      // The argument is already what we want! 
      return value; 
     } 
    } 
} 

bunu eğer umurumda değerini dışarı kazmak GetBoundValue() yöntem Bir BindingExpression alır veya değilse argümanı geri çevirir. Gerçek anahtar "Yol" u bulmaktı ve sonra mülkiyeti ve değerini elde etmek için bunu kullanıyordu.

Sınırlama: Özgün sorumlumda, ViewModel'imin alt nesnelerini incelerken, benim bağladığımda Path="Identity.Name" vardı. Bu, yukarıdaki kodun doğrudan bağlı nesne üzerindeki bir özelliğe doğru olmasını beklediğinden, çalışmaz. Neyse ki ViewModel'imi zaten düzleştirdim, bu yüzden artık geçerli değil, ancak kontrolün veri içeriğini alt nesne olarak belirlemek için bir çözüm olabilir.

Cevapları ve tartışmalarımı tekrar kazmaya başladığından ve yapıtına bir parça sağladığından Eduardo Brites'e biraz kredi vermek istiyorum. Ayrıca, ValidationRules'i tamamen boşaltıp IDataErrorInfo'yi kullanmak üzereyken, farklı türler ve doğrulama karmaşıklıkları için bunları birlikte kullanma önerisini beğeniyorum.

+1

IDataErrorInfo'nun yeterli olmadığı durumlar var, örneğin bir datagrid'teki benzersiz alanları doğrulamak istediğinizde ve satırları karşılaştırmanız gerekiyor. Aradığın şeyi bulmana sevindim. Şerefe! –

+0

Sınırlamayı kaldırmak için cevabımıma bakın. –

1

Amacıyla senin 2 soruya cevap vermek:

dize strval = (string) ((BindingExpression) değeri). Dataıtem

+0

Bu * neredeyse * çalışır, ancak yapmaz.DataItem, mülkün değerini içermez, bunun yerine DataContext (evet, özellik oradadır). Ne yazık ki, hangi özellik * bu * validasyon kural örneğinin kontrol edilmesi gerektiğini bilmiyorum. BindingExpression'da hangi özelliğin bağlı olduğunu (yani Yol'u) belirlememi sağlayan hiçbir şey göremiyorum. – mbmcavoy

+0

"RequiredRule" amacının genel bir tür olduğunu düşündüm, bu özellik hangi özellikte olduğu kadar önemli olduğunu farketmez ... –

+0

Evet, RequiredRule sınıfı herhangi bir string özelliğini doğrulayabilmelidir. Yukarıdaki XAML durumunda, TextBox Path = "Identity.Name" ile bağlanır. RequiredRule uygulanarak Path = "Identity.Description" ile ilişkili başka bir TextBox'ım var. Geçirilen BindingExpression'ın DataItem'i veri bağlamı olduğundan, hem * Identity.Name hem de Identity.Description (ViewModel'imdeki her şeyin yanı sıra) görebiliyorum. Hata ayıklayıcısında önem verdiğim değeri görebilmem de ValidationRule'ın hangi özelliği doğrulaması gerektiğini belirlemenin bir yolunu göremiyorum. – mbmcavoy

3

Bu, mbmcavoy'un answer ürününün bir uzantısıdır.

Bağlantı yolları sınırlamasını kaldırmak için GetBoundValue yöntemini değiştirdim. BindingExpression, Debugger'da görünür olan ancak normal kod aracılığıyla erişilemeyen ResolvedSource ve ResolvedSourcePropertyName özelliklerine sahiptir. Onları yansıma yoluyla elde etmek hiç sorun olmaz ve bu çözüm herhangi bir bağlayıcı yolla çalışmalıdır.

private object GetBoundValue(object value) 
{ 
    if (value is BindingExpression) 
    { 
     // ValidationStep was UpdatedValue or CommittedValue (validate after setting) 
     // Need to pull the value out of the BindingExpression. 
     BindingExpression binding = (BindingExpression)value; 

     // Get the bound object and name of the property 
     string resolvedPropertyName = binding.GetType().GetProperty("ResolvedSourcePropertyName", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null).ToString(); 
     object resolvedSource = binding.GetType().GetProperty("ResolvedSource", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null); 

     // Extract the value of the property 
     object propertyValue = resolvedSource.GetType().GetProperty(resolvedPropertyName).GetValue(resolvedSource, null); 

     return propertyValue; 
    } 
    else 
    { 
     return value; 
    } 
} 
+1

.NET 4.5 öncesi hedefleme yapan herkes için, .NET 4.5'te ResolvedSource ve ResolvedSourcePropertyName eklenmiştir, bu nedenle çalışmaz. – user2339814

+0

Çok kullanışlıdır. Doğrulanmış denetimin silindiği (bir grid kontrol hücresindeyken), bir kez sonlandırıldığı ve ResolvedSourcePropertyName için null değeri olan bir durum buldum. Bu yüzden, bu boşta ToString'i çağırmaktan kaçınmak için GetValue() öğesinden dönüş değeri üzerinde null bir kontrol eklemem gerekiyordu. –

3

Bu, mbmcavoy'un ve adabyron'un yanıtının alternatif bir uzantısıdır.

public static object GetPropertyValue(object obj, string propertyName) 
{ 
    foreach (String part in propertyName.Split('.')) 
    { 
     if (obj == null) { return null; } 

     Type type = obj.GetType(); 
     PropertyInfo info = type.GetProperty(part); 
     if (info == null) { return null; } 

     obj = info.GetValue(obj, null); 
    } 

    return obj; 
} 

Şimdi sadece

object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null); 

için
object propertyValue = GetPropertyValue(dataItem, propertyName); 

ilgili değiştirin:

yolları bağlanması için sınırlama ortadan kaldırmak için, böyle bir yöntem kullanılarak özellik değerini almak numaralı telefonu gönderin: Get property value from string using reflection in C#

İlgili konular