Değer dönüştürücüler değerli olduklarından daha fazla sorun mu var?


20

Ç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 IValueConverterveya IMultiValueConverterdeğeri veya değerleri döküm, objectdoğ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
    • RaisePropertyChangedtüretilmiş her özellik için çağrılmalıdır (aşağıdaki Güncelleme 2'ye bakın)
    • 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 BoolToVisibilityve aradıklarım MatchToVisibilityve 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 MatchToVisibilityde ilişkili değer bir olup olmadığını denetler olmasıdır enumve bu ise, bu kontroller emin olmak için Value1, Value2vb 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 FullNameeklemek 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.

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.


Sadece bir not: BooleanToVisibilitygö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üyor ValueConverter. Öte yandan, (ne tür öğelerin görünür olması gerekir) MatchToVisibilityiş mantığını kodlamaktadır View. Bence bu mantık benim ViewModeldediğim şeye doğru, hatta daha da ileriye doğru itilmelidir EditModel. Kullanıcının görebileceği test edilen bir şey olmalıdır.
Scott Whitlock

@Scott, bu iyi bir nokta. Şu anda üzerinde çalıştığım uygulama, kullanıcılar için farklı izin düzeylerinin bulunduğu gerçekten bir "iş" uygulaması değil, bu yüzden bu satırlar boyunca düşünmüyordum. MatchToVisibilitybazı 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:Namemod ile eşleşecek şekilde (ile ) etiketlenir Bana bunun "iş mantığı" olduğunu söylememiştim, ama yorumunuza biraz düşünce vereceğim.
devuxer

Örnek: radyo, CD veya MP3 modunda olabilecek bir stereo sisteminiz olduğunu varsayalım. Kullanıcı arayüzünün farklı bölümlerindeki her bir moda karşılık gelen görseller olduğunu varsayın. (1) görünümün hangi grafiklerin hangi moda karşılık geldiğine karar vermesine izin verebilir ve buna göre açıp kapatabilirsiniz, (2) her mod değeri için görünüm modelindeki özellikleri gösterebilirsiniz (örn. IsModeRadio, IsModeCD) veya (3) her grafik elemanı / grup için görünüm modelindeki özellikler (ör. IsRadioLightOn, IsCDButtonGroupOn). (1) benim görüşüme göre doğal bir uyum gibi görünüyordu, çünkü zaten mod farkındalığı var. Bu durumda ne düşünüyorsun?
devuxer

Bu en uzun soru, şimdiye kadar tüm SE'de gördüğüm! :]
trejder

Yanıtlar:


10

ValueConvertersBazı durumlarda kullandım ve mantığı ViewModeldiğerlerine koydum . Benim hissim a tabakanın bir ValueConverterparçası haline gelir View, bu yüzden mantık gerçekten Viewo zaman oraya koymak, aksi takdirde koymak ViewModel.

Şahsen ben es gibi -özel kavramlar ViewModelile ilgili bir sorun görmüyorum çünkü benim uygulamalarda sadece için test edilebilir ve ikili bir yüzey olarak var . Ancak, bazı insanlar (yapmıyorum) bir çok iş mantığı koydu ve bu durumda daha çok iş katmanlarının bir parçası gibi, bu durumda orada WPF'ye özgü şeyler istemezdim.ViewBrushViewModelViewViewModelViewModel

Farklı bir ayrımı tercih ederim:

  • View- WPF şeyler, bazen (XAML ve kod arkasına gibi) test edilemeyen, aynı zamanda ValueConverters
  • ViewModel - WPF'ye özel olan test edilebilir ve çiftlenebilir sınıf
  • EditModel - manipülasyon sırasında modelimi temsil eden iş katmanının bir parçası
  • EntityModel - modelimi kalıcı olarak temsil eden iş katmanının bir parçası
  • Repository- EntityModelveritabanının kalıcılığından sorumludur

Yani, bunu yolu, ben pek ihtiyacım var ValueConverters

"Con'larınızdan" bazılarından uzaklaşma şeklim, benim ViewModelçok genel olmam . Örneğin, ViewModelaradığım biri ChangeValueViewModelLabel özelliği ve Value özelliği uygular. On Viewbir var Labelolduğunu Etiket özelliğine bağlar ve bir TextBoxo Değer özelliğine bağlar.

Sonra tür kapalı ChangeValueViewbir DataTemplateanahtar var ChangeValueViewModel. WPF ViewModelbunu gördüğünde bunu uygular View. Benim yapıcı ChangeValueViewModel, durumunu yenilemek için ihtiyaç duyduğu etkileşim mantığını EditModel(genellikle sadece a'yı geçerek Func<string>) ve kullanıcı Değeri düzenlediğinde gerçekleştirmesi gereken eylemi alır (yalnızca bir Actionmantığı yürüten EditModel).

Üst ViewModel(ekran) bir alır EditModelOluşturucu ve yalnızca uygun temel başlatır ViewModelgibi s ChangeValueViewModel. Ebeveyn ViewModel, kullanıcı herhangi bir değişiklik yaptığında yapılacak eylemi enjekte ettiğinden, tüm bu eylemleri kesebilir ve başka işlemler yapabilir. Bu nedenle, bir için enjekte edilen düzenleme eylemi ChangeValueViewModelşöyle görünebilir:

(string newValue) =>
{
    editModel.SomeField = newValue;
    foreach(var childViewModel in this.childViewModels)
    {
        childViewModel.RefreshStateFromEditModel();
    }
}

Açıkçası foreachdöngü başka bir yerde yeniden düzenlenebilir, ancak bunun yaptığı eylemi yapmak, modele uygulamak, daha sonra (modelin durumunu bilinmeyen bir şekilde güncellediğini varsayarak), tüm çocuklara ViewModelgitmelerini ve durumlarını almalarını söyler . tekrar model. Devlet değiştiyse, PropertyChangedolaylarını gerektiği gibi yürütmekten sorumludurlar .

Bu, bir liste kutusu ve bir ayrıntı paneli arasındaki etkileşimi oldukça güzel bir şekilde ele alır. Kullanıcı yeni bir seçim seçtiğinde, seçeneğini seçim EditModelile günceller EditModelve ayrıntı paneli için gösterilen özelliklerin değerlerini değiştirir. ViewModelAyrıntı paneli bilgilerini görüntülemekle sorumlu olan çocuklara otomatik olarak yeni değerleri kontrol etmeleri gerektiği bildirilir ve değiştiyse PropertyChangedetkinliklerini başlatırlar.


/ NOD. Bu benimkinin görünüşüne oldukça benziyor.
Ian

+1. Cevabınız için teşekkürler Scott, hemen hemen aynı katmanlara sahibim ve aynı zamanda iş mantığını görünüm modeline koymuyorum. (Kayıt için, önce EntityFramework Kodunu kullanıyorum ve görünüm modelleri ile varlık modelleri arasında çeviri yapan bir hizmet katmanım var, ya da tam tersi.) dönüşüm mantığının tamamını / çoğunu görünüm modeli katmanına yerleştirmek.
devuxer

@DanM - Evet, katılıyorum. ViewModelKatmandaki dönüşümü tercih ederim . Herkes benimle aynı fikirde değil, ancak mimarinizin çalışma şekline bağlı.
Scott Whitlock

2
İlk paragrafı okuduktan sonra +1 diyecektim, ama sonra ikinci paragrafınızı okudum ve ViewModels'e görünüme özgü kod koymaya kesinlikle katılmıyorum. Bunun tek istisnası, ViewModel özel bir genel Görünüm ( CalendarViewModelbir CalendarViewUserControl için bir veya a DialogViewModeliçin bir gibi DialogView) arkasına gitmek için oluşturulmuş olmasıdır . Bu sadece benim fikrim olsa :)
Rachel

@Rachel - Eh, ikinci paragrafımı okumaya devam etseydin, tam olarak bunu yaptığımı görürdün. :) Benim iş mantığım yok ViewModel.
Scott Whitlock

8

Dönüştürme, bir nesnenin görünürlüğüne karar vermek, hangi görüntünün görüntüleneceğini belirlemek veya hangi fırça renginin kullanılacağını belirlemek gibi Görünümle İlgili bir şeyse, dönüştürücülerimi her zaman Görünüm'e koydum.

Bir alanın maskelenmesi gerekip gerekmediğini belirlemek gibi İşletmeyle İlgili ise veya bir kullanıcının bir eylem gerçekleştirme izni varsa, dönüşüm benim ViewModel'imde gerçekleşir.

Senin örneklerden bakınca sizin WPF büyük bir parçası eksik düşünüyorum: DataTriggers. Koşullu değerleri belirlemek için dönüştürücüler kullanıyor gibi görünüyorsunuz, ancak dönüştürücüler gerçekten bir veri türünü diğerine dönüştürmek için olmalıdır.

Yukarıdaki örneğinizde

Örnek: radyo, CD veya MP3 modunda olabilecek bir stereo sisteminiz olduğunu varsayalım. Kullanıcı arayüzünün farklı bölümlerindeki her bir moda karşılık gelen görseller olduğunu varsayın. (1) görünümün hangi grafiklerin hangi moda karşılık geldiğine karar vermesine izin verebilir ve buna göre açıp kapatabilirsiniz, (2) her mod değeri için görünüm modelindeki özellikleri gösterebilirsiniz (örneğin, IsModeRadio, IsModeCD) veya (3) her grafik eleman / grup için görünüm modelindeki özellikler (ör. IsRadioLightOn, IsCDButtonGroupOn). (1) benim görüşüme göre doğal bir uyum gibi görünüyordu, çünkü zaten mod farkındalığı var. Bu durumda ne düşünüyorsun?

DataTriggerHangi görüntünün gösterileceğini belirlemek için bir kullanırım , değil Converter. Dönüştürücü, bir veri türünü diğerine dönüştürmek için kullanılırken, bir değere dayalı bazı özellikleri belirlemek için bir tetikleyici kullanılır.

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{StaticResource RadioImage}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Mode}" Value="CD">
            <Setter Property="Source" Value="{StaticResource CDImage}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Mode}" Value="MP3">
            <Setter Property="Source" Value="{StaticResource MP3Image}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Bunun için bir Dönüştürücü kullanmayı düşüneceğim tek zaman, bağlı değer aslında görüntü verilerini içeriyorsa ve bunu UI'nin anlayabileceği bir veri türüne dönüştürmem gerekiyordu. Örneğin, veri kaynağı adlı bir özellik ImageFilePathiçeriyorsa, görüntü dosyası konumunu içeren dizeyi Resmim BitmapImageiçin Kaynak olarak kullanılabilecek bir dönüştürücüye dönüştürmek için bir Dönüştürücü kullanmayı düşünürdüm.

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{Binding ImageFilePath, 
            Converter={StaticResource StringPathToBitmapConverter}}" />
</Style>

Sonuç olarak, bir veri türünü diğerine dönüştüren genel dönüştürücülerle dolu bir kütüphane ad alanı var ve nadiren yeni bir dönüştürücü kodlamak zorundayım. Belirli dönüşümler için dönüştürücüler isteyeceğim durumlar var, ancak bunlar bunları yazmayı asla umursamayacağım kadar seyrek.


+1. Bazı iyi puanlar verin. Daha önce tetikleyicileri kullandım, ancak benim durumumda, görüntü kaynaklarını (bir özellik olan) değiştirmiyorum, tüm Gridöğeleri değiştiriyorum. Ayrıca, görünüm modelimdeki verilere ve yapılandırma dosyasında tanımlanan belirli bir renk paletine dayalı olarak ön plan / arka plan / kontur için set fırçalar gibi şeyler yapmaya çalışıyorum. Bunun bir tetikleyici veya dönüştürücü için mükemmel bir seçim olduğundan emin değilim. Şimdiye kadar çoğu görünüm mantığı görünüm modeline koyarak ile ilgili tek sorun tüm RaisePropertyChanged()aramaları kablolama .
devuxer

@DanM Aslında tüm bu şeyleri DataTriggerGrid'in öğelerini bile değiştirerek yapardım . Genellikle ContentControldinamik içeriğimin olması gereken yere bir yer yerleştiririm ve ContentTemplatebir tetikleyicide takas ederim . İlgileniyorsanız aşağıdaki başlıkta bir örneğim var (başlıklı bölüme ilerleyin Using a DataTrigger) rachel53461.wordpress.com/2011/05/28/…
Rachel

Daha önce veri şablonlarını ve içerik denetimlerini kullandım, ancak her görünüm için her zaman benzersiz bir görünüm modelim olduğundan tetikleyicilere hiç ihtiyacım olmadı. Her neyse, tekniğiniz mükemmel bir anlam ifade ediyor ve oldukça zarif, ama aynı zamanda çok ayrıntılı. MatchToVisibility ile bu kısaltılabilir: <TextBlock Text="I'm a Person" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Person}}"ve<TextBlock Text="I'm a Business" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Business}}"
devuxer

1

Bu, neyi test ettiğinize bağlıdır .

Test yok: intermix ViewModel ile kodu isteğinize göre görüntüleyin (daha sonra istediğiniz zaman yeniden düzenleyebilirsiniz).
ViewModel ve / veya daha düşük sürümlerde testler: dönüştürücüler kullanın.
Model katmanları ve / veya altındaki testler: intermix Kodu ViewModel ile görüntüleme

ViewModel, Görünüm Modeli'ni özetler . Şahsen, Fırçalar, vb için ViewModel kullanın ve dönüştürücüler atlayın. Verilerin " en saf " formda olduğu katman (lar) üzerinde test edin (örn. Model katmanlar ).


2
Test hakkında ilginç noktalar, ama sanırım görünüm modelinde dönüştürücü mantığa sahip olmak görünüm modelinin test edilebilirliğine nasıl zarar veriyor? Kesinlikle gerçek UI kontrollerini görünüm modeline koymayı önermiyorum . Sadece görüntülemek özgü özellikler gibi Visibility, SolidColorBrushve Thickness.
devuxer

@DanM: Önce görüntüleme yaklaşımı kullanıyorsanız sorun olmaz . Ancak, bazıları ViewModel'in bir Görünümü referans aldığı bir ViewModel ilk yaklaşımı kullanır , bu sorunlu olabilir .
Jake Berger

Merhaba Jay, kesinlikle ilk bakış. Görünüm, bağlanması gereken özelliklerin adları dışında görünüm modeli hakkında hiçbir şey bilmez. Takip ettiğiniz için teşekkürler. +1.
devuxer

0

Bu muhtemelen bahsettiğiniz tüm sorunları çözmez, ancak dikkate alınması gereken iki nokta vardır:

İlk olarak, dönüştürücü kodunu ilk stratejinizde bir yere koymanız gerekir. Görüşün bir kısmını mı yoksa görünüm modelini mi düşünüyorsunuz? Görünüşün bir parçasıysa, neden görünüm modeli yerine görünüme özgü özellikleri görünüme yerleştirmiyorsunuz?

İkincisi, dönüştürücü olmayan tasarımınız zaten var olan gerçek nesnelerin özelliklerini değiştirmeye çalışıyor gibi görünüyor. INotifyPropertyChanged'i zaten uyguluyor gibi görünüyor, neden bağlanmak için görünüme özgü bir sarıcı nesnesi oluşturmuyorsunuz? İşte basit bir örnek:

public class RealData
{
    private bool mIsInteresting;
    public bool IsInteresting
    {
        get { return mIsInteresting; }
        set 
        {
            if (mIsInteresting != null) 
            {
                mIsInteresting = value;
                RaisePropertyChanged("IsInteresting");
            }
        }
    }
}

public class RealDataView
{
    private RealData mRealData;

    public RealDataView(RealData data)
    {
        mRealData = data;
        mRealData.PropertyChanged += OnRealDataPropertyChanged;
    }

    public Visibility IsVisiblyInteresting
    {
       get { return mRealData.IsInteresting ? Visibility.Visible : Visibility.Hidden; }
       set { mRealData.IsInteresting = (value == Visibility.Visible); }
    }

    private void OnRealDataPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsInteresting") 
        {
            RaisePropertyChanged(this, "IsVisiblyInteresting");
        }
    }
}

Varlık modelimin özelliklerini doğrudan görünüm veya görünüm modelinde değiştirdiğimi ima etmek istememiştim. Görünüm modeli kesinlikle varlık modeli katmanımdan farklı bir katman. Aslında, şimdiye kadar yaptığım işler salt okunur görüşlerde yapıldı. Bu, uygulamamın herhangi bir düzenleme içermeyeceği anlamına gelmez, ancak düzenleme için kullanılan kontrollerde herhangi bir dönüşüm yapılmadığını görmüyorum (bu nedenle, listelerdeki seçimler hariç tüm bağlantıların tek yönlü olduğunu varsayalım). Gerçi "veri görünümleri" hakkında iyi bir nokta. Bu, sorumun en üstünde bahsettiğim XAML havarilerinde ortaya atılan bir kavramdı.
devuxer

0

Bazen sanallaştırmadan yararlanmak için bir değer dönüştürücü kullanmak iyidir.

Bunun bir örneği, bir ızgarada yüz binlerce hücre için bitmasked verileri göstermek zorunda kaldığımız bir projede. Her bir hücre için görünüm modelindeki bit maskeleri çözdüğümüzde, programın yüklenmesi çok uzun sürdü.

Ancak, tek bir hücrenin kodunu çözen bir değer dönüştürücü oluşturduğumuzda, program zamanın bir kısmında yüklenir ve aynı derecede duyarlıydı, çünkü dönüştürücü yalnızca kullanıcı belirli bir hücreye baktığında çağrılır (ve yalnızca çağrılması gerekir) kullanıcı ızgaradaki görüşünü değiştirdiğinde en fazla otuz kez).

MVVM şikayetinin çözümün nasıl olduğunu bilmiyorum, ancak yükleme süresini% 95 oranında kesti.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.