Çok sayıda değer dönüşümü gerektiren görünümlere sahip bir WPF uygulaması üzerinde çalışıyorum. Başlangıçta, felsefem (kısmen XAML Disciples hakkındaki bu canlı tartışmadan esinlenerek ), görünüm modelini kesinlikle görünümün veri gereksinimlerini destekleme konusunda yapmam gerektiğiydi . Bu, verileri görünürlükler, fırçalar, boyutlar vb. Şeylere dönüştürmek için gereken değer dönüşümlerinin değer dönüştürücüler ve çok değerli dönüştürücülerle gerçekleştirileceği anlamına geliyordu. Kavramsal olarak, bu oldukça zarif görünüyordu. Görüş modeli ve görüş ayrı bir amaca sahip olacak ve güzelce ayrılacaktır. "Veri" ve "görünüm" arasında net bir çizgi çizilir.
Bu stratejiyi "eski kolej denemesi" yaptıktan sonra, bu şekilde gelişmeye devam etmek isteyip istemediğim konusunda bazı şüphelerim var. Aslında değer dönüştürücülerini boşaltmayı ve (neredeyse) tüm değer dönüşümünün sorumluluğunu görünüm modelinin ellerine yerleştirmeyi düşünüyorum.
Değer dönüştürücüleri kullanmanın gerçeği, temiz bir şekilde ayrılmış endişelerin görünen değerini ölçüyor gibi görünmüyor. Değer dönüştürücülerle ilgili en büyük sorunum, kullanımlarının sıkıcı olması. Sen uygulamak, yeni bir sınıf oluşturmak zorunda IValueConverter
veya IMultiValueConverter
değeri veya değerleri döküm, object
doğru türe, test DependencyProperty.Unset
(çoklu değer dönüştürücüler için en az), dönüşüm mantığı yazma bir kaynak sözlükte dönüştürücü kayıt [aşağıda güncelleme göreceksiniz ] ve son olarak, dönüştürücüyü oldukça ayrıntılı XAML kullanarak bağlayın (bu hem bağlayıcının (bağların) hem de dönüştürücünün adı için sihirli dizelerin kullanılmasını gerektirir[aşağıdaki güncellemeye bakın]). Hata iletileri genellikle şifreli olduğundan, özellikle Visual Studio'nun tasarım modunda / İfade Karışımı'nda hata ayıklama işlemi de piknik yapmaz.
Bu, görünüm modelini tüm değer dönüşümlerinden sorumlu hale getirmenin alternatifinin bir gelişme olduğu anlamına gelmez. Bu, diğer tarafta çimlerin daha yeşil olması meselesi olabilir. Endişelerin zarif ayrılığını kaybetmenin yanı sıra, bir grup türetilmiş özellik yazmalı ve RaisePropertyChanged(() => DerivedProperty)
hoş olmayan bir bakım sorunu olabileceği kanıtlanan temel özellikleri ayarlarken dikkatli bir şekilde aradığınızdan emin olmalısınız .
Aşağıda, görünüm modellerinin dönüşüm mantığını işlemesine ve değer dönüştürücülerle uğraşmasına izin vermenin artılarını ve eksilerini bir araya getirdiğim ilk liste:
- Artıları:
- Çoklu dönüştürücüler elimine edildiği için daha az toplam bağlanma
- Daha az sihirli dize (bağlama yolları
+ dönüştürücü kaynak adları) Artık her dönüştürücüyü kaydetmenize gerek yok (artı bu listeyi koruyarak)- Her dönüştürücüyü yazmak için daha az çalışma (uygulama arabirimi veya döküm gerekmez)
- Dönüşümlere yardımcı olmak için bağımlılıkları kolayca enjekte edebilir (örn. Renk tabloları)
- XAML işaretlemesi daha az ayrıntılı ve okunması daha kolay
- Dönüştürücünün yeniden kullanımı hala mümkündür (bazı planlama gerekli olsa da)
- DependencyProperty.Unset ile gizemli bir sorun yok (çok değerli dönüştürücülerde fark ettiğim bir sorun)
* Üstü çizikler, biçimlendirme uzantıları kullanırsanız kaybolan yararları gösterir (aşağıdaki güncellemeye bakın)
- Eksileri:
- Görünüm modeli ve görünüm arasında daha güçlü bağlantı (ör. Özellikler görünürlük ve fırçalar gibi kavramlarla ilgilenmelidir)
- Görünümdeki her ciltleme için doğrudan eşlemeye izin veren daha fazla toplam özellik
(aşağıdaki Güncelleme 2'ye bakın)RaisePropertyChanged
türetilmiş her özellik için çağrılmalıdır- Dönüştürme bir kullanıcı arayüzü öğesinin bir özelliğini temel alıyorsa, yine de dönüştürücülere güvenilmelidir
Yani, muhtemelen söyleyebileceğiniz gibi, bu konuda biraz mide ekşimesi var. Ben sadece kodlama sürecinin değer dönüştürücüler kullanın ya da benim görünüm modelimde çok sayıda değer dönüştürme özellikleri açığa vurmak gibi verimsiz ve sıkıcı olduğunu fark etmek için yeniden düzenleme yolda gitmek için çok tereddüt.
Artıları / eksileri var mı? Her iki değer dönüştürme aracını deneyenler için hangisinin sizin için daha iyi çalıştığını buldunuz ve neden? Başka alternatif var mı? (Öğrenciler tip tanımlayıcı sağlayıcılar hakkında bir şeyden bahsettiler, ancak ne hakkında konuştuklarını ele alamadım. Bununla ilgili herhangi bir fikir takdir edilecektir.)
Güncelleştirme
Bugün, değer dönüştürücülerini kaydetme ihtiyacını ortadan kaldırmak için "biçimlendirme uzantısı" adı verilen bir şey kullanmanın mümkün olduğunu öğrendim. Aslında, sadece onları kaydetme ihtiyacını ortadan kaldırmakla kalmaz, aynı zamanda yazarken bir dönüştürücü seçmek için akıllıca sağlar Converter=
. Beni başlatan makale: http://www.wpftutorial.net/ValueConverters.html .
Bir biçimlendirme uzantısı kullanma yeteneği, artılarım ve eksilerim listesindeki ve tartışmamdaki dengeyi bir miktar değiştirir (üstü çizili bilgilere bakın).
Bu vahiy sonucunda, dönüştürücüler için kullandığım BoolToVisibility
ve aradıklarım MatchToVisibility
ve diğer tüm dönüşümler için görünüm modeli olan bir melez sistemle denemeler yapıyorum . MatchToVisibility temel olarak bağlı değerin (genellikle bir numaralandırma) XAML'de belirtilen bir veya daha fazla değerle eşleşip eşleşmediğini kontrol etmeme izin veren bir dönüştürücüdür.
Örnek:
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue=Visible, IfFalse=Hidden, Value1=Finished, Value2=Canceled}}"
Temel olarak bunun ne olduğu, durumun Bitti veya İptal edildi olup olmadığını kontrol etmektir. Öyleyse, görünürlük "Görünür" olarak ayarlanır. Aksi takdirde, "Gizli" olarak ayarlanır. Bu çok yaygın bir senaryo olduğu ortaya çıktı ve bu dönüştürücünün bana görünüm modelimde 15 özellik kaydetti (artı ilişkili RaisePropertyChanged ifadeleri). Not yazarken o Converter={vc:
bir intellisense menüsünde, "MatchToVisibility" gösterileri kadar. Bu, hata olasılığını belirgin şekilde azaltır ve değer dönüştürücülerini kullanmayı daha az sıkıcı hale getirir (istediğiniz değer dönüştürücünün adını hatırlamanız veya aramanıza gerek yoktur).
Merak ediyorsanız, aşağıdaki kodu yapıştıracağım. Bu uygulamanın önemli bir özelliği MatchToVisibility
de ilişkili değer bir olup olmadığını denetler olmasıdır enum
ve bu ise, bu kontroller emin olmak için Value1
, Value2
vb da aynı türden enums bulunmaktadır. Bu, enum değerlerinden herhangi birinin yanlış yazılıp yazılmadığının tasarım zamanı ve çalışma zamanı kontrolü sağlar. Bunu derleme zamanı kontrolüne göre iyileştirmek için, bunun yerine aşağıdakini kullanabilirsiniz (bunu elle yazdım, bu yüzden herhangi bir hata yaptıysam lütfen beni affet):
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue={x:Type {win:Visibility.Visible}},
IfFalse={x:Type {win:Visibility.Hidden}},
Value1={x:Type {enum:Status.Finished}},
Value2={x:Type {enum:Status.Canceled}}"
Bu daha güvenli olsa da, benim için buna değmeyecek kadar ayrıntılı. Bunu yapmam gerekirse görünüm modelinde bir özellik de kullanabilirim. Her neyse, şimdiye kadar denediğim senaryolar için tasarım zamanı kontrolünün mükemmel olduğunu düşünüyorum.
İşte kodu MatchToVisibility
[ValueConversion(typeof(object), typeof(Visibility))]
public class MatchToVisibility : BaseValueConverter
{
[ConstructorArgument("ifTrue")]
public object IfTrue { get; set; }
[ConstructorArgument("ifFalse")]
public object IfFalse { get; set; }
[ConstructorArgument("value1")]
public object Value1 { get; set; }
[ConstructorArgument("value2")]
public object Value2 { get; set; }
[ConstructorArgument("value3")]
public object Value3 { get; set; }
[ConstructorArgument("value4")]
public object Value4 { get; set; }
[ConstructorArgument("value5")]
public object Value5 { get; set; }
public MatchToVisibility() { }
public MatchToVisibility(
object ifTrue, object ifFalse,
object value1, object value2 = null, object value3 = null,
object value4 = null, object value5 = null)
{
IfTrue = ifTrue;
IfFalse = ifFalse;
Value1 = value1;
Value2 = value2;
Value3 = value3;
Value4 = value4;
Value5 = value5;
}
public override object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var ifTrue = IfTrue.ToString().ToEnum<Visibility>();
var ifFalse = IfFalse.ToString().ToEnum<Visibility>();
var values = new[] { Value1, Value2, Value3, Value4, Value5 };
var valueStrings = values.Cast<string>();
bool isMatch;
if (Enum.IsDefined(value.GetType(), value))
{
var valueEnums = valueStrings.Select(vs => vs == null ? null : Enum.Parse(value.GetType(), vs));
isMatch = valueEnums.ToList().Contains(value);
}
else
isMatch = valueStrings.Contains(value.ToString());
return isMatch ? ifTrue : ifFalse;
}
}
İşte kodu BaseValueConverter
// this is how the markup extension capability gets wired up
public abstract class BaseValueConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public abstract object Convert(
object value, Type targetType, object parameter, CultureInfo culture);
public virtual object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
İşte ToEnum uzantısı yöntemi
public static TEnum ToEnum<TEnum>(this string text)
{
return (TEnum)Enum.Parse(typeof(TEnum), text);
}
Güncelleme 2
Bu soruyu yayınladığımdan beri, özellikler ve bağımlı özellikler için NotifyPropertyChanged kodunu enjekte etmek için "IL dokuma" kullanan açık kaynaklı bir projeyle karşılaştım. Bu, Josh Smith'in görüş modeli hakkındaki görüşünü "steroidler üzerinde değer dönüştürücü" olarak uygulamak mutlak bir esinti yapar. Sadece "Otomatik Olarak Uygulanan Özellikler" i kullanabilirsiniz, gerisini dokumacı yapacaktır.
Örnek:
Bu kodu girersem:
public string GivenName { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
... derlenen budur:
string givenNames;
public string GivenNames
{
get { return givenName; }
set
{
if (value != givenName)
{
givenNames = value;
OnPropertyChanged("GivenName");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
Bu, yazmanız, okumanız, geçmeniz vb. İçin gereken kod miktarında büyük bir tasarruftur. Daha da önemlisi, bağımlılıklarınızın ne olduğunu anlamanıza gerek kalmaz. Çağrılara FullName
eklemek için özenle bağımlılık zincirine çıkmak zorunda kalmadan yeni "özellik alır" ekleyebilirsiniz RaisePropertyChanged()
.
Bu açık kaynaklı projeye ne denir? Orijinal sürüm "NotifyPropertyWeaver" olarak adlandırılır, ancak sahibi (Simon Potter) o zamandan beri bir dizi IL dokumacısını barındırmak için "Fody" adlı bir platform oluşturdu. Bu yeni platform altındaki NotifyPropertyWeaver'ın eşdeğeri PropertyChanged.Fody olarak adlandırılır.
- Fody kurulum talimatları: http://code.google.com/p/fody/wiki/SampleUsage (" Fazilet " i "PropertyChanged" ile değiştirin)
- PropertyChanged.Fody proje sitesi: http://code.google.com/p/propertychanged/
NotifyPropertyWeaver (yüklemesi biraz daha kolay, ancak gelecekte hata düzeltmelerinin ötesinde güncellenmesi gerekmeyecek) ile gitmek isterseniz, işte proje sitesi: http://code.google.com/p/ notifypropertyweaver /
Her iki durumda da, bu IL dokumacı çözümleri, steroidler ile değer dönüştürücüleri arasındaki görünüm modeli arasındaki tartışmada hesabı tamamen değiştirir.
MatchToVisibility
bazı basit mod anahtarları etkinleştirmek için uygun bir yol gibi görünüyordu (özellikle açılıp kapatılabilen bir ton parça ile bir görünüm var. Çoğu durumda, görünümün bölümleri bile x:Name
mod ile eşleşecek şekilde (ile ) etiketlenir Bana bunun "iş mantığı" olduğunu söylememiştim, ama yorumunuza biraz düşünce vereceğim.
BooleanToVisibility
görünürlükle ilgili bir değer alır (doğru / yanlış) ve bunu başka bir değere çevirir. Bu, a'nın ideal kullanımı gibi görünüyorValueConverter
. Öte yandan, (ne tür öğelerin görünür olması gerekir)MatchToVisibility
iş mantığını kodlamaktadırView
. Bence bu mantık benimViewModel
dediğim şeye doğru, hatta daha da ileriye doğru itilmelidirEditModel
. Kullanıcının görebileceği test edilen bir şey olmalıdır.