Ko'p RaisePropertyChangedda C # MVVM javob bermaydigan UI

Men C# ga yangi boshlaganman va yaqinda MVVMLight-dan foydalanishni boshladim. Menda mavjud bo'lgan loyiha va muammoning asosiy xulosasi. Mening dasturim fayl tizimini/katalog tuzilishini o'qiydi va uni TreeView-da qayta yaratadi. Keyinchalik foydalanuvchi qaysi fayllar ishlashini aniqlash uchun CheckBoxni tekshiradi. Agar foydalanuvchi biror katalogni tekshirsa, u katalogning barcha pastki elementlari o'z-o'zidan tekshiriladi.

Fayl daraxti tarkibi

Kataloglar odatda yuzlab fayllarni o'z ichiga oladi, shuning uchun ma'lum bir misolda minglab fayl moslamalari yaratiladi. Tasdiqlash qutilari ikki tomonlama ko'rinishdagi ToBeProcessed xususiyatiga qarashadi. Mana, bu kodning parchasi (TreeViewModelBase dan olingan katalog va fayl moslamalari).

public class TreeViewModelBase : ObservableObject
{
    private bool _toBeProcessed = false;

    public bool ToBeProcessed
    {
        get
        {
            return _toBeProcessed;
        }

        set
        {
            if (_toBeProcessed != value)
            {
                _toBeProcessed = value;
                RaisePropertyChanged(nameof(ToBeProcessed));
            }
        }
    }

    ...

}

Bundan tashqari, mening MainViewModel-da hamma narsani tekshiradi/olib tashlaydigan usul bor. Bu xususiyat CheckAll va tasdiqlash qutisiga boolean bog'liq. "Hammasini tekshirish" katagiga belgi qo'yilganda, barcha bolalarning ToBeProceded xususiyatini CheckAll qiymatiga belgilaydigan buyruq boshlanadi.

public class MainViewModel : ViewModelBase
{

    ...

    private async void ToggleAll()
    {
       //Prevent tree from being modified while the values are being updated.
        TreeIsEnabled = false;//Bound to IsEnabled on TreeView

       //My attempt to update the UI from another thread.
        await Task.Run(() =>
        {
           //Update each child's ViewModel
            foreach (TreeViewModelBase child in GetChildren())
            {
                child.ToBeProcessed = CheckAll;
            }
        });

       //Re-enable the tree when the await is finished.
        TreeIsEnabled = true;
    }

    ...

}

Har bir narsa kutilgandek ishlaydi, lekin asosiy muammo, UI minglab RaisePropertyChanged voqealarini ko'rsatayotganda, mening UI butunlay javob bermaydi. Men bu erda kutilgan Vazifam hech narsa qilmaganiga biroz aminman, chunki UI ishi hali hammasini bajarishi kerak va u erda shishaning bo'ynini yotadi. Men yechim ustida biroz qidirib qoldim, ammo topilgan echimlar men uchun ishlamaydi, yoki men o'z ishimni hal qilish yo'lini topa olmayapman.

Vaqtingiz uchun rahmat!

1

7 javoblar

Minglab UI elementlari juda ko'p. Hech bir foydalanuvchi interfeysi tizimi bu yodda ishlab chiqilmagan, chunki foydalanuvchi oddiy shaklda minglab boshqarish vositalari bilan ishlashga qodir emas.

Men yechim ustida biroz qidirib qoldim, lekin topilgan yechimlar men uchun ishlamayapti, yoki o'z holatlarimni o'zimning hal qilishimga yo'l topa olmayapman.

Siz shunga o'xshash tarzda foydalanasiz: virtualizatsiya . Fikr darhol butun daraxtini qurish o'rniga, siz faqat siz ko'rsatadigan qismlarni yaratishingiz kerak. Foydalanuvchi tugunni kengaytirganda, u papkada faqat fayllarni/pastki papkalarni daraxtga qo'shasiz.

1
qo'shib qo'ydi
Bu mening tashvishim edi. Men shu paytgacha WPFning avtomatik tortiq yukiga tayanmoqdaman. Mening katalog tuzilmasi aslida hozircha aniqlangan, Katalog> Sub Directory> Fayllar. Mening kodni ishlatganimda, daraxtni ko'targandan so'ng, Sub katalog kengaytirilmaydi. Pastki katalog kengaytirilganda deyarli birdan ishlaydi. Muammo shundaki, agar men tugunni kengaytirsam (yoki uni yashirishda va wpf elementlarni yo'q qilmagan bo'lsa), u osadi. Davom etishdan oldin UI elementlarini avtohokinetika va qulflash mumkinmi?
qo'shib qo'ydi muallif SolidSloth, manba

Buning o'rniga harakat qilib ko'ring.

private void ToggleAll()
{
   //Prevent tree from being modified while the values are being updated.
    TreeIsEnabled = false;//Bound to IsEnabled on TreeView

   //My attempt to update the UI from another thread.
    Task task = new Task(() =>
    {
       //Update each child's ViewModel
        foreach (TreeViewModelBase child in GetChildren())
        {
            child.ToBeProcessed = CheckAll;
        }
    });
    task.Start();

   //Re-enable the tree when the await is finished.
    TreeIsEnabled = true;
}
0
qo'shib qo'ydi
@SolidSloth Men sizni hech qachon "Task" ning konstruktoridan hech qachon foydalanmaslikni tavsiya qilaman, bu aslida yanada ko'proq xarajat qiladi. NET 4.5 va yuqorisi uchun Task.Run-ni ishlatish, agar siz haqiqatan ham Taskni sozlashni xohlasangiz, bu holda Task.Factory.StartNew dan foydalaning, lekin hech qachon konstruktordan foydalanmang.
qo'shib qo'ydi muallif Eyal Perry, manba
Bundan tashqari, bu kod kutilganidek ishlamaydi, chunki u ba'zi holatlarda ish tugagunga qadar daraxtni ishga tushirish imkonini beradi.
qo'shib qo'ydi muallif Eyal Perry, manba
Tashakkur, Kevin. Afsuski, bu avvalgi urinishlarimdan biri edi. Darhaqiqat, men turli xil Mavzu/Vazifalar echimlarini sinab ko'rdim.
qo'shib qo'ydi muallif SolidSloth, manba

Buning o'rniga harakat qilib ko'ring.

private void ToggleAll()
{
   //Prevent tree from being modified while the values are being updated.
    TreeIsEnabled = false;//Bound to IsEnabled on TreeView

   //My attempt to update the UI from another thread.
    Task task = new Task(() =>
    {
       //Update each child's ViewModel
        foreach (TreeViewModelBase child in GetChildren())
        {
            child.ToBeProcessed = CheckAll;
        }
    });
    task.Start();

   //Re-enable the tree when the await is finished.
    TreeIsEnabled = true;
}
0
qo'shib qo'ydi
@SolidSloth Men sizni hech qachon "Task" ning konstruktoridan hech qachon foydalanmaslikni tavsiya qilaman, bu aslida yanada ko'proq xarajat qiladi. NET 4.5 va yuqorisi uchun Task.Run-ni ishlatish, agar siz haqiqatan ham Taskni sozlashni xohlasangiz, bu holda Task.Factory.StartNew dan foydalaning, lekin hech qachon konstruktordan foydalanmang.
qo'shib qo'ydi muallif Eyal Perry, manba
Bundan tashqari, bu kod kutilganidek ishlamaydi, chunki u ba'zi holatlarda ish tugagunga qadar daraxtni ishga tushirish imkonini beradi.
qo'shib qo'ydi muallif Eyal Perry, manba
Tashakkur, Kevin. Afsuski, bu avvalgi urinishlarimdan biri edi. Darhaqiqat, men turli xil Mavzu/Vazifalar echimlarini sinab ko'rdim.
qo'shib qo'ydi muallif SolidSloth, manba

Kutib turing, kalit so'zlarni kutib turing:

  • Xotira ajratish
  • UI Sinxronizatsiya tarkibiga qaytib

Har ikkala operatsiya ham dasturning ta'sirchanligini pasaytiradi, chunki ular UI da amalga oshiriladi.

Agar qilayotgan ishlaringiz UI yangilanayotgan bo'lsa va siz bu ma'lumotni chindan ham bajarishingiz shart emas, unda sizning vazifangizni yaratishga hojat yo'q. Bu faqat ishlashni yomonlashtiradi, chunki vazifani yaratish ham xotirani ajratadi.

Siz nima qilsangiz, muammoingizni qanday ko'rib chiqayotganingizni "qayta loyihalash". Barcha bolalarni mantiqiy ravishda yangilash kerak emas. Siz WPFni ingl. Sifatida o'zgartirishingiz mumkin, hatto INPC ni chaqirishga muhtoj emassiz, bu ham Reflection xarajatlarini qoplaydi.

Bu Trigger uchun bir ish kabi ko'rinadi, quyidagi satrlarda bir narsa:

 

... Daraxt elementlari katakchasi:


    
         
                    
                        
                    
         
    

Trigger faqat chegara tekshiruvi holatini o'zgartiradi, bu esa navbatida, IsSelected nomli Descendantning modeli boolean xususiyatini almashtirish imkonini beradi, bu esa o'z navbatida tanlov holatini saqlab qolishdan iboratdir.

Qaysi qutingizdan chiqsangiz ham, barcha belgilash katakchasi belgilanmagan bo'lsa, har bir matn qutisi oldingi holatiga qaytadi. Buni xohlamasangiz, har bir bola bilan bog'lanish va barcha katakchalarni belgilash katakchasini bekor qilishingiz mumkin.

tanlov ma'lumoti zarur bo'lganida, tanlangan elementlar orasidan loop qilish va ularni ishlov berish kerak.

0
qo'shib qo'ydi
Tashakkur, Eyal. Agar ushbu hodisani tetiklashdan ko'ra ko'proq intuitivroq ko'rinadigan bo'lsa, men ushbu echimni juda yaxshi ko'raman. Afsuski, u UI muzqaymoq bilan bog'liq muammoni hal qilmaydi, lekin bu juda ko'p UI yangilanishlarining cheklanishi bo'lishi mumkin deb o'ylayman. Bu men bilan birga yashashim mumkin bo'lgan cheklovdir, bu menga to'liq muzlatilgan UI ega bo'lish uchun xatolik keltiradi. Muammoning echimi, bu yondashuv bilan moddaning modeliga o'tish uchun mening majburiyatimni oladi, garchi UI yangilamasa ham (bu siz aytgan bo'lishi mumkin).
qo'shib qo'ydi muallif SolidSloth, manba

Parallel.ForEach loop bu erda sizga ishlaydi?

https://msdn.microsoft.com/tr -us/kutubxona/dd460720 (v = vs.110) .aspx

Buning o'rniga:

        foreach (TreeViewModelBase child in GetChildren())
        {
            child.ToBeUploaded = CheckAll;
        }
0
qo'shib qo'ydi
Hech qanday imkoniyat bo'lmasa, quyidagi harakatlarni qildim: Parallel.ForEach (GetChildren (), (child) => {child.ToBeUploaded = CheckAll;});
qo'shib qo'ydi muallif SolidSloth, manba

Parallel.ForEach loop bu erda sizga ishlaydi?

https://msdn.microsoft.com/tr -us/kutubxona/dd460720 (v = vs.110) .aspx

Buning o'rniga:

        foreach (TreeViewModelBase child in GetChildren())
        {
            child.ToBeUploaded = CheckAll;
        }
0
qo'shib qo'ydi
Hech qanday imkoniyat bo'lmasa, quyidagi harakatlarni qildim: Parallel.ForEach (GetChildren (), (child) => {child.ToBeUploaded = CheckAll;});
qo'shib qo'ydi muallif SolidSloth, manba

Stiven Cleary tufayli men o'z qarorimga keldim. https://stackoverflow.com/a/42422827/4748204

Faqat TreeView-ga qo'shildim:

VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"

Mening holimda, mening TreeView-ni chegaralashimga ham ega bo'ldim. Chegarasi WPF-ni TreeView virtualizatsiyasini e'tiborsiz qoldirishi va tashqi ko'rinishlarni tashqi ko'rinishdan tashqariga olib chiqmasligi kabi ko'rinardi. Ushbu virtualizatsiya xususiyatlarini qo'shish UI-ni faqat ekrandagi elementlar uchun CheckBox o'zgarishlar qilishiga sabab bo'ldi. CheckBox-larning qolgan qismi talabga qarab paydo bo'ladi. Natija juda yaxshi va oxir-oqibat, mening mavjud kodim juda oz o'zgartirish kerak bo'ldi. ToggleAll usulidan kutishni olib tashladim. Men, ehtimol, TreeIsEnabled o'chirib tashlayman, chunki ishlash juda tez, chunki siz hatto o'chirib qo'yish/faollashtirishni idrok qila olmaysiz.

0
qo'shib qo'ydi