سئوالات و مباحث WPF

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
استاد ، در لینک زیر میگه که کارایی رندرینگ در wpf بهتره .
البته این کنترل را ایجاد کرد . فقط متن را رندر نکرد و تغییر نداد :

 

the_king

مدیرکل انجمن
استاد ، یه سئوال دیگه درباره ی این برنامه تون دارم .
من وقتی پروژه ی wpf اش را اجرا میکنم ، عملکرد کارت گرافیک مجتمع ام تا 80 الی 90 درصد میره . (کارت گرافیک مجزا ندارم . مدلش هم intel HD 4600 و کارت مجتمع پردازنده ی i5 4460 هست) .
تفاوت عملکرد رسم در سیستم من هم طبق اطلاعاتی که خودتون در صفحه دادین ، حدود 10 برابر wpf کندتر هست .
میگم این قضیه ی کندی در سیستم من ، بخاطر ضعف کارت گرافیکم شاید باشه؟ ها؟
قبلا در مورد دلیل تفاوت توضیح دادم، ربطی به توانایی کارت گرافیکی نداشت که ضعف یا قدرت اش رو مطرح کنید.
مثلا سیستمی که به کارت گرافیک GTX 1050 Ti یا بالاتر مجهز باشه ، احتمال داره که سرعت رندرینگش خیلی بهتر بشه . نه؟
قضیه گلوگاه رو که فراموش نکرده اید؟ فرضا در حالت کلی میدونیم که سرعت دویدن انسان به پای ماشین سواری نمیرسه، در مسافت های طولانی ماشین برنده است. اما فرضا هدف اینه که با حداکثر سرعت از خانه این سر خیابان بروید خانه آن طرف خیابان، فاصله فقط چند متر ئه.
طبعا این فاصله رو با پای پیاده سریعتر می دوید تا اینکه بخواهید در ماشین رو باز کنید، سوار ماشین بشوید، روشنش کنید، از پارک دربیاریدش و بروید آنور خیابان پارک اش کنید، ماشین رو خاموش کنید و پیاده شوید. اینجا گلوگاه سرعت ماشین ئه؟ نه. مشکل اینه که دور موتور ماشین کم بوده؟ نه. پس وقتی گلوگاه سرعت ماشین نیست، خیلی فرق می کنه که ماشین پراید باشه یا بوگاتی شیرون؟ نه. قطعا با بوگاتی سریعتر از پراید میشه، اما تاثیرش خیلی کم ئه، چرا؟ چون بخش خیلی ناچیزی از زمان فرایند مربوط به سرعت حرکت ماشین ئه، تاثیر اصلی رو گلوگاه ها دارند. وقتی بیشتر زمان در جای دیگری بجز حرکت ماشین هدر میره، تلاش برای بهبود سرعت حرکت ماشین کمک چندانی نخواهد کرد. مشکل جای دیگری است.
میتونم بپرسم کارت گرافیک شما چه مدلی هست و تفاوت رسم این دو برنامه ی wpf و winform با چه اختلافی در سیستم شما اجرا میشه و همچنین عملکرد gpu در سیستم تون تا چند درصد میره؟
تاثیر چندانی نداره. یک مورد رو قبلا بهتون گفتم، باز هم یادآوری می کنم، اینکه پردازنده (حالا چه پردازنده اصلی یا پردازنده کارت گرافیکی) فلان درصد مشغول میشه نشانگر عملکرد اون نیست، میزان مشغول بودن اونه، برای مقایسه عملکرد ملاک خوبی نیست. فقط نشون میده که فلان درصد از زمان مشغول ئه. ممکنه کدی یک کار رو با 15 درصد توان پردازنده در زمان کمتری انجام بده نسبت به کد دیگری که همون کار رو با 80 درصد توان پردازنده در مدت بیشتری انجام داده. 80 درصد زمان پردازنده رو صرف کرده اما پردازنده کار مفید کمتری انجام داده.
البته قضیه ی جدا بودن دو نخ ui و نخ رندر که ذاتا باعث هماهنگیِ کمتر و کندتر نسبت به تک نخی میشه ، جای خود . ولی میخوام بدونم کارت گرافیک اگه قویتر باشه ، چقدر در رندر این برنامه تاثیر داره .
خیلی کم، شاید یکی دو درصد نامحسوس. وقتی سهم یک بخش در کل یک فرایند ناچیز باشه، مثلا زمان اجرای یک بخش نسبت به زمان کل فرایند، اونوقت بهبود زمان اون بخش هم تاثیر کمتری داره. برای همینه که برای بهبود یک فرایند باید روی بخش هایی تمرکز بشه که سهم بیشتری دارند.
 

the_king

مدیرکل انجمن
این times ای که در برنامه نوشتید ، همون تعدادِ دفعاتِ رسم هست دیگه . درسته؟
بله.
یعنی هر چقدر بیشتر باشه ، تعداد رسم اش هم بیشتره دیگه . درسته؟
بله.
یعنی هر چی بیشتر باشه ، رسمِ بیشتر و سریعتری داره دیگه . درسته؟
بله.
الان واسه ی من که ۴۴۶۰ هه ، winform ام 49000 time بود . اما 6700hq که هم در عملکرد تک و هم چند هسته ای بهتر از پردازنده ام هست ، 87000 time که حدودا ۵ برابر کندتر بود.
چرا؟!!
چون سیستم کندتری بوده، حالا به هر دلیلی، چه عدم تناسب سخت افزار ها و چه تنظیمات نرم افزاری و یا هر مورد دیگری.
کارت گرافیک gtx 950m ddr5 هم فقط اندکی از کارت گرافیک مجتمع ام بهتر عمل کرد .

چرا تفاوت پردازنده ی ۴۴۶۰ و ۶۷۰۰hq این قدر و اون هم ۶۷۰۰hq ، در این برنامه ، این قدر ضعیف تره؟
هزاران کاربر یک لپ تاپ مدل خاص با مشخصات سخت افزاری مشابه رو با نرم افزار های benchmarking تست می کنند و نتیجه اش رو آپلود می کنند، الزاما دو تاشون مثل هم نمیشه. همونطور که ممکنه یک لپ تاپ الان که تازه ویندوز نصب کرده اید یک نتیجه benchmarking داشته باشه، دو سال دیگه که سنگین و کند شد یک نتیجه دیگه. بماند که سایتی مثل cpuboss.com در مورد عملکرد تک هسته ای نظر متفاوتی داره.

در صورتی که در برنامه های دیگه که خودم تست کردم (چه تک یا چند هسته ای) و همچنین در بنچمارک های رسمی ، هم در عملکرد تک و چند هسته ای ، 6700hq به مراتب سریعتر از ۴۴۶۰ هه مخصوصا در عملکرد چند هسته ای .

بی زحمت ، درباره ی اجرای این برنامه در سیستم تون میگید که wpf و winform هر کدوم چند times میدن؟
به نظر من مقایسه بیخودی ئه، اما اگر تمایل داشته باشید می توانید در انجمن نیازمندی های مجید آنلاین یا سایر سایت ها کد پروژه و فایل اجرایی اش رو آپلود کنید تا کاربران در صورت تمایل مشارکت کنند و نتیجه تست شون رو اعلام کنند، من شرکت نخواهم کرد. اما از این آمار چه نتیجه ای میشه گرفت جز اینکه سیستم اصغر آقا در این تست سریعتر از سیستم حسن آقا است؟ نتیجه برجسته دیگری حاصل نمیشه. مجموعه ای از مشخصات نرم افزاری و سخت افزاری دست به دست هم دادند تا اجرای فلان نرم افزار روی سیستم بهمان کارکرد خاص خودش رو داشته باشه، منحصر بفرد ئه، شرایط فعلی اون سیستم ئه. مورد کلی و قابلیت تعمیمی ازش درنمیاد که بخواهید نتیجه بگیرید که پس در حالت کلی فلان میشه، مثلا فلان برابر میشه، از آمار همچین چیزی در نمیاد.
اینترنت پر از گزارش رتبه های benchmarking ئه، مورد جدیدی رو که نمی خواهید در کارکرد پردازنده ها یا کارت گرافیکی ها کشف کنید.
 

the_king

مدیرکل انجمن
استاد ، در لینک زیر میگه که کارایی رندرینگ در wpf بهتره .
البته این کنترل را ایجاد کرد . فقط متن را رندر نکرد و تغییر نداد :

حکم صادر نکرده که کارایی Rendering در WPF همیشه بهتره، یک نمونه برای مقایسه ایجاد کرده و در اون مقایسه خاص Rendering در WPF کارایی بهتری داره. میتونه در بیست مقایسه همینطور باشه، در دو مقایسه خلاف این. مطلب جدید یا غیر منتظره ای که نیست.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد . :rose:
مدتی به دلایل شخصی ، از کامپیوتر کلا فاصله داشتم . به همون دلایل هم فعلا شرایطم جوری هه که (ادامه ی ساخت) پروژه را نسبت به آموزش بیشتر میتونم پیگیری کنم و بصورت موقت ، آموزش را بعدا پیگیری میکنم .

استاد ، من این mvvm را صحیح و درست یاد بگیرم ، یه گام خوبی را برمیدارم .
بی زحمت ، لینکِ یک یا دو یا نهایتا سه مقاله ای که از 0 تا 100 برای mvvm باشه را میدین؟
البته شاید 50 درصدش را بلد باشم ولی از صفر باشه ، نکاتی که شاید بگه و ندونم ، خیلی بهتره .
من لینک مقالات mvvm را خیلی زیاد دارم . یعنی حداقل 10 مقاله ی مفصل و بسیار زیاد . اما نمیدونم کدوم شون قشنگ و از پایه یاد دادن .

مثلا لینک های زیر ، نمیدونم کدوم شون بهتر توضیح میده :


و


و


و


و


و


و



------------------------------------------------


بعد اینکه مقاله ی زیر را خوندم :


هر چند نکاتی گیرم اومد اما مقاله ی جونداری نبود (هر چند ساده توضیح داد) و جوری نبود که بیاد از مبنا و کامل بشکافه .
مخصوصا من قضیه ی command ها را متوجه نشدم .

چرا وقتی که میشه از رویدادها استفاده کرد ، کاری اضافی تر مبنی بر پیاده سازیِ اینترفیسِ ICommand را انجام بدیم؟
و در لینک بالا ، وقتی اینترفیس را پیاده سازی کرد :

C#:
public class ChangeTextCommand : ICommand
{
    public MainWindowViewModel mwv;
    private int i = 0;
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter)
    {
        i++;
        mwv.MyString = "Hey, you Clicked me " + i.ToString();
    }
}

و بعد هم استفاده کرد :

C#:
        private ICommand changeText;
        public ICommand ChangeText
        {
            get
            {
                return changeText;
            }
            set
            {
                changeText = value;
            }
        }
 
        public MainWindowViewModel()
        {
            changeText = new ChangeTextCommand { mwv = this};
        }

و همچنین در دکمه :

XML:
<Button
    Command="{Binding ChangeText}"
    Content="Change String"
    />

در کدهای بالا ، کدی نیست که ما متوجه بشیم که متدِ Execute ، در صورتی که رویداد کلیک (برای دکمه در کد بالا) اتفاق میافته ، اجرا میشه ؟
از کجا از کدهای بالا باید متوجه شد که متدِ Execute ، زمانی اجرا میشه که رویداد کلیک اتفاق افتاد؟
آیا کلا Command ها ، زمان کلیک روی دکمه ها اجرا میشن یا اینکه میتونیم خودمون مشخص کنیم که زمانی که فلان رویداد اجرا شد ، کدِ درونِ متدِ Execute مون اجرا بشه؟
اگه ما میتونیم تعیین کنیم ، چجوری میشه؟

مثلا مقاله ای که در این باره (درباره ی Command ها و دلایل استفاده از اونها بجای رویدادها) هم توضیح هات توش باشه ، میدین؟
تشکر استاد .
 

the_king

مدیرکل انجمن
استاد ، من این mvvm را صحیح و درست یاد بگیرم ، یه گام خوبی را برمیدارم .
بی زحمت ، لینکِ یک یا دو یا نهایتا سه مقاله ای که از 0 تا 100 برای mvvm باشه را میدین؟
مقاله 0 تا 100 چیزی رو توضیح نمیده، اونم تو علمی که دائم در حال تحول ئه. با تمرین و کد نویسی های مداوم یاد می گیرید نه خوندن دو سه تا مقاله.
چرا وقتی که میشه از رویدادها استفاده کرد ، کاری اضافی تر مبنی بر پیاده سازیِ اینترفیسِ ICommand را انجام بدیم؟
کدی که فکر می کنید میشه با رویداد نوشت رو بنویسید و با الگوی MVVM مقایسه کنید.
و در لینک بالا ، وقتی اینترفیس را پیاده سازی کرد :

C#:
public class ChangeTextCommand : ICommand
{
    public MainWindowViewModel mwv;
    private int i = 0;
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter)
    {
        i++;
        mwv.MyString = "Hey, you Clicked me " + i.ToString();
    }
}

و بعد هم استفاده کرد :

C#:
        private ICommand changeText;
        public ICommand ChangeText
        {
            get
            {
                return changeText;
            }
            set
            {
                changeText = value;
            }
        }

        public MainWindowViewModel()
        {
            changeText = new ChangeTextCommand { mwv = this};
        }

و همچنین در دکمه :

XML:
<Button
    Command="{Binding ChangeText}"
    Content="Change String"
    />

در کدهای بالا ، کدی نیست که ما متوجه بشیم که متدِ Execute ، در صورتی که رویداد کلیک (برای دکمه در کد بالا) اتفاق میافته ، اجرا میشه ؟
در ICommand ها اجرا شدن وظیفه متد Execute ئه، پس در اینکه چرا Execute هست و چرا MyMethod نیست ابهامی نداریم.
اینکه در ICommand ها Execute فراخوانی بشه که بدیهی ئه، پس صرفا ابهام اینه که چرا Execute ای که در ChangeTextCommand هست فراخوانی میشه، چرا ICommand دیگری انتخاب نمیشه یا به چه طریقی بین دکمه و ChangeTextCommand ارتباط برقرار میشه؟ اونم دلیلش اون Binding ئه. وقتی روی دکمه کلیک بشه Command ای اجرا خواهد شد که منبعش ChangeText ئه، حالا هر مقداری که داره، چون ممکنه در حین اجرا مقدارش رو تغییر بدهید. مقدار اولیه اش هم برای مواقعی که تغییر نکنه چیه؟ اون new ChangeTextCommand { mwv = this} ئه.
برای همینه که وقتی روی دکمه کلیک شد Execute داخل ChangeTextCommand اجرا میشه.
از کجا از کدهای بالا باید متوجه شد که متدِ Execute ، زمانی اجرا میشه که رویداد کلیک اتفاق افتاد؟
هر زمان روی دکمه کلیک شد یک ICommand ای هدف گرفته شده که Execute داخل اون ICommand اجرا میشه، فقط باید ببینید اون ICommand کدوم شیء ئه. یعنی Command به کدوم شیء هدایت شده.
آیا کلا Command ها ، زمان کلیک روی دکمه ها اجرا میشن یا اینکه میتونیم خودمون مشخص کنیم که زمانی که فلان رویداد اجرا شد ، کدِ درونِ متدِ Execute مون اجرا بشه؟
مختص کلیک روی دکمه نیست. در خود توضیحات Commanding هم مثال های دیگری می بینید که حتی ربطی به کلیک ماوس ندارند.
در اغلب کنترل ها یک یا چند مورد مثل OnClick هست که داخلش نوشته اگر Command مقداری داره و CanExecute اش اجازه میده، Execute رو اجرا کنه. اما اگر خودتون هم کنترلی طراحی کرده اید می توانید بر اساس شرایط دلخواه خودتون همچین روالی رو اجرا کنید، یعنی در رخداد های مورد نظرتون ابتدا null نبودن مقدار Command و سپس CanExecute اش رو بررسی کنید که اگر true بود Execute رو فراخوانی کنید. کد مناسب اینکار در داخل کنترل های WPF هست.
مثلا مقاله ای که در این باره (درباره ی Command ها و دلایل استفاده از اونها بجای رویدادها) هم توضیح هات توش باشه ، میدین؟
اشکال کار تون اینجا است که کد نمی نویسید، اینها مفاهیمی است که باید با کد نویسی جا بیافتند، وقتی کد ننویسید توضیحات بقیه بدردتون نمیخوره.
شما الان کدوم کد رو با رویداد نوشته اید که مبنای این مقایسه با Command قرار می دهید؟ هیچ کدی، مقایسه تون رو هوا است.
کدش رو بنویسید تا مساله برایتان روشن بشه. چطوری میشه نتیجه گرفت که کدی که با رویداد نوشته اید اصول MVVM رو رعایت می کنه یا نه؟ وقتی که کدش رو نوشته باشید تا وابستگی هاش رو بررسی کنید، تا وقتی ننوشته اید چیزی برای بررسی وجود نداره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
خیلی ممنون استاد .

اول (برای خودم به عنوان اینکه بعدا مورد یادآوری قرار بگیره) بگم که دو مقاله ی زیر (لینک اولی را در پست قبلی هم دادم) ، هر چند برای مبتدی ها نوشته شده اما وقتی هر دو مقاله با هم خونده شه ، نکات بسیار خوبی از mvvm توش گیر آدم میاد :


و


------------------------------------------

حالا ، در لینک دومی ، اون جور که متوجه شدم (و اگه اشتباه نکنم) ، میخواد بگه (بصورت علنی و صریح این رو نگفت اما عبارتی که من از منظورش برداشت کردم را دارم میگم) که از Command بجای رویدادها استفاده کنیم (این تیکه را صریح نگفت) .
چون Command را بصورت Binding استفاده میکنیم و Binding هم چون بصورت پویا و دینامیک هست (باز هم تا اینجا را صریح نگفت) ، اگه اروری هم بده (و پروپرتیِ ای که بهش Binding میکنیم ، وجود نداشته باشه) ، اروری پرتاب نمیکنه و باعث نمیشه که برنامه مون اجرا نشه . صرفا زمان اجرا ، هشدار میده که فلان پروپرتی وجود نداره :

کد:
We haven't added that command to the ViewModel yet - so that Binding isn't going to work, but there are no errors. If you spin it up with an f5 you will find it runs OK. This is because Bindings fail gracefully. A fact that can be useful sometimes but can easily catch out the unwary.

و همچنین :

کد:
It's looking at MainWindowViewModel for a public property which is an ICommand called AddTextCommand and not finding it. Unlike if you connected up an event handler in markup the binding fails gracefully and the app just carries on going.

من این مقاله را دیدم ، ازش این نتیجه را گرفتم که در بالا گفتم . یعنی بجای رویدادها ، از Command ها استفاده کنم مخصوصا برای پیاده سازی MVVM چون Command ها را Binding میکنیم و Binding هم چون پویا هستند ، جابجایی و انتقال کد را که یکی از اهداف مهم در MVVM هست را خیلی راحت تر میکنه .

اما وقتی مقاله ی زیر را فعلا تا نصفه خوندم (که لینک داده بودید) :


دیدم که هدف Command ها اصلا جایگزینی با رویدادها نیست و اصلا دو چیز کاملا متفاوتی هستند :
- اولا Command ها یک مکانیزم ورودی هستند و اصلا رویداد و یا کاری شبیه به رویداد برای جایگزین شدنِ رویداد ، انجام نمیدن پس نمیشه بجای رویداد ازشون استفاده کرد .

- دوما ، Command Sources یا اشیاء یا المنت هایی که Command ها را اجرا میکنن (و پروپرتیِ Command و ... دارند تا بتونیم command هامون را بهشون Binding کنیم) ، اون المنت هایی هستند که اینترفیسِ ICommandSource را پیاده سازی میکنن . المنت هایی که این اینترفیس را پیاده سازی کردن ، بسیار محدود هستند و چند تا از معروف ترین هاش ، المنت های ButtonBase و MenuItem و Hyperlink (و ریبون ها و چند تای دیگه) هستن .
یعنی عملا برای اِعمال Command ها ، محدودیت روی المنت های خاصی داریم (نه همه ی المنت ها) .

- سوما ، روی همین المنت ها ، فقط روی یک رویدادشون Command ها اجرا میشن . یعنی المنت های ButtonBase و MenuItem و Hyperlink ، فقط وقتی که کلیک بشن ، Command هایی که بهشون Binding کردیم را اجرا میکنن .
یعنی این طور نیست که روی همین المنت های محدود هم بتونیم طوری تعیین کنیم که اگه فلان رویدادش اتفاق افتاد ، بیاد Command ای که تعیین میکنیم را اجرا کنه :

کد:
ButtonBase, MenuItem, and Hyperlink invoke a command when they are clicked, and an InputBinding invokes a command when the InputGesture associated with it is performed.

خوب اینجا سئوال مهم و اساسی این پیش میاد که Command ها که جوری طراحی شدن که میشه اینها را Binding کرد و این ، بزرگ ترین مزیت براشون هست که جداسازیِ کدهایی که در متد Execute اش را مینویسیم ، از view را رقم بزنه ، خوب چرا جوری طراحی نکردن که این محدودیت ها را نداشته باشه؟
یعنی اولا محدود به چند المنت خاص و محدود نباشه و دوما مثلا برای اون المنت های ButtonBase و MenuItem و Hyperlink ، فقط برای رویداد کلیک شون نباشه و ما بتونیم تعیین کنیم که برای کدوم رویداد باشه؟

چون وقتی این محدودیت ها باشه ، پس Command ها که جایگزینِ رویدادها نمیشن . رویدادها را هم اگه استفاده کنیم ، چون handler ئه رویداد را به المنت ای متصل میکنیم ، انتقال کد یا آپدیتِ view (و حذف و اضافه کردنِ المنت جدید) را با مشکلاتِ بیشتری مواجه میکنه که باید وقت بیشتری برای درست کردن شون صرف کرد و کلا باعث میشه با استفاده از رویدادها ، هدف mvvm که جداسازی view از منطق تجاری هست ، کمرنگ تر بشه .


یه سئوال دیگه اینکه با این همه محدودیت ای که Command ها دارن ، پس عملا در mvvm ، کاربرد بسیار محدودی دارن که و حتی اگه استفاده هم کنیم ، باز فایده ی چندانی نداره که . درست میگم؟
چون فرض کنید در پروژه ای 100 تا المنت مختلف دارید و 500 تا از رویدادهاشون که مختلف هم هستند ، کد دارند . حالا فرضا 20 تا از این المنت ها ، button باشن . 20 تا هم رویداد کلیک برای دکمه داشته باشید که بجای رویداد ، از Command براشون استفاده کرده باشید .
تکلیف بقیه ی رویدادهای دکمه هایی که استفاده کردید چی میشه؟
همچنین تکلیف بقیه ی المنت ها و رویدادهاشون (فرضا المنت combobox و grid و ...) که Command نمیتونن انجام بدن چی میشه؟
شما وقتی 500 تا رویداد دارین ، فقط 20 تاش را از Command استفاده کنید و 480 تای دیگه را از رویداد استفاده کنید ، (حتی اگه بر عکس باشه و فرضا 480 تا از Command و 20 تا را از رویداد استفاده کنید) ، در هر صورت ، باز view و المنت ها را به رویداد متصل کردید که باعث میشه جداسازی (منطق تجاری از view) ، پویا نباشه .
یعنی میخوام بگم فرضا حتی یه دونه از 500 تا را هم از رویداد استفاده کنیم ، باز باعث میشه کار خراب بشه . پس در این صورت فرقی نداره که دیگه از Command ها استفاده کنیم .
درست میگم؟

----------------------------------------------

استاد ، من هنوز 2 چیز را دقیق در mvvm متوجه نشدم (البته در حال خوندن مقاله ها و پیگیری کردن هستم چون اگه بگید جستجو نمیکنم) .
اینکه در پست 670 که پروژه ی mvvm داده بودم که از درون کلاس ViewModel ، پروپرتی ای از نوع Model (که نام و نوعش Student بود) در نظر گرفت . یکی از سئوال هام اون موقع این بود که آیا میشه از view ، به اعضایِ پروپرتیِ Student که در ViewModel هست ، دسترسی داشته باشیم (که mvvm را هم نقض نکرده باشه) ؟
اما من یه چیزی شبیه این رو در یه مثالی دیدم که اگه اشتباه نکنم ، یه همچین کاری را با Binding کردن انجام داد . لینک زیر :


اون قسمت از کد که :

XML:
                <GridView> 
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" /> 
 
                    <GridViewColumn Header="Address" Width="200" DisplayMemberBinding="{Binding Address}"/> 
                </GridView>

یا یه کم بالاتر اش ، که در کنترل Button ، مقدار Path را بصورت Path=SelectedItem.Address نوشت (البته برای ListView ، پروپرتیِ SelectedItem نداریم بلکه SelectedItems داریم که نمیدونم حالا اشتباه تایپی داشت یا نه) .
اگه اشتباه نکنم ، Binding کردن ، چون پویا هست ، مشکلی نداشته باشه . دقیق نمیدونم .
حالا باید بیشتر مقالات را ببینم .

---------------------------------------

سئوال بعدی هم که هنوز حل نشده اینه که وقتی در لایه ی Model و ViewModel ، اینترفیس INotifyPropertyChanged را پیاده سازی میکنن (فرض کنید این پیاده سازی در کلاس مربوط به Model انجام شد) ، خوب در کلاس مربوط به ViewModel ، از رویداد PropertyChanged که درون Model پیاده سازی شد ، باید اتصال انجام داده بشه و رویداد PropertyChanged را به handler ای متصل کنیم دیگه . درسته؟
اگه درسته ، من هیچ جا ندیدم که مثلا در لایه ی ViewModel یا View ، این رویداد را که در لایه ی ViewModel یا Model پیاده سازی کردن ، بخوان به handler ای متصل کنن .
وقتی اتصال به رویداد انجام نشه ، این که متدی را اجرا نمیکنه تا بهشون پیامی بده . پس چجوری کار میکنه؟!!

ببخشید استاد که بسیار طولانی شد .
تشکر :rose:
 

the_king

مدیرکل انجمن
من این مقاله را دیدم ، ازش این نتیجه را گرفتم که در بالا گفتم . یعنی بجای رویدادها ، از Command ها استفاده کنم مخصوصا برای پیاده سازی MVVM چون Command ها را Binding میکنیم و Binding هم چون پویا هستند ، جابجایی و انتقال کد را که یکی از اهداف مهم در MVVM هست را خیلی راحت تر میکنه .
پویا هستند ولی بخاطر پویا بودن شون نیست.
دیدم که هدف Command ها اصلا جایگزینی با رویدادها نیست و اصلا دو چیز کاملا متفاوتی هستند :
بله.
- اولا Command ها یک مکانیزم ورودی هستند و اصلا رویداد و یا کاری شبیه به رویداد برای جایگزین شدنِ رویداد ، انجام نمیدن پس نمیشه بجای رویداد ازشون استفاده کرد .
بله.
- دوما ، Command Sources یا اشیاء یا المنت هایی که Command ها را اجرا میکنن (و پروپرتیِ Command و ... دارند تا بتونیم command هامون را بهشون Binding کنیم) ، اون المنت هایی هستند که اینترفیسِ ICommandSource را پیاده سازی میکنن . المنت هایی که این اینترفیس را پیاده سازی کردن ، بسیار محدود هستند و چند تا از معروف ترین هاش ، المنت های ButtonBase و MenuItem و Hyperlink (و ریبون ها و چند تای دیگه) هستن .
یعنی عملا برای اِعمال Command ها ، محدودیت روی المنت های خاصی داریم (نه همه ی المنت ها) .
اینکه فلان کنترل ها مشخصه Command دارند به این معنی نیست که در جاهای دیگر نمیشه Command بکار برد.
- سوما ، روی همین المنت ها ، فقط روی یک رویدادشون Command ها اجرا میشن . یعنی المنت های ButtonBase و MenuItem و Hyperlink ، فقط وقتی که کلیک بشن ، Command هایی که بهشون Binding کردیم را اجرا میکنن .
بله.
یعنی این طور نیست که روی همین المنت های محدود هم بتونیم طوری تعیین کنیم که اگه فلان رویدادش اتفاق افتاد ، بیاد Command ای که تعیین میکنیم را اجرا کنه :

کد:
ButtonBase, MenuItem, and Hyperlink invoke a command when they are clicked, and an InputBinding invokes a command when the InputGesture associated with it is performed.

خوب اینجا سئوال مهم و اساسی این پیش میاد که Command ها که جوری طراحی شدن که میشه اینها را Binding کرد و این ، بزرگ ترین مزیت براشون هست که جداسازیِ کدهایی که در متد Execute اش را مینویسیم ، از view را رقم بزنه ، خوب چرا جوری طراحی نکردن که این محدودیت ها را نداشته باشه؟
فرمان و رخداد دو چیز متفاوت ئه، جای همدیگه رو نمی گیرند.
این محدودیت در Command که نیست، در Command هیچ روالی نیست که بگه فقط با المنت های فلان و بهمان کار می کنم یا فقط روی رخداد هایی با نام فلان و بهمان، این کنترل های طراحی شده در WPF هستند که معمولا صرفا برای عمل اصلی شون Command در نظر گرفته اند که به سادگی برای اون عمل اصلی فرمان تعریف بشه، معنی اش نه اینکه که جایگزین رویداد اصلی شده و نه معنی اش اینه که دیگه نمیشه برای سایر رویداد ها فرمان اجرا کرد. فقط معنی اش اینه که برای انجام یک کاری با منطق مشخصی Command ای تعریف شده که انجامش رو ساده کنه. حالا ممکنه برنامه نویس در رویداد های دیگری Command های دیگری رو اجرا کنه یا در کلاس وارث مشخصه های Command جدیدی اضافه کنه و ...
کنترل ها قراره بر اساس عملکرد کاربر تعاملی با ViewModel داشته باشند تا فرضا موقع کلیک روی دکمه فلان عملکرد برای بروز رسانی داده ها انجام بشه. این عملکرد کاربر هم چیزی نیست که برای هر رخدادی مرسوم باشه، مثلا موقع تغییر ابعاد دکمه یا پنجره عموما تعاملی با ViewModel پیش نمیاد، چه ارتباطی بین داده هایی مثل نام کاربری و فرضا Hover یا تغییر رنگ المنت هست؟ منطقی نیست که برای همه رخداد ها Command نوشته بشه و در ضمن محدودیت از جانب Command نیست، طراح اون کنترل منطقی ندونسته که Command رو برای همه رخداد ها در نظر بگیره.
 

the_king

مدیرکل انجمن
یعنی اولا محدود به چند المنت خاص و محدود نباشه و دوما مثلا برای اون المنت های ButtonBase و MenuItem و Hyperlink ، فقط برای رویداد کلیک شون نباشه و ما بتونیم تعیین کنیم که برای کدوم رویداد باشه؟
قبلا هم گفتم. شما در کدتون هر جا که لازم میدونید هر Command ای که میخواهید بکار می برید و Execute می کنید، چه مانعی برای فراخوانی Command برای رخداد دلخواه شما هست؟ هیچ مانعی.
چون وقتی این محدودیت ها باشه ، پس Command ها که جایگزینِ رویدادها نمیشن . رویدادها را هم اگه استفاده کنیم ، چون handler ئه رویداد را به المنت ای متصل میکنیم ، انتقال کد یا آپدیتِ view (و حذف و اضافه کردنِ المنت جدید) را با مشکلاتِ بیشتری مواجه میکنه که باید وقت بیشتری برای درست کردن شون صرف کرد و کلا باعث میشه با استفاده از رویدادها ، هدف mvvm که جداسازی view از منطق تجاری هست ، کمرنگ تر بشه .
در MVVM نمی توانید در View کدی بنویسید که به Model وابسته باشه، اگر چنین موردی پیش بیاد دیگه MVVM نیست.
در کلاس View هم هر متد متصل به رویدادی داشته باشید، باید کدی برایش بنویسید که در زمان کامپایل کاری با Model نداشته باشه.
چون در زمان کامپایل View باید طرف ارتباط مشخص باشه تا کد کامپایل بشه، برای همین نمیدونم در مورد چی صحبت می کنید، اما فکر کنم هنوز یک نمونه کد هم بر اساس این رویداد ها که میگید ننوشته اید. و مشکل همینه، روی چیزی فرض تون رو بنا می کنید که شدنی بودنش رو بررسی نکرده اید.
یه سئوال دیگه اینکه با این همه محدودیت ای که Command ها دارن ، پس عملا در mvvm ، کاربرد بسیار محدودی دارن که و حتی اگه استفاده هم کنیم ، باز فایده ی چندانی نداره که . درست میگم؟
کدوم محدودیت؟ محدودیتی به این شکل که توصیف می کنید نیست.
چون فرض کنید در پروژه ای 100 تا المنت مختلف دارید و 500 تا از رویدادهاشون که مختلف هم هستند ، کد دارند . حالا فرضا 20 تا از این المنت ها ، button باشن . 20 تا هم رویداد کلیک برای دکمه داشته باشید که بجای رویداد ، از Command براشون استفاده کرده باشید .
تکلیف بقیه ی رویدادهای دکمه هایی که استفاده کردید چی میشه؟
فرضا یک دکمه دارید که وقتی ماوس رویش رفت رنگش عوض میشه یا انیمیشنی اجرا می کنه یا وقتی ابعاد المنتی تغییر کرد متناسب با اون کادر یک المنت دیگری تغییر ابعاد میده، این جور رخداد ها ربطی به View دارند؟ نه. برای همین نیازی به Command ای ندارند که بخواد کاری در مورد ViewModel انجام بده.
همچنین تکلیف بقیه ی المنت ها و رویدادهاشون (فرضا المنت combobox و grid و ...) که Command نمیتونن انجام بدن چی میشه؟
اولا Command نمی توانند انجام بدهند بی معنیه، اون Command صرفا یک مشخصه است که در رخداد خاصی اجرا میشه، می توانید اینکار رو هر جا که لازم می دانید انجام بدهید. شما در هر کدی می توانید یک شیء Command رو بکار ببرید، چه کد مربوط به رخداد خاصی باشه و چه نباشه. ثانیا در اغلب موارد KeyBinding و MouseBinding کفایت می کنه، مثال هاشون رو جستجو کنید. ثالثا اغلب رخداد هایی که منجر به تغییر حالت کنترل می شوند مقدار مشخصه ای رو تغییر می دهند که امکان Bind کردن شون به مشخصه ای در ViewModel هست، بنابر این نیازی به ارجاع رخداد به ViewModel نیست که Command ای لازم باشه.
شما وقتی 500 تا رویداد دارین ، فقط 20 تاش را از Command استفاده کنید و 480 تای دیگه را از رویداد استفاده کنید ، (حتی اگه بر عکس باشه و فرضا 480 تا از Command و 20 تا را از رویداد استفاده کنید) ، در هر صورت ، باز view و المنت ها را به رویداد متصل کردید که باعث میشه جداسازی (منطق تجاری از view) ، پویا نباشه .
یعنی میخوام بگم فرضا حتی یه دونه از 500 تا را هم از رویداد استفاده کنیم ، باز باعث میشه کار خراب بشه . پس در این صورت فرقی نداره که دیگه از Command ها استفاده کنیم .
درست میگم؟
خیر. کدوم رویداد؟ کدوم کد؟ مثل اینه که سر اینکه برای تهیه سیمان با کیفیت فلفل و تخم مرغ چقدر لازمه تردید دارید، در حالی که اصلا تا حالا سیمان تهیه نکرده اید که بدونید فلفل و تخم مرغ در تهیه سیمان نقشی نداره. شما هنوز یک نمونه کد MVVM ننوشته اید که با صرفا یک رویداد همچین کاری رو انجام بده، اون 500 تا پیشکش. یک عددش رو بنویسید و بعد فکر کنید 500 بشه چی میشه. منطق کار تون از اساس مشکل داره، طبق معمول با فرض اشتباه پیش میرید، صد بار هم بگم گوش هم نمی کنید، چه فایده. تا وقتی کد ننویسید بیخودی رو هوا برای خودتون فرضیه های اشتباه می بافید و نتیجه گیری های اشتباه می کنید.
استاد ، من هنوز 2 چیز را دقیق در mvvm متوجه نشدم (البته در حال خوندن مقاله ها و پیگیری کردن هستم چون اگه بگید جستجو نمیکنم) .
اینکه در پست 670 که پروژه ی mvvm داده بودم که از درون کلاس ViewModel ، پروپرتی ای از نوع Model (که نام و نوعش Student بود) در نظر گرفت . یکی از سئوال هام اون موقع این بود که آیا میشه از view ، به اعضایِ پروپرتیِ Student که در ViewModel هست ، دسترسی داشته باشیم (که mvvm را هم نقض نکرده باشه) ؟
ارتباط بین ViewModel و View هیچ ایرادی نداره، اما نباید View به هیچ طریقی وابسته به Model باشه یا برعکس، پس اگر موقع کامپایل View به شیء ای از Model نیاز داشته باشه MVVM نیست و ایراد داره، به هر طریقی که اینکار انجام شده باشه نباید در زمان کامپایل وابستگی ایجاد کنه.
برای همینه که Binding و Command مناسب MVVM ئه، چون در زمان اجرا ارتباط رو بررسی می کنه، نه زمان کامپایل.
دسترسی ای رو که ایجاد کرده اید اگر در کدی باشه که موقع کامپایل View این وابستگی ایجاد می کنه با MVVM جور در نمیاد.
اما من یه چیزی شبیه این رو در یه مثالی دیدم که اگه اشتباه نکنم ، یه همچین کاری را با Binding کردن انجام داد . لینک زیر :


اون قسمت از کد که :

XML:
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />

                    <GridViewColumn Header="Address" Width="200" DisplayMemberBinding="{Binding Address}"/>
                </GridView>

یا یه کم بالاتر اش ، که در کنترل Button ، مقدار Path را بصورت Path=SelectedItem.Address نوشت (البته برای ListView ، پروپرتیِ SelectedItem نداریم بلکه SelectedItems داریم که نمیدونم حالا اشتباه تایپی داشت یا نه) .
اگه اشتباه نکنم ، Binding کردن ، چون پویا هست ، مشکلی نداشته باشه . دقیق نمیدونم .
حالا باید بیشتر مقالات را ببینم .
نگید پویا، بخاطر Dynamic بودنش نیست، پویا بودن به این معنا است که قابل تغییر ئه، مثل متغیر، مثل مشخصه، پویا بودن به این معنی نیست که موقع کامپایل مقدارش بررسی نشه یا وابستگی ایجاد نکنه.
بخاطر Late Binding بودنه، یعنی بعدا متصل میشه، یعنی بعد از کامپایل ارتباط برقرار می کنه، یعنی موقع کامپایل ارتباط چک نمیشه، ارتباط برقرار نمیشه، بعدا موقع اجرا وقتی لازم شد ارتباط تازه چک میشه و برقرار میشه. برای همین موقع کامپایل View اون Binding ها به چیزی وابسته اش نمی کنه، اون مقدارش فقط یک string ئه.
سئوال بعدی هم که هنوز حل نشده اینه که وقتی در لایه ی Model و ViewModel ، اینترفیس INotifyPropertyChanged را پیاده سازی میکنن (فرض کنید این پیاده سازی در کلاس مربوط به Model انجام شد) ، خوب در کلاس مربوط به ViewModel ، از رویداد PropertyChanged که درون Model پیاده سازی شد ، باید اتصال انجام داده بشه و رویداد PropertyChanged را به handler ای متصل کنیم دیگه . درسته؟
معمولا نه، چون معمولا خودتون نیازی ندارید که اختصاصی از این تغییر مقدار مشخصه ها با خبر شوید، معمولا برای این می نویسیدش که Binding ها بتوانند از تغییر مقدار مشخصه با خبر شوند، برای همین معمولا در ViewModel به متدی متصلش نمی کنید.
فقط دقت کنید که چه زمانی این PropertyChanged لازم میشه، زمانی که Model مستقلا داده ای رو با روال داخلش تغییر داده و میخواد به ViewModel اطلاع بده که داده قبلی رو به هر دلیلی بدون اینکه به View و رفتار کاربر مربوط باشه تغییر دادم، نه وقتی که View با عملکردی منجر به تغییر داده ای شد که بعدا در Model منعکس خواهد شد، نه وقتی ViewModel داده ای رو برای ثبت کردن به Model منتقل می کنه، نه وقتی مشخصه میخواد از طریق Binding مقدارش رو داخل Model بنویسه، فقط وقتی که Model از داخل مقداری رو تغییر داده که Binding ها ازش بی خبر هستند.
اگه درسته ، من هیچ جا ندیدم که مثلا در لایه ی ViewModel یا View ، این رویداد را که در لایه ی ViewModel یا Model پیاده سازی کردن ، بخوان به handler ای متصل کنن .
وقتی اتصال به رویداد انجام نشه ، این که متدی را اجرا نمیکنه تا بهشون پیامی بده . پس چجوری کار میکنه؟!!
بله، معمولا کدی نمی نویسند. کار می کنه چون کد هایی در WPF هست که ازش استفاده می کنه، شما کدش رو ننوشته اید اما در خود NET. هست.
foreach چطور با IEnumerable کار می کنه؟ using چطور با IDisposable کار می کنه؟ اینها کد هایی دارند که دنبال اون اینترفیس مورد نظرشون می گردند. Binding ها هم کد هایی دارند که از این INotifyPropertyChanged استفاده می کنند.
همونطور که ممکنه برای یک کلاس IDisposable رو تعریف کنید و هیچوقت متد Dispose رو فراخوانی نکنید اما در using ها بصورت خودکار ازش استفاده میشه، اینجا هم ممکنه شما خودتون از INotifyPropertyChanged.PropertyChanged استفاده نکنید اما در Binding ها ازش استفاده میشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
خیلی ممنون استاد . :rose:
نکات خیلی خوبی را اشاره کردید . جواب و پرسش از دو پست قبلی تون را در پست بعدی ام میپرسم . چون این پست خودش طولانی هست .
اما قبل اش در این پست ، اون پروژه ی MVVM ای که در پست 670 نوشته بودم را به روزرسانی کردم و در این پست پیوست میکنم ، مجددا بی زحمت بررسی کنید که از لحاظ معماری MVVM دیگه مشکلی نداره؟

این پروژه را طبق مقالاتی که در لینک های زیر :


و


که همون لینک هایی بودن که در پست قبلی دادم و همچنین مخصوصا نمونه کدِ MVVM ای که در لینک زیر هست که خیلی دنبال همچین پروژه ای گشتم :


طبق مقالات در این لینک ها ، پروژه را به روزرسانی کردم .
در این پروژه ، از کمپوننت MVVMLight استفاده کردم .

-----------------------------------------

طبق مقالات بالا و همچنین تجربه ی کوچیکی که بدست آوردم ، چیزهایی که در زیر میگم را ببینید درستن؟ :

اینکه متوجه شدم در معماری MVVM ، اولا میتونیم در view ، رویدادهای خودمون را درست کنیم اما هر جایی اگه لازم بود با کلاس های Model رابطه برقرار کنیم ، یعنی اعضای کلاس Model را فراخونی یا تغییر بدیم (یا هر کار دیگه ای روشون کنیم) ، در این صورت ، حتما برای فراخونی (یا دستکاریِ) اعضای Model ، باید از لایه ی ViewModel بهشون دسترسی پیدا کنیم نه از لایه ی View .
اما در هر صورت ، اگه رویدادی را مینویسیم که در کد و بدنه ی اون رویداد نیاز به فراخونی یا تغییر مقدار یا کلا نیاز به دسترسی به کلاس Model نباشه ، در اون صورت میتونیم بدنه ی اون رویداد را در لایه ی View بدون هیچ دغدغه ای بنویسیم .

نکته ی دیگه اینکه چون Binding ها ، زمان کمپایل بررسی نمیشن و زمان اجرا بررسی میشن
(البته گفتید که نگم Binding ها پویا هستند که دقیق متوجه نشدم) ، بنابراین بدون هیچ دغدغه ای میتونیم حتی پروپرتی هایی که درون کلاس Model هستند را به پروپرتی های لایه ی View مون Binding کنیم (مثل Binding کردن پروپرتی Student.FirstName که این پروپرتیِ FirstName درون لایه ی Model هست ، به پروپرتی Text ئه TextBox در لایه ی View) .

در این پروژه ، مثلا برای به روزرسانی Student.FirstName به مقدارِ جدید "Shahid Mohsen" (درهندلر برای Command با نامِ ChangeStudentMemberValueForCommand) ، چون FirstName ، عضوی از کلاس Student هست ، باید این فراخونی و تغییر مقدار ، از کلاس ViewModel انجام بشه ، نه از کلاس View (یا نه از طریق هندلر ای که برای رویداد در View تعریف میشه) .
برای این کار هم ، چون در رویداد کلیک دکمه ی ChangeStudentMemberValueButton میخواستم این کار را انجام بدم ، از Command استفاده کردم .

-------------------------------

اما من دقیق تفاوت معماریِ MVVM (مثلا همین پروژه) با پروژه هایی که معماری MVVM را پیاده سازی نمیکنن (و لایه ی ViewModel ندارند) را متوجه نشدم .
بیاید فرضا همین پروژه ی کوچیک را با حالتی که همین پروژه ، MVVM را پیاده سازی نکنه ، مقایسه کنیم :

درون کلاس StudentViewModel ، در متدی که برای Command با نام ChangeStudentMemberValueForCommand نوشته شد ، پروپرتی this.Student.FirstName به مقدار "Shahid Mohsen" و پروپرتی this.Student.LastName به مقدار "Hojaji" تغییر مقدار دادن .
یعنی اعضای کلاس Student که FirstName و LastName بودن ، مقداردهی و به روزرسانی شدن .
پس بنابراین ، اگه در به روزرسانیِ بعدی برنامه ، بیایم پروپرتیِ FirstName یا LastName را از کلاس Student حذف کنیم ، چون این کد موقع کمپایل کردن چک میشه ، پس با ارور ، برنامه دیگه اجرا نمیشه .
در صورتی که معماری MVVM هم اگه پیاده سازی نشه ، باز هم همینطوره .

در این پروژه ، چون Command ها با Binding متصل میشن ، با حذف کردنِ Command مون (یعنی پروپرتیِ ChangeStudentMemberValueCommand و متد ChangeStudentMemberValueForCommand) ، اون View مون دیگه خطای زمان کامپایل نمیده و اجرا میشه (هر چند اگه Command مورد نظر را بهش مجددا Binding نکنیم ، خوب مشخص هست که مقداری نمیگیره و خطای Binding را زمان اجرا اون هم صرفا زمانی که برنامه با ویژال استودیو اجرا میشه را میده اما خطای زمان کامپایل هم نمیده) .

ولی پروژه ای که در واقع Command نداشته باشه (نه اینکه صرفا معماری MVVM داشته باشه) ، چون با رویداد متصل شد ، با حذف رویداد ، زمان کامپایل خطای عدم وجود هندلر برای رویداد را میگیره.
تفاوت شون فقط همینه؟

اما نکته ی مهم اینه که اولا زمانی که نتونیم از Command ها استفاده کنیم (مثلا زمانی که بخوایم از رویداد Mouse Enter در دکمه ای استفاده کنیم) و همچنین در اون رویداد به اعضای کلاس Model دسترسی داشته باشیم (مثل رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter برای دکمه ی MouseEnterChangeStudentMemberValueButton در پروژه که میخواد به پروپرتیِ Student.FirstName و Student.LastName دسترسی داشته باشه) ، در این صورت که نمیتونیم از Command استفاده کنیم ، برای اینکه معماری MVVM نقض نشه ، باید چی کار کنیم؟
من متدی به نام MouseEnterChangeStudentMemberValueButtonMouseEnterMethod را در کلاس StudentViewModel ساختم و در رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter که در View ام بود ، این متد را فراخونی کردم چون اگه درکم از معماری MVVM درست بوده باشه ، برای دسترسی به اعضای کلاس Model ، باید از کلاس ViewModel بهش دسترسی داشته باشیم نه از کلاس View .
کاری که کردم ، درسته؟
اگه غلط هست ، چی کار باید کنم؟
اگه درست هست ، خوب چون رویداد (ای را در لایه ی View که همون رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter هست) به دکمه ی مورد نظرمون متصل کردیم ، خوب این که با مانی که معماری MVVM پیاده سازی نشه ، فرقی نداره . چون در پروژه های بدون معماری MVVM ، بخوایم هندلر رویداد را حذف کنیم ، خطای زمان کمپایل میده و در معماری MVVM هم همینطور . یعنی با حذف هندلر رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter در این پروژه ، خطای زمان کمپایل میده و پروژه اجرا نمیشه .

یا اینکه بجای این کار ، بهتره که در کلاس ViewModel ، یه پروپرتی ای از نوع View (که View مون در اینجا همون کلاس MainWindow هست) ایجاد کنیم و توسط این پروپرتی (یعنی درون کلاس ViewModel) ، رویدادِ MouseEnter را برای دکمه ی MouseEnterChangeStudentMemberValueButton متصل کنیم؟
این روش بهتره؟
بهتر هم باشه ، ولی به Command که با Binding متصل میشه ، اصلا نمیرسه .

-------------------------------

کلا هدف از معماری MVVM را بیشتر توضیح میدین؟ منظورم از لحاظ تئوری نیست . یعنی اینکه باعث نگه داری و به روزرسانی بهتر و جداسازی لایه ی View و Model میشه . منظورم از این لحاظ نیست .
بلکه مقایسه بصورت عملی و مقایسه بصورتی که در بالا گفتم هست .
یعنی مثلا فلان پروپرتی یا فلان رویداد یا binding یا command ای که در معماری MVVM تعریف کردیم را حالا اگه بصورتی که معماری MVVM نداشته باشه ، پیاده سازی کنیم ، چه قابلیت هایی بهمون میده که در زمانی که MVVM را پیاده سازی نکردیم ، نمیده ؟
از این لحاظ منظورمه .

ببخشید زیاد شد .
خیلی ممنون استاد :rose:
 

پیوست ها

  • MVVM_Practies.rar
    1.9 مگایابت · بازدیدها: 1
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
اینکه فلان کنترل ها مشخصه Command دارند به این معنی نیست که در جاهای دیگر نمیشه Command بکار برد.

خیلی ممنون استاد
نکات بسیار خوبی گفتین :rose:
منظورتون از جاهای دیگه ، المنت ها و کنترل های دیگه هست؟
به هر حال زمانی میشه از Command ها در المنت ای استفاده کنیم که اون المنت ، اینترفیس ICommandSource را پیاده سازی کرده باشه یا ما در کنترل شخصی مون که میسازیم ، پیاده سازی کرده باشیم . درسته دیگه؟


بله.

فرمان و رخداد دو چیز متفاوت ئه، جای همدیگه رو نمی گیرند.
این محدودیت در Command که نیست، در Command هیچ روالی نیست که بگه فقط با المنت های فلان و بهمان کار می کنم یا فقط روی رخداد هایی با نام فلان و بهمان، این کنترل های طراحی شده در WPF هستند که معمولا صرفا برای عمل اصلی شون Command در نظر گرفته اند که به سادگی برای اون عمل اصلی فرمان تعریف بشه، معنی اش نه اینکه که جایگزین رویداد اصلی شده و نه معنی اش اینه که دیگه نمیشه برای سایر رویداد ها فرمان اجرا کرد.

منظورم این بود که ای کاش یه جوری Command ها را در المنت ها بکار میگرفتن که زمانی میخواستیم Command ای را برای المنت ای تعریف کنیم ، بتونیم بگیم که برای کدوم رویداد از اون المنت باشه .
یعنی جوری طراحی میکردن که بجای رویداد بشه از Command ها استفاده کرد .
چون Command را Binding میکنیم و زمانی که بخوایم حذف شون کنیم ، چون Binding میشن و زمان اجرای برنامه بررسی میشن ، زمان کمپایل برنامه ، اروری نمیده و خیلی برای جداسازی view با بخش هندلر یا execute اش ، مناسب هست .

فقط معنی اش اینه که برای انجام یک کاری با منطق مشخصی Command ای تعریف شده که انجامش رو ساده کنه. حالا ممکنه برنامه نویس در رویداد های دیگری Command های دیگری رو اجرا کنه یا در کلاس وارث مشخصه های Command جدیدی اضافه کنه و ...

همین که رویدادی را متصل کنه ، خودش اصل داستان هست دیگه .
چون وقتی رویداد را متصل کنه و بعدا بخواد هندلرش را حذف کنه ، ارور زمان کمپایل میده و برنامه اجرا نمیشه .
وگرنه اگه رویدادی متصل شد ، حداقل دیگه نیازی به تعریف Command های شخصی (نه Command های از پیش تعریف شده) نیست . چون طرف کدی را که قراره در متد Execute ئه Command بنویسه را در همون رویداد مینویسه دیگه .

کنترل ها قراره بر اساس عملکرد کاربر تعاملی با ViewModel داشته باشند تا فرضا موقع کلیک روی دکمه فلان عملکرد برای بروز رسانی داده ها انجام بشه. این عملکرد کاربر هم چیزی نیست که برای هر رخدادی مرسوم باشه، مثلا موقع تغییر ابعاد دکمه یا پنجره عموما تعاملی با ViewModel پیش نمیاد، چه ارتباطی بین داده هایی مثل نام کاربری و فرضا Hover یا تغییر رنگ المنت هست؟ منطقی نیست که برای همه رخداد ها Command نوشته بشه و در ضمن محدودیت از جانب Command نیست، طراح اون کنترل منطقی ندونسته که Command رو برای همه رخداد ها در نظر بگیره.

همونطور که در همین دو پست تلویحا اشاره کردید ، زمانی نیاز هست که View با ViewModel رابطه داشته باشه که View بخواد به اعضای Model دسترسی داشته باشه . درست میگم؟

یعنی به قول خودتون نیازی نیست که فرضا وقتی که بخوایم ابعاد کنترلی را تغییر بدیم (و چون در این نوع کد ، لازم به دسترسی به اعضای Model نداریم) ، بخوایم این نوع کدمون را در ViewModel بنویسیم. بلکه این نوع کد را در همون View مینویسیم .
هر زمان که خواستیم به اعضای Model دسترسی داشته باشیم ، اون کد را در ViewModel باید بنویسیم
مثل تغییر مقدار پروپرتی this.Student.FirstName در متد برای Command با نام ChangeStudentMemberValueForCommand در پروژه ی پست قبل . درسته؟

اگه درسته ، پس این ، جزء یکی از نکات اساسی بود که اشاره کردین و خیلی ممنونم . ندیدم در مقالاتی که تا حالا خوندم ، به این نکته اشاره کنن . چون خیلی مهم هست که چه زمانی ، چه کدی را در چه جایی بنویسیم .
تشکر استاد .


قبلا هم گفتم. شما در کدتون هر جا که لازم میدونید هر Command ای که میخواهید بکار می برید و Execute می کنید، چه مانعی برای فراخوانی Command برای رخداد دلخواه شما هست؟ هیچ مانعی.

بله .
البته در بالا هم اشاره کردم .
مانع اصلی ، به نظرم همون متصل کردن رویداد هست .
چون وقتی رویداد را متصل کنه و بعدا بخواد هندلرش را حذف کنه ، ارور زمان کمپایل میده و برنامه اجرا نمیشه .
وگرنه اگه رویدادی متصل شد ، حداقل دیگه نیازی به تعریف Command های شخصی (نه Command های از پیش تعریف شده) نیست .

در MVVM نمی توانید در View کدی بنویسید که به Model وابسته باشه، اگر چنین موردی پیش بیاد دیگه MVVM نیست.
در کلاس View هم هر متد متصل به رویدادی داشته باشید، باید کدی برایش بنویسید که در زمان کامپایل کاری با Model نداشته باشه.
چون در زمان کامپایل View باید طرف ارتباط مشخص باشه تا کد کامپایل بشه

این تیکه را که bold کردم ، جزء همون موردی هست که در پاراگراف بالاتر گفتید . و اینکه زمانی در ViewModel (برای رویدادهای View) کد مینویسیم که قراره View به اعضای Model دسترسی داشته باشن و کدهای این دسترسی را در ViewModel مینویسیم .
اما اگه نیازی به دسترسی در رویدادهای View به Model نباشه ، در همون View همه ی کدهامون را مینویسیم .


فرضا یک دکمه دارید که وقتی ماوس رویش رفت رنگش عوض میشه یا انیمیشنی اجرا می کنه یا وقتی ابعاد المنتی تغییر کرد متناسب با اون کادر یک المنت دیگری تغییر ابعاد میده، این جور رخداد ها ربطی به View دارند؟ نه. برای همین نیازی به Command ای ندارند که بخواد کاری در مورد ViewModel انجام بده.

منظورتون "ربطی به Model" هست در اونجایی که bold کردم دیگه؟ درست میگم؟

اولا Command نمی توانند انجام بدهند بی معنیه، اون Command صرفا یک مشخصه است که در رخداد خاصی اجرا میشه، می توانید اینکار رو هر جا که لازم می دانید انجام بدهید. شما در هر کدی می توانید یک شیء Command رو بکار ببرید، چه کد مربوط به رخداد خاصی باشه و چه نباشه.

بله .
ولی در پاراگراف های بالاتر ، مشکل متصل کردن رویدادها را که گفتم .
البته این رو هم میدونم که درون اون کلاسِ کنترل (مثل کلیکِ دکمه) ، وقتی Command ای را Binding میکنیم ، اون Command را در رویدادی (مثل کلیک دکمه) Binding میکنن و ما هم میتونیم این کار را کنیم . اما اتصال به اون رویداد ، در درون همون کلاس (در اینجا کلاس دکمه) انجام میشه و اتوماتیک هست و ما با استفاده از Command در دکمه ، دیگه رویداد کلیک دکمه را به هندلر ای متصل نمیکنیم که با حذف اون هندلر در آپدیت بعدی ، نگران خطای کمپایلر باشیم .

ثانیا در اغلب موارد KeyBinding و MouseBinding کفایت می کنه، مثال هاشون رو جستجو کنید.

ممنون . بله ، باید درباره ی این موارد هم جستجو کنم . فعلا اطلاع دقیقی ندارم .

خیر. کدوم رویداد؟ کدوم کد؟ مثل اینه که سر اینکه برای تهیه سیمان با کیفیت فلفل و تخم مرغ چقدر لازمه تردید دارید، در حالی که اصلا تا حالا سیمان تهیه نکرده اید که بدونید فلفل و تخم مرغ در تهیه سیمان نقشی نداره. شما هنوز یک نمونه کد MVVM ننوشته اید که با صرفا یک رویداد همچین کاری رو انجام بده، اون 500 تا پیشکش. یک عددش رو بنویسید و بعد فکر کنید 500 بشه چی میشه. منطق کار تون از اساس مشکل داره، طبق معمول با فرض اشتباه پیش میرید، صد بار هم بگم گوش هم نمی کنید، چه فایده. تا وقتی کد ننویسید بیخودی رو هوا برای خودتون فرضیه های اشتباه می بافید و نتیجه گیری های اشتباه می کنید.

منظورم اینه که فرضا 500 تا رویداد میخوایم برای کنترل های مختلف بنویسیم که به اعضای Model دسترسی داشته باشن . 20 تاشون را که قراره در رویداد کلیک دکمه بنویسیم ، بجاشون از Command ها استفاده میکنیم .
اما 480 تای دیگه را چون از رویدادهای دیگه استفاده میکنیم که بصورت پیش فرض Command ای براشون در نظر گرفته نشد (برخلاف کلیک دکمه) ، مجبوریم از رویدادها استفاده کنیم .
برای استفاده از رویدادها هم اونها را باید به هندلر متصل کنیم . در آپدیت بعدی برنامه ، اگه بخوایم بعضی از این هندلرها را حذف کنیم ، برخلاف Command ها ، ارور زمان کمپایل میدن و برنامه اجرا نمیشه .

خوب اگه قراره این طور باشه ، چه اینکه یک هندلر برای رویداد را حذف کنیم (که باعث ارور زمان کمپایل میشه) و چه صدها هندلر را ، در نتیجه ، که اجرا نشدن برنامه هست ، فرقی نداره . پس بنابراین بجای اون 20 تا رویداد کلیک دکمه که از Command ها (یی که بصورت شخصی میسازیم ، یعنی در متد Execute اش خودمون کد مینویسیم) استفاده کرده بودیم ، بجای اونها هم از رویدادها استفاده کنیم (و رویداد کلیک دکمه را هم به هندلر ای متصل کنیم) که در نتیجه اش (که اجرا نشدن برنامه بود) که فرقی نداره .
پس فرقی نداره که بجای Command های (شخصی سازی شده ای) که به عنوان رویداد کلیک دکمه استفاده میکنیم ، مستقیما از رویداد کلیک دکمه بجاش استفاده کنیم .
نمیدونم تونستم منظورم را برسونم یا نه.

ارتباط بین ViewModel و View هیچ ایرادی نداره، اما نباید View به هیچ طریقی وابسته به Model باشه یا برعکس، پس اگر موقع کامپایل View به شیء ای از Model نیاز داشته باشه MVVM نیست و ایراد داره، به هر طریقی که اینکار انجام شده باشه نباید در زمان کامپایل وابستگی ایجاد کنه.
برای همینه که Binding و Command مناسب MVVM ئه، چون در زمان اجرا ارتباط رو بررسی می کنه، نه زمان کامپایل.
دسترسی ای رو که ایجاد کرده اید اگر در کدی باشه که موقع کامپایل View این وابستگی ایجاد می کنه با MVVM جور در نمیاد.

بله .
یا به عبارتی ، Binding کردنِ View به پروپرتی در هر لایه ای (چه لایه ی ViewModel یا لایه ی Model) ، هیچ اشکالی نداره چون Binding ، کدها و ارتباطات را زمان اجرای برنامه بررسی میکنه و همون زمان که نیاز داره بررسی میکنه نه زمان کمپایل برنامه (و بنابراین باعث عدم اجرای برنامه نمیشه) .

اما اگه بخوایم از View به اعضای Model دسترسی داشته باشیم ، باید این دسترسی را از لایه ی ViewModel با اعضای لایه ی Model برقرار کنیم نه از لایه ی View به Model .

البته دلیل عملی اش را نمیدونم و در پاراگراف آخر پست قبلی پرسیدم .

ادامه در پست بعدی ...
 
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
نگید پویا، بخاطر Dynamic بودنش نیست، پویا بودن به این معنا است که قابل تغییر ئه، مثل متغیر، مثل مشخصه، پویا بودن به این معنی نیست که موقع کامپایل مقدارش بررسی نشه یا وابستگی ایجاد نکنه.

بله .
این رو بگم منظورم این بود که توی اون لینکی که داده بودم ، در کدی که داد :

XML:
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />

                    <GridViewColumn Header="Address" Width="200" DisplayMemberBinding="{Binding Address}"/>
                </GridView>

در کد بالا ، طرف اومد مستقیما به پروپرتی Name و Address که از اعضای کلاس Person (از لایه ی Model) اش بودن ، Binding انجام داد . در صورتی که DataContext ئه View اش را PersonViewModel (از لایه ی ViewModel) در نظر گرفت .
در لایه ی ViewModel ، پروپرتی Persons از نوع IList<Person> هست . نباید اول نام این پروپرتی را میگفت و بعد اعضای هر عضوی از این IList را که Name و Address بودن ، فراخونی میکرد؟

البته من Binding به کالکشن ها را نخوندم و احتمالا بخاطر همینه که قوانین Binding به اعضای کالکشن ها را بلد نیستم . درسته؟

----------------------------------------------------------------------------

سئوال دیگه در این زمینه اینکه در مقاله در لینک زیر :


در قسمت توضیحات "List Change Notification" ، اول یه پروپرتی ای بنام StringList از نوع List<string> در نظر گرفته .
بعد در متدی بنام AddList که متدی هست که به Command ئه AddListCommand معرفی شده (به عنوان متدی که زمان Execute ، اجرا بشه) ، میاد عضو جدیدی را به اعضای کالکشنِ StringList اضافه میکنه و بعد متد RaisePropertyChanged("StringList"); را برای اطلاع دادن به View اجرا میکنه .

اما در آخرِ همون بخش این نتیجه را میگیره که با اونکه متد RaisePropertyChanged را اجرا کرد و باعث شد تا به View اطلاع بده و بخش get ئه پروپرتیِ StringList را مجددا فراخونی کنه ، اما دقت نمیکنه که مدخل جدید و عضو جدیدی به لیست اضافه شد و بنابراین متوجه ی تغییرات (اضافه شدن عضو جدید) نمیشه . هر چند متوجه نشدم که چرا حتی با وجود اطلاع و اجرای متد RaisePropertyChanged ، متوجه ی اضافه شدنِ ورودی و عضو جدید به کالکشن نمیشه؟

نهایتا اشاره میکنه که برای استفاده از کالکشن (بجای استفاده از لیست یا هر چیز دیگه ای) ، از نوع ObservableCollection استفاده کنید که INotifyCollectionChanged را پیاده سازی کرده .
میخوام بپرسم که اگه ما اینترفیس INotifyCollectionChanged را پیاده سازی کنیم ، در این صورت میتونیم از نوع List و هر نوع کالکشن دیگه ای استفاده کنیم دیگه . درسته؟

دلیل اینکه در اون لینک ، با فراخونی RaisePropertyChanged ، اطلاع از اضافه شدن عضو جدید به کالکشن ، درست کار نکرد ، دقیقا چی بود؟ چون کل کالکشن با مقدار جدیدی تعویض نشد . صرفا مقدار جدیدی به اعضاش اضافه شد و به طبع ، INotifyPropertyChanged باید جواب بده . این طور نیست؟


بخاطر Late Binding بودنه، یعنی بعدا متصل میشه، یعنی بعد از کامپایل ارتباط برقرار می کنه، یعنی موقع کامپایل ارتباط چک نمیشه، ارتباط برقرار نمیشه، بعدا موقع اجرا وقتی لازم شد ارتباط تازه چک میشه و برقرار میشه. برای همین موقع کامپایل View اون Binding ها به چیزی وابسته اش نمی کنه، اون مقدارش فقط یک string ئه.

آها خیلی ممنون . نکته ی خوبی بود که Binding ، زمان اجرای برنامه کلا متصل میشه و نه زمان کمپایل برنامه و بنابراین اگه Binding ، اروری هم داشته باشه ، ارور زمان کمپایل هم نمیده و باعث عدم اجرای برنامه نمیشه .

معمولا نه، چون معمولا خودتون نیازی ندارید که اختصاصی از این تغییر مقدار مشخصه ها با خبر شوید، معمولا برای این می نویسیدش که Binding ها بتوانند از تغییر مقدار مشخصه با خبر شوند، برای همین معمولا در ViewModel به متدی متصلش نمی کنید.

آها . پروپرتیِ INotifyPropertyChanged را در MVVM بخاطر این پیاده سازی میکنیم که به پروپرتی های (معمولا المنت و کنترل هایی) که به پروپرتی های اون کلاس (ای که اینترفیس INotifyPropertyChanged را پیاده سازی کردیم) Binding کردیم ، اطلاع از تغییر مقدار را بده . درسته؟
نکته ی خیلی خوبی بود . تشکر

و واسه ی این در هر دو لایه ی ViewModel و Model ، اینترفیس INotifyPropertyChanged را پیاده سازی میکنن چون Binding ها به قول تون Late Binding هستن و زمان اجرای برنامه ، ارتباطات را چک میکنن ، پس میشه از کلاس View به کلاس های هم ViewModel و هم Model ، عمل Binding را انجام داد .
پس در واقع در هر لایه ای که (معمولا المنت های) View مون را به لایه ی مورد نظر Binding کردیم (لایه ی ViewModel یا Model) ، فقط در اون صورت لازمه که اینترفیس INotifyPropertyChanged را در اون لایه پیاده سازی کنیم . درسته؟

یعنی اگه المنت های View مون را به پروپرتی های لایه ی ViewModel مون Binding کردیم ، در اون صورت لازمه که برای کلاس های ViewModel مون ، اینترفیس INotifyPropertyChanged را پیاده سازی کنیم . در این صورت لازم نیست برای کلاس های Model مون ، این اینترفیس را پیاده سازی کنیم .
اگه هم به پروپرتی های لایه ی Model مون Binding انجام دادیم ، در این صورت اینترفیس INotifyPropertyChanged را فقط برای لایه ی Model مون پیاده سازی میکنیم و پیاده سازی اش برای لایه ی ViewModel لازم نیست .
اگه هم به هر دو لایه ی ViewModel و Model مون Binding کردیم که اینترفیس INotifyPropertyChanged را برای هر دوی این لایه ها پیاده سازی میکنیم .
درست میگم؟

بنابراین در پروژه ای که دادم چون از View هم به پروپرتی های Student.FirstName و Student.LastName (که در لایه ی Model هستند) و هم به پروپرتی ChangeStudentMemberValueCommand (که در لایه ی ViewModel هست) ، Binding انجام دادیم ، پس باید هر دو کلاس Model و ViewModel با هم اینترفیس INotifyPropertyChanged را پیاده سازی کنن . درسته؟

البته قبلا (در پست صفحه ی 34) وقتی پرسیده بودم که چرا Binding ای که بین پروپرتی های دو المنت و کنترل انجام میدیم ، نیاز به پیاده سازی اینترفیس INotifyPropertyChanged نداره اما در اینجا چرا نیاز داره ، که جواب دادید که چون کلاس ها در اشیاء Target برای بایندینگ (کلاس های Target Binding) در اونها ، پروپرتی هاشون بجای اینکه مقدارشون را در فیلدها ذخیره کنن ، از DependencyProperty ها استفاده میکنن (و Binding ها هم از مقادیر DependencyProperty ها استفاده میکنه) که نکته ی خیلی خوبی بود .
این به این معناست که اگه ما بجای ذخیره ی مقادیر پروپرتی ها درون فیلدها ، از DependencyProperty استفاده کنیم ، دیگه نیاز به پیاده سازی اینترفیس INotifyPropertyChanged در کلاس مورد نظر نداریم؟



فقط دقت کنید که چه زمانی این PropertyChanged لازم میشه، زمانی که Model مستقلا داده ای رو با روال داخلش تغییر داده و میخواد به ViewModel اطلاع بده که داده قبلی رو به هر دلیلی بدون اینکه به View و رفتار کاربر مربوط باشه تغییر دادم،

بله

نه وقتی که View با عملکردی منجر به تغییر داده ای شد که بعدا در Model منعکس خواهد شد، نه وقتی ViewModel داده ای رو برای ثبت کردن به Model منتقل می کنه،

البته فرق خاصی نباید داشته باشه . داره؟
چون فرض کنید دو تا دکمه داریم . Content ئه دکمه ی 1 ، به پروپرتیِ Property_1 در کلاس ViewModel مون Binding کردیم .
در رویدادی از دکمه ی 2 بخوایم مقدار Property_1 در کلاس ViewModel را تغییر بدیم ، باز باید Property_1 مون به Binding مون اطلاع بده که مقدارش تغییر کرد تا مقدار Content ئه دکمه ی 1 تغییر کنه دیگه .

نه وقتی مشخصه میخواد از طریق Binding مقدارش رو داخل Model بنویسه،

بله.

بله، معمولا کدی نمی نویسند. کار می کنه چون کد هایی در WPF هست که ازش استفاده می کنه، شما کدش رو ننوشته اید اما در خود NET. هست.
foreach چطور با IEnumerable کار می کنه؟ using چطور با IDisposable کار می کنه؟ اینها کد هایی دارند که دنبال اون اینترفیس مورد نظرشون می گردند. Binding ها هم کد هایی دارند که از این INotifyPropertyChanged استفاده می کنند.
همونطور که ممکنه برای یک کلاس IDisposable رو تعریف کنید و هیچوقت متد Dispose رو فراخوانی نکنید اما در using ها بصورت خودکار ازش استفاده میشه، اینجا هم ممکنه شما خودتون از INotifyPropertyChanged.PropertyChanged استفاده نکنید اما در Binding ها ازش استفاده میشه.

آها خیلی ممنون .
پس Binding ها هم مثل foreach و IEnumerable و ... ، در کلاس مورد نظر میگردن ببینن که آیا اون کلاس ، اینترفیس مورد نظرشون را پیاده سازی کرده یا نه تا از اونها و اعضای پیاده سازی شده شون استفاده کنن .
درسته؟

ببخشید استاد که خیلی طولانی شد .
واقعا متشکرم . :rose:
 

the_king

مدیرکل انجمن
اما قبل اش در این پست ، اون پروژه ی MVVM ای که در پست 670 نوشته بودم را به روزرسانی کردم و در این پست پیوست میکنم ، مجددا بی زحمت بررسی کنید که از لحاظ معماری MVVM دیگه مشکلی نداره؟
بله.
اینکه متوجه شدم در معماری MVVM ، اولا میتونیم در view ، رویدادهای خودمون را درست کنیم اما هر جایی اگه لازم بود با کلاس های Model رابطه برقرار کنیم ، یعنی اعضای کلاس Model را فراخونی یا تغییر بدیم (یا هر کار دیگه ای روشون کنیم) ، در این صورت ، حتما برای فراخونی (یا دستکاریِ) اعضای Model ، باید از لایه ی ViewModel بهشون دسترسی پیدا کنیم نه از لایه ی View .
بله.
اما در هر صورت ، اگه رویدادی را مینویسیم که در کد و بدنه ی اون رویداد نیاز به فراخونی یا تغییر مقدار یا کلا نیاز به دسترسی به کلاس Model نباشه ، در اون صورت میتونیم بدنه ی اون رویداد را در لایه ی View بدون هیچ دغدغه ای بنویسیم .
بله. کلا رویداد مورد خاص و منحصر بفردی نیست که برایش قاعده یا استثناء در نظر گرفته بشه، هر کدی که در View می نویسید چه مربوط به متد رخداد باشه و چه نباشه نباید به Model وابسته باشه، چون اگر وابسته باشه در زمان کامپایل کد View رو به Model وابسته می کنه.
نکته ی دیگه اینکه چون Binding ها ، زمان کمپایل بررسی نمیشن و زمان اجرا بررسی میشن (البته گفتید که نگم Binding ها پویا هستند که دقیق متوجه نشدم)
پویا یعنی چی؟ یعنی مستعد تغییر ئه، میتونه تغییر کنه. int a = 5 مستعد تغییر ئه، a میتونه تغییر کنه، readonly int a = 5 پویا نیست، a نمیتونه تغییر کنه. حالا این پویا بودن و نبودن ربطی به وابستگی بین Model و View داره، نه. چرا ربطی نداره؟ مثلا ()Model a = new Model میتونه بدون وجود کلاس Model کامپایل بشه؟ نمیتونه. و همین کد همونقدر به Model وابسته است که ()readonly Model a = new Model وابسته است، در اولی a پویا بود در دومی پویا نبود، ولی این پویا بودن و نبودنش تاثیری در اینکه همچین کدی موقع کامپایل به وجود Model نیاز داره ایجاد نمی کنه.
اما object a = NewObject("Model") رو در نظر بگیرید، تصور کنید NewObject متدی است که اسم یک کلاس رو میگیره و متد سازنده اش رو پیدا می کنه و بر اساسش یک شیء میسازه، کدش رو خودتون هم میتوانید بنویسید. حالا این کد موقع کامپایل به Model وابسته است؟ نیست، چرا نیست؟
چون "Model" فقط یک رشته string ئه، کامپایلر فقط کامپایلش می کنه، نه اجرا، این کد مادامی که اجرا نشه، دنبال کلاس "Model" نخواهد گشت. این کد بدون وجود کلاس Model کامپایل میشه و خطایی رخ نمیده، ربطی هم به این نداره که a پویا بود یا نبود.
برای readonly object a = NewObject("Model") هم وابستگی ایجاد نمیشه، a پویا هم نیست.
پس وابستگی ربطی به پویا بودن و نبودن نداره.

، بنابراین بدون هیچ دغدغه ای میتونیم حتی پروپرتی هایی که درون کلاس Model هستند را به پروپرتی های لایه ی View مون Binding کنیم (مثل Binding کردن پروپرتی Student.FirstName که این پروپرتیِ FirstName درون لایه ی Model هست ، به پروپرتی Text ئه TextBox در لایه ی View) .
بله.
اما من دقیق تفاوت معماریِ MVVM (مثلا همین پروژه) با پروژه هایی که معماری MVVM را پیاده سازی نمیکنن (و لایه ی ViewModel ندارند) را متوجه نشدم .
بیاید فرضا همین پروژه ی کوچیک را با حالتی که همین پروژه ، MVVM را پیاده سازی نکنه ، مقایسه کنیم :

درون کلاس StudentViewModel ، در متدی که برای Command با نام ChangeStudentMemberValueForCommand نوشته شد ، پروپرتی this.Student.FirstName به مقدار "Shahid Mohsen" و پروپرتی this.Student.LastName به مقدار "Hojaji" تغییر مقدار دادن .
یعنی اعضای کلاس Student که FirstName و LastName بودن ، مقداردهی و به روزرسانی شدن .
پس بنابراین ، اگه در به روزرسانیِ بعدی برنامه ، بیایم پروپرتیِ FirstName یا LastName را از کلاس Student حذف کنیم ، چون این کد موقع کمپایل کردن چک میشه ، پس با ارور ، برنامه دیگه اجرا نمیشه .
در صورتی که معماری MVVM هم اگه پیاده سازی نشه ، باز هم همینطوره .
مساله وابستگی است، نه اینکه نهایتا نیازی به Model سازگار نباشه، مزیت MVVM در عدم وابستگی است، نه اینکه کلا هر Model ای بتونه جای Model ناسازگار دیگری رو بگیره. اگر قرار بر حذف FirstName باشه که دیگه سازگاری معنی نداره. باید Model ای جایگزین مدل قبلی بشه که با طراحی قبلی سازگار باشه.
در این پروژه ، چون Command ها با Binding متصل میشن ، با حذف کردنِ Command مون (یعنی پروپرتیِ ChangeStudentMemberValueCommand و متد ChangeStudentMemberValueForCommand) ، اون View مون دیگه خطای زمان کامپایل نمیده و اجرا میشه (هر چند اگه Command مورد نظر را بهش مجددا Binding نکنیم ، خوب مشخص هست که مقداری نمیگیره و خطای Binding را زمان اجرا اون هم صرفا زمانی که برنامه با ویژال استودیو اجرا میشه را میده اما خطای زمان کامپایل هم نمیده) .

ولی پروژه ای که در واقع Command نداشته باشه (نه اینکه صرفا معماری MVVM داشته باشه) ، چون با رویداد متصل شد ، با حذف رویداد ، زمان کامپایل خطای عدم وجود هندلر برای رویداد را میگیره.
تفاوت شون فقط همینه؟
نه، ربطی به رخداد نداره. نمیدونم چرا گیر داده اید به رخداد. رخداد اصلا در مساله نقشی نداره.
در MVVM وابستگی بین Model و View نیست، یعنی هر View ای میتونه با View سازگار دیگری جایگزین بشه، هر Model ای میتونه با Model سازگار دیگری جایگزین بشه، میتونه وسط اجرا View به View دیگری تغییر کنه، یا وسط اجرا Model جایگزین بشه.
اما نکته ی مهم اینه که اولا زمانی که نتونیم از Command ها استفاده کنیم (مثلا زمانی که بخوایم از رویداد Mouse Enter در دکمه ای استفاده کنیم) و همچنین در اون رویداد به اعضای کلاس Model دسترسی داشته باشیم (مثل رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter برای دکمه ی MouseEnterChangeStudentMemberValueButton در پروژه که میخواد به پروپرتیِ Student.FirstName و Student.LastName دسترسی داشته باشه) ، در این صورت که نمیتونیم از Command استفاده کنیم ، برای اینکه معماری MVVM نقض نشه ، باید چی کار کنیم؟
چرا نمی توانید از Command استفاده کنید؟ نمی توانید در متد MouseEnterChangeStudentMemberValueButton_MouseEnter اون شیء Command مورد نظر تون رو بکار ببرید و متد Execute اش رو اجرا کنید؟ قطعا می توانید.
من متدی به نام MouseEnterChangeStudentMemberValueButtonMouseEnterMethod را در کلاس StudentViewModel ساختم و در رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter که در View ام بود ، این متد را فراخونی کردم چون اگه درکم از معماری MVVM درست بوده باشه ، برای دسترسی به اعضای کلاس Model ، باید از کلاس ViewModel بهش دسترسی داشته باشیم نه از کلاس View .
کاری که کردم ، درسته؟
بله.
اگه غلط هست ، چی کار باید کنم؟
غلط نیست. اما نیازی نیست که رخداد رو به متد متصل کنید :
XML:
<Window x:Class="MVVM_Practies.MainWindow"
        .
        .
        .
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:command="http://www.galasoft.ch/mvvmlight"
XML:
        <Button VerticalAlignment="Center" HorizontalAlignment="Center" Height="20" Width="100">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseEnter">
                    <command:EventToCommand Command="{Binding Path=ChangeStudentMemberValueCommand}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
در ضمن تصور کنید که بجای ChangeStudentMemberValueCommand ای که پارامتر نداره و میخواد خودش مقادیر ثابتی رو به FirstName و LastName بده، یک Command منعطف ای تعریف می کردید که دو تا پارامتر FirstName و LastName رو (بصورت آرایه یا فقط یک رشته ای که وسطش یک جداکننده خاص در نظر گرفته اید یا هر روش دلخواه دیگری) دریافت کنه و بتوانید در همون CommandParameter داخل XAML (با <x:Array> یا هر روش متناسب دیگری) اون دو مقدار FirstName و LastName رو مشخص کنید. که در واقع مقدار دهی FirstName و LastName رو در View انجام داده باشید و دیگه نخواهید برای هر مقدار ثابت FirstName و LastName یک متد اختصاصی در ViewModel اضافه کنید.
اگه درست هست ، خوب چون رویداد (ای را در لایه ی View که همون رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter هست) به دکمه ی مورد نظرمون متصل کردیم ، خوب این که با مانی که معماری MVVM پیاده سازی نشه ، فرقی نداره . چون در پروژه های بدون معماری MVVM ، بخوایم هندلر رویداد را حذف کنیم ، خطای زمان کمپایل میده و در معماری MVVM هم همینطور . یعنی با حذف هندلر رویداد MouseEnterChangeStudentMemberValueButton_MouseEnter در این پروژه ، خطای زمان کمپایل میده و پروژه اجرا نمیشه .
شما دارید چیزی رو در ViewModel تغییر می دهید و میگید چرا View نسبت بهش حساس و وابسته است.
قرار نیست چون MVVM ئه پس View به ViewModel وابسته نباشه، اینها قراره با هم در ارتباط باشند.
همونطور که ViewModel باید با Model در ارتباط باشه.
یا اینکه بجای این کار ، بهتره که در کلاس ViewModel ، یه پروپرتی ای از نوع View (که View مون در اینجا همون کلاس MainWindow هست) ایجاد کنیم و توسط این پروپرتی (یعنی درون کلاس ViewModel) ، رویدادِ MouseEnter را برای دکمه ی MouseEnterChangeStudentMemberValueButton متصل کنیم؟
این روش بهتره؟
نه، تفکیک درستی نیست. چیزی که مربوط به View است باید داخل View باشه، چیزی که مربوط به ViewModel ئه، داخل ViewModel. قرار نیست ViewModel کاری رو انجام بده که باید مربوط به View باشه. اگر قراره برای MouseEnter کاری انجام بشه خود View باید برایش فکری بکنه. اگر لازمه موقع MouseEnter برای مقدار دهی چیزی به ViewModel رجوع کنه کار خوبی کرده ولی ViewModel خودش نباید برای MouseEnter که به تعامل کاربر مربوطه فکری بکنه.
کلا هدف از معماری MVVM را بیشتر توضیح میدین؟ منظورم از لحاظ تئوری نیست . یعنی اینکه باعث نگه داری و به روزرسانی بهتر و جداسازی لایه ی View و Model میشه . منظورم از این لحاظ نیست .
هدف همونه، تئوری و عملی اش یکی ئه، اگر در عمل با تئوری جور در نمیومد که تئوری اشتباه بود و سراغش نمی رفتند. مزایا و معایب این جدا سازی رو هم که می توانید جستجو کنید.
 

the_king

مدیرکل انجمن
بله .
این رو بگم منظورم این بود که توی اون لینکی که داده بودم ، در کدی که داد :

XML:
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />

                    <GridViewColumn Header="Address" Width="200" DisplayMemberBinding="{Binding Address}"/>
                </GridView>

در کد بالا ، طرف اومد مستقیما به پروپرتی Name و Address که از اعضای کلاس Person (از لایه ی Model) اش بودن ، Binding انجام داد . در صورتی که DataContext ئه View اش را PersonViewModel (از لایه ی ViewModel) در نظر گرفت .
در لایه ی ViewModel ، پروپرتی Persons از نوع IList<Person> هست . نباید اول نام این پروپرتی را میگفت و بعد اعضای هر عضوی از این IList را که Name و Address بودن ، فراخونی میکرد؟

البته من Binding به کالکشن ها را نخوندم و احتمالا بخاطر همینه که قوانین Binding به اعضای کالکشن ها را بلد نیستم . درسته؟
خوب بخونیدش، Binding ها رو باید بشناسید، Binding در WPF لازمتون میشه.
سئوال دیگه در این زمینه اینکه در مقاله در لینک زیر :


در قسمت توضیحات "List Change Notification" ، اول یه پروپرتی ای بنام StringList از نوع List<string> در نظر گرفته .
بعد در متدی بنام AddList که متدی هست که به Command ئه AddListCommand معرفی شده (به عنوان متدی که زمان Execute ، اجرا بشه) ، میاد عضو جدیدی را به اعضای کالکشنِ StringList اضافه میکنه و بعد متد RaisePropertyChanged("StringList"); را برای اطلاع دادن به View اجرا میکنه .

اما در آخرِ همون بخش این نتیجه را میگیره که با اونکه متد RaisePropertyChanged را اجرا کرد و باعث شد تا به View اطلاع بده و بخش get ئه پروپرتیِ StringList را مجددا فراخونی کنه ، اما دقت نمیکنه که مدخل جدید و عضو جدیدی به لیست اضافه شد و بنابراین متوجه ی تغییرات (اضافه شدن عضو جدید) نمیشه . هر چند متوجه نشدم که چرا حتی با وجود اطلاع و اجرای متد RaisePropertyChanged ، متوجه ی اضافه شدنِ ورودی و عضو جدید به کالکشن نمیشه؟
قبلا براتون توضیح دادم، فرض کنید که من یک مشخصه به نام A دارم از نوع List. این A یک شیء رو به عنوان لیست بر میگردونه. کی این شیء تغییر می کنه؟ وقتی تعداد اعضاء اش تغییر کنه؟ نه. هر چقدر داخل لیست رو تغییر بدهید، لیست همون شیء قبلی ئه، پس چه زمانی مشخصه تغییر کرده؟ فقط زمانی که A با یک List دیگر (یا null) جایگزین بشه، فقط همین موقع. وقتی اعضاء این شیء List تغییر می کنند، عضوی اضافه یا کم بشه، اون شیء List همچنان همون شیء List ئه، اعضاء داخلش تغییر کرده اند، ولی خودش همون شیء لیست ئه، شی ای که به مشخصه مربوطه تغییر نکرده، مشخصه A تغییر نکرده. برای همین AddList به خودی خود منجر به تغییر مشخصه StringList و رخداد PropertyChanged نمیشه. PropertyChanged فقط حواسش به تغییر مقدار StringList ئه، کاری نداره به اینکه اعضاء این StringList کم و زیاد شده اند. برای همین فراخوانی RaisePropertyChanged لازمه.
نهایتا اشاره میکنه که برای استفاده از کالکشن (بجای استفاده از لیست یا هر چیز دیگه ای) ، از نوع ObservableCollection استفاده کنید که INotifyCollectionChanged را پیاده سازی کرده .
میخوام بپرسم که اگه ما اینترفیس INotifyCollectionChanged را پیاده سازی کنیم ، در این صورت میتونیم از نوع List و هر نوع کالکشن دیگه ای استفاده کنیم دیگه . درسته؟
آره ولی به خودی خود نه، کی کار می کنه؟ وقتی کسی که اون کلاس مورد نظر رو فرضا ObservableCollection (یا هر کلاس دیگری که میخواد مشابه این عمل رو انجام بده) طراحی کرده و داخلش INotifyCollectionChanged رو پیاده سازی کرده بیاد و در جایی که لازمه، CollectionChanged رو فراخوانی کنه. مثلا اگر من INotifyCollectionChanged رو در کلاسی پیاده سازی کنم و یادم بره در جایی که لازمه CollectionChanged رو فراخوانی کنم، اون موقع با کلاس من کار نخواهد کرد. این اینترفیس ها خود به خود کاری انجام نمی دهند، باید برنامه نویس درست ازشون استفاده کنه تا عمل کنند.
دلیل اینکه در اون لینک ، با فراخونی RaisePropertyChanged ، اطلاع از اضافه شدن عضو جدید به کالکشن ، درست کار نکرد ، دقیقا چی بود؟ چون کل کالکشن با مقدار جدیدی تعویض نشد . صرفا مقدار جدیدی به اعضاش اضافه شد و به طبع ، INotifyPropertyChanged باید جواب بده . این طور نیست؟
بله.
و واسه ی این در هر دو لایه ی ViewModel و Model ، اینترفیس INotifyPropertyChanged را پیاده سازی میکنن چون Binding ها به قول تون Late Binding هستن و زمان اجرای برنامه ، ارتباطات را چک میکنن ، پس میشه از کلاس View به کلاس های هم ViewModel و هم Model ، عمل Binding را انجام داد .
پس در واقع در هر لایه ای که (معمولا المنت های) View مون را به لایه ی مورد نظر Binding کردیم (لایه ی ViewModel یا Model) ، فقط در اون صورت لازمه که اینترفیس INotifyPropertyChanged را در اون لایه پیاده سازی کنیم . درسته؟
INotifyPropertyChanged بخاطر صرفا Binding نیست، بخاطر Binding در شرایطی است که داده تغییر می کنه و میخواد از تغییر مطلع بشه.
اگر تغییر داده رخ نده، INotifyPropertyChanged ای رخ نمیده و طبعا Binding اش هم نیازی به INotifyPropertyChanged نداره.
بله، ولی ViewModel چه نیازی به INotifyPropertyChanged داره؟ مگه عامل تغییر ئه که بخواد به View یا Model اطلاع بده که من مقداری رو تغییر دادم؟ ViewModel صرفا یک رابط ئه، قرار نیست خودش شخصا تصمیم به انجام کاری بگیره که بخواد به Model یا View اطلاع بده که من فلان مقدار رو تغییر دادم، همچین اختیاری نداره که سر خود تغییری بده، بدون اراده کاربر (از طرف View) یا بدون اراده منبع داده (از طرف Model) که نمیتونسته همچین تصمیمی رو سر خود بگیره، برای همین عامل تغییر یا Model ئه یا View و برای همین INotifyPropertyChanged رو در Model می بینید، نه ViewModel. ممکنه همچین شرایطی رو در کدی ایجاد کنید ولی من مثالی برای این شرایط سراغ ندارم.
یعنی اگه المنت های View مون را به پروپرتی های لایه ی ViewModel مون Binding کردیم ، در اون صورت لازمه که برای کلاس های ViewModel مون ، اینترفیس INotifyPropertyChanged را پیاده سازی کنیم.
اگر مشخصه هایی در ViewModel دارید که تغییر می کنند بله، اما ابهام اینجا است، ViewModel که فقط یک رابط بین View و Model ئه بر چه اساسی باید مقدار مشخصه هاش رو تغییر بده که بخواد به View تغییر رو اطلاع بده؟ ViewModel رو شما می توانید عامل تغییر تصور کنید؟ کاربر اینور داره با سیستم کار می کنه بعد از اونور ViewModel بخواد به View اطلاع بده که من از قیافه این کاربر خوشم نیومد و داده هاش رو در Model پاک می کنم؟ من دنبال مثال برای این شرایط هستم، نه اینکه بگم اشتباه ئه، یا بگم محال ئه همچین شرایطی پیش بیاد، صرفا مثال کاربردی برایش سراغ ندارم.
در این صورت لازم نیست برای کلاس های Model مون ، این اینترفیس را پیاده سازی کنیم .
Model میتونه در اون set اش عامل تغییر داده باشه، ربطی به کاری که در ViewModel یا View می کنید نداره. تصور کنید که Model ارتباط اش با سرور قطع شده، مجددا بهش وصل شده و داده ها رو با وضعیت آخرین نگارش مجددا میخونه، باید به ViewModel تغییر رو اطلاع بده. حالا اینکه در ViewModel چه کردید و چه نکردید ربطی به این مساله در Model نداره که بخواهید بگید چون در ViewModel فلان کردم پس در Model اینترفیس رو حذف می کنم، به کاری که در ViewModel می کنید ربطی نداره.
اگه هم به پروپرتی های لایه ی Model مون Binding انجام دادیم ، در این صورت اینترفیس INotifyPropertyChanged را فقط برای لایه ی Model مون پیاده سازی میکنیم و پیاده سازی اش برای لایه ی ViewModel لازم نیست .
بله، ولی به این فکر کنید که چرا دارید INotifyPropertyChanged رو می نویسید. برای اینکه وقتی مقدار مشخصه تغییر کرد، بخواهد با INotifyPropertyChanged اطلاعش بده، پس فکر کنید که کی بهش نیاز هست و کی بهش نیازی نیست. بله، طبعا ربطی به پیاده سازی INotifyPropertyChanged در ViewModel نداره.
 

the_king

مدیرکل انجمن
اگه هم به هر دو لایه ی ViewModel و Model مون Binding کردیم که اینترفیس INotifyPropertyChanged را برای هر دوی این لایه ها پیاده سازی میکنیم .
درست میگم؟
فقط به این خاطر که Binding کرده اید؟ خیر. اما اگر داده تغییر می کنه و طبعا میخواهید Binding با تغییر داده درست کار کنه، بله.
تصور کنید که در ViewModel مشخصه هایی دارید، بهشون Binding در View هم دارید ولی ViewModel تحت هیچ شرایطی مقدار مشخص اش رو تغییر نمیده، INotifyPropertyChanged اش به چه درد Binding میخوره وقتی هیچوقت تغییری در مشخصه رخ نخواهد داد که بخواهد اطلاعش بدهد؟
اگر همچین شرایطی که میگید باشه بله، ولی مثالی از این وضعیت به ذهنم نمیرسه که ViewModel بخواد به جایی اطلاع بده که من مقداری رو تغییر دادم.
بنابراین در پروژه ای که دادم چون از View هم به پروپرتی های Student.FirstName و Student.LastName (که در لایه ی Model هستند) و هم به پروپرتی ChangeStudentMemberValueCommand (که در لایه ی ViewModel هست) ، Binding انجام دادیم ، پس باید هر دو کلاس Model و ViewModel با هم اینترفیس INotifyPropertyChanged را پیاده سازی کنن . درسته؟
در این مثالی که دارید نه. ChangeStudentMemberValueCommand در حین اجرا تغییر داده میشه؟ نه. پس لزومی نداره برای تغییرش به View اطلاع داده بشه، چون اصلا تغییر نمی کنه، یک مقدار اولیه داره که تا آخر هم همونه. اما اگر تغییر می کرد، یا قراره کمپوننت ای بشه که در جای دیگری با شرایط قابل تغییری استفاده خواهد شد، بله.

این به این معناست که اگه ما بجای ذخیره ی مقادیر پروپرتی ها درون فیلدها ، از DependencyProperty استفاده کنیم ، دیگه نیاز به پیاده سازی اینترفیس INotifyPropertyChanged در کلاس مورد نظر نداریم؟
بله. یکی از سناریو هایی که استفاده از Dependency properties کاربرد پیدا می کنه همین Data Binding ئه.
البته فرق خاصی نباید داشته باشه . داره؟
از نظر پیاده سازی نه، ولی فرق اساسی اش اینه که DependencyProperty قابلیت های بیشتری داره و محدود به صرفا همین یک کاربرد نیست، نه فقط برای یک اطلاع رسانی تغییرات و بس.
چون فرض کنید دو تا دکمه داریم . Content ئه دکمه ی 1 ، به پروپرتیِ Property_1 در کلاس ViewModel مون Binding کردیم .
در رویدادی از دکمه ی 2 بخوایم مقدار Property_1 در کلاس ViewModel را تغییر بدیم ، باز باید Property_1 مون به Binding مون اطلاع بده که مقدارش تغییر کرد تا مقدار Content ئه دکمه ی 1 تغییر کنه دیگه .
بله.
پس Binding ها هم مثل foreach و IEnumerable و ... ، در کلاس مورد نظر میگردن ببینن که آیا اون کلاس ، اینترفیس مورد نظرشون را پیاده سازی کرده یا نه تا از اونها و اعضای پیاده سازی شده شون استفاده کنن .
بله.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بله.

بله.


پویا یعنی چی؟ یعنی مستعد تغییر ئه، میتونه تغییر کنه. int a = 5 مستعد تغییر ئه، a میتونه تغییر کنه، readonly int a = 5 پویا نیست، a نمیتونه تغییر کنه. حالا این پویا بودن و نبودن ربطی به وابستگی بین Model و View داره، نه. چرا ربطی نداره؟ مثلا ()Model a = new Model میتونه بدون وجود کلاس Model کامپایل بشه؟ نمیتونه. و همین کد همونقدر به Model وابسته است که ()readonly Model a = new Model وابسته است، در اولی a پویا بود در دومی پویا نبود، ولی این پویا بودن و نبودنش تاثیری در اینکه همچین کدی موقع کامپایل به وجود Model نیاز داره ایجاد نمی کنه.
اما object a = NewObject("Model") رو در نظر بگیرید، تصور کنید NewObject متدی است که اسم یک کلاس رو میگیره و متد سازنده اش رو پیدا می کنه و بر اساسش یک شیء میسازه، کدش رو خودتون هم میتوانید بنویسید. حالا این کد موقع کامپایل به Model وابسته است؟ نیست، چرا نیست؟
چون "Model" فقط یک رشته string ئه، کامپایلر فقط کامپایلش می کنه، نه اجرا، این کد مادامی که اجرا نشه، دنبال کلاس "Model" نخواهد گشت. این کد بدون وجود کلاس Model کامپایل میشه و خطایی رخ نمیده، ربطی هم به این نداره که a پویا بود یا نبود.
برای readonly object a = NewObject("Model") هم وابستگی ایجاد نمیشه، a پویا هم نیست.
پس وابستگی ربطی به پویا بودن و نبودن نداره.

سلامی مجدد
واقعا خیلی ممنون استاد از توضیحات مفصل تون . :rose:
خیلی از نکات برام روشن شد . هر چند اندکی هنوز مونده که در حال پیگیری و پرسش ام . :)


غلط نیست. اما نیازی نیست که رخداد رو به متد متصل کنید :
XML:
<Window x:Class="MVVM_Practies.MainWindow"
        .
        .
        .
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:command="http://www.galasoft.ch/mvvmlight"
XML:
        <Button VerticalAlignment="Center" HorizontalAlignment="Center" Height="20" Width="100">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseEnter">
                    <command:EventToCommand Command="{Binding Path=ChangeStudentMemberValueCommand}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
در ضمن تصور کنید که بجای ChangeStudentMemberValueCommand ای که پارامتر نداره و میخواد خودش مقادیر ثابتی رو به FirstName و LastName بده، یک Command منعطف ای تعریف می کردید که دو تا پارامتر FirstName و LastName رو (بصورت آرایه یا فقط یک رشته ای که وسطش یک جداکننده خاص در نظر گرفته اید یا هر روش دلخواه دیگری) دریافت کنه و بتوانید در همون CommandParameter داخل XAML (با <x:Array> یا هر روش متناسب دیگری) اون دو مقدار FirstName و LastName رو مشخص کنید. که در واقع مقدار دهی FirstName و LastName رو در View انجام داده باشید و دیگه نخواهید برای هر مقدار ثابت FirstName و LastName یک متد اختصاصی در ViewModel اضافه کنید.

خیلی ممنون استاد . کد خیلی خوبی دادید . در واقع با این میشه یکی از مشکل مهم command ها را تا حد زیادی پوشش داد .
البته در حاشیه بگم که من هنوز متوجه نشدم که این جستجوها را چجوری انجام میدین . یعنی فرضا چجوری متوجه شدین که برای این کار ، یه کلاسی بنام EventToCommand در mvvmlight وجود داره؟
اسناد mvvmlight بسیار ضعیفه . حداقل من چیزی پیدا نکردم (اگه شما پیدا کردین ، لینک بدید ممنون میشم) . من صرفا از توی سایت github ، صرفا کدهای نرم افزار را میبینم که توضیحات اعضا هم توی همون کدها هست مثل این :


اما سایتی باشه که اسناد را ارائه کنه و برای هر عضو یا کلاس و ... ، توضیحاتی ارائه کنه را ندیدم . هر چند سایت خودش هست :


اما این طور نیست که مثل مایکروسافت کلاس ها و اعضا اش را لیست کرده باشه و توضیح داده باشه .

-----------------------------------------

حالا من پروژه ی قبلی را یه چیزهای دیگه ای بهش اضافه کردم که در زیر پیوست کردم .
همونطور که در توضیحات تون گفتید ، در دکمه ی آخری ، یه x:Array برای پروپرتیِ EventToCommand.CommandParameter ساختم .
اما مشکل اینجاست که در هندلر (متد) مربوط به این command ، یعنی درون MyParameteredCommandHandler ، چجوری به مقدار این پروپرتیِ EventToCommand.CommandParameter دسترسی داشته باشم؟

اگه برای EventToCommand ، یک نام (x:Name) در نظر بگیرم و بعدا در متد MyParameteredCommandHandler بهش دسترسی داشته باشم که کار خراب میشه (یعنی مناسب برای این هدف نیست . نه اینکه نشه چون برای چند تا دکمه ی متفاوت میخوام پروپرتیِ CommandParameter شون را مقداردهی کنم) .

اگه یه پروپرتی (از نوع object) در StudentViewModel بسازم و پروپرتیِ EventToCommand.CommandParameter را بهش binding کنم (مثلا binding ئه دو طرفه یا یک طرفه از source به سمت target) ، در این صورت مشکل اینه که باز چجوری به CommandParameter مقدار بدم؟
در این صورت مثلا علاوه بر اینکه برای این دکمه کد زیر را نوشتم :

XML:
        <Button Margin="10, 205, 0, 0" Padding="5, 2" HorizontalAlignment="Left" VerticalAlignment="Top" Height="28" Content="Parametered Comand Button 1">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseEnter">
                    <command:EventToCommand Command="{Binding Path=MyParameteredCommand}">
                        <command:EventToCommand.CommandParameter>
                            <x:Array Type="{x:Type system:String}">
                                <system:String>Shahid Hassan</system:String>
                                <system:String>Daneshgar</system:String>
                            </x:Array>
                        </command:EventToCommand.CommandParameter>
                    </command:EventToCommand>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>

و رویداد MouseEnter را برای EventTrigger اش در نظر گرفتم ، علاوه بر این ، رویداد MouseEnter را برای Button هم براش هندلر ای در view بسازم و به CommandParameter ، مقدار بدم؟
این جوری ، این مشکل بودجود نمیاد که رویداد MouseEnter برای Button ، با رویدادِ MouseEnter ای که در i:EventTrigger (برای Button) سِت کردم ، تداخل بودجود بیاد براشون و هندلرِ Button ای که در View متصل کردیم ، بعد از این رویداد که در i:EventTrigger مشخص کردیم اجرا بشه یا هر تداخل دیگه ای؟

کلا با چه روشی میشه مقدار CommandParameter را خوند یا احیانا مقداردهی کرد؟

----------------------------------------------

سئوال دیگه اینکه در کد بالا ، اون تگ <i:Interaction.Triggers> ، اون پروپرتیِ Triggers ، از نوع attached property هست؟
چرا مستندات کلاس system.windows.interactivity.interaction و اعضاش ، یه جوری هست؟ یعنی توضیحات خاصی نداره . مثلِ مثلا المنت های wpf نیست که خوب مستندسازی کرده باشه .
البته نوشته اسناد مربوط به Expression Blend SDK for Silverlight هست که دقیق نمیدونم چیه.

ادامه در پست دیگه .
 

پیوست ها

  • MVVM_Practies.rar
    1.9 مگایابت · بازدیدها: 1
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بله. کلا رویداد مورد خاص و منحصر بفردی نیست که برایش قاعده یا استثناء در نظر گرفته بشه، هر کدی که در View می نویسید چه مربوط به متد رخداد باشه و چه نباشه نباید به Model وابسته باشه، چون اگر وابسته باشه در زمان کامپایل کد View رو به Model وابسته می کنه.

بله . حواسم نبود .


چرا نمی توانید از Command استفاده کنید؟ نمی توانید در متد MouseEnterChangeStudentMemberValueButton_MouseEnter اون شیء Command مورد نظر تون رو بکار ببرید و متد Execute اش رو اجرا کنید؟ قطعا می توانید.

بله . البته منظورم اتصال رویدادها به هندلرشون بود که تقریبا جواب دادین .

نه، ربطی به رخداد نداره. نمیدونم چرا گیر داده اید به رخداد. رخداد اصلا در مساله نقشی نداره.
در MVVM وابستگی بین Model و View نیست، یعنی هر View ای میتونه با View سازگار دیگری جایگزین بشه، هر Model ای میتونه با Model سازگار دیگری جایگزین بشه
، میتونه وسط اجرا View به View دیگری تغییر کنه، یا وسط اجرا Model جایگزین بشه.

مساله وابستگی است، نه اینکه نهایتا نیازی به Model سازگار نباشه، مزیت MVVM در عدم وابستگی است، نه اینکه کلا هر Model ای بتونه جای Model ناسازگار دیگری رو بگیره. اگر قرار بر حذف FirstName باشه که دیگه سازگاری معنی نداره. باید Model ای جایگزین مدل قبلی بشه که با طراحی قبلی سازگار باشه.

شما دارید چیزی رو در ViewModel تغییر می دهید و میگید چرا View نسبت بهش حساس و وابسته است.
قرار نیست چون MVVM ئه پس View به ViewModel وابسته نباشه، اینها قراره با هم در ارتباط باشند.
همونطور که ViewModel باید با Model در ارتباط باشه.


به نظرم یکی از مهمترین نکات ، اون نکته ای که بولد کردم و شما گفته بودین ، هست .
اول اینکه رویداد نقشی در mvvm نداره .
همونطور که RelayCommand را به یه متد (دلیگیتِ Action) متصل میکنیم ، رویداد هم صرفا به یک دلیگیت متصل میکنیم و بنابراین هیچ فرقی ندارن .

نکته ی مهم دیگه که گفتید اینکه View میتونه با View ی سازگار (نه با هر View ای بلکه با View ی سازگار) در mvvm جایگزین بشه . همچنین Model هم میتونه با هر Model ئه سازگار (نه با هر Model ای بلکه با Model ئه سازگار) جایگزین بشه .

البته ، این ، هدف mvvm هست .
در واقع در mvvm که در لینک زیر گفت :


The key benefit is allowing true separation between the View and Model beyond achieving separation and the efficiency that you gain from having that. What that means in real terms is that when your model needs to change, it can be changed easily without the view needing to and vice-versa.

در mvvm ، وقتی که model مون را تغییر بدیم ، دیگه نیازی نیست که view مون را تغییر بدیم .
اگه view مون را تغییر بدیم ، نیازی نیست که model مون را تغییر بدیم .
که در نتیجه باعث میشه که بتونیم model مون را با model ئه سازگار دیگه ای (و همچنین view مون را با view سازگار دیگه ای) جایگزین کنیم .

اما به این معنی نیست که با تغییر Model یا View ، نیازی نیست که ViewModel مون را هم تغییر بدیم . بلکه در این صورت ، ViewModel مون نیاز به تغییر داره .

درست میگم؟

اما همونطور که قبلا گفتم ، این قضیه را در سطح تقریبا تئوری یاد گرفتم . وقتی این قضیه را در سطح کدها توی ذهنم بررسی کردم (پروژه ی mvvm با پروژه ی غیر mvvm) ، وقتی که در mvvm فرضا اعضای model ای را تغییر میدیم (حذف میکنیم یا نوع اش را تغییر میدیم) ، دیدم عمده تفاوت شون اینه که در mvvm (بجز زمانی که binding میکنیم ، باز اون هم بجز command هایی که سفارشی میسازیم) ، در ازای تغییر اعضای Model ، باید ViewModel مون را باید تغییر بدیم اما در معماریِ غیرِ mvvm ، همین تغییر را منتها در View اش باید بدیم .
همچنین در ازای تغییر View ، باز هم باید ViewModel مون را تغییر بدیم .

یعنی از لحاظ تغییر دادن ، فرقی ندارن منتها در جایی که باید تغییر بدیم ، یعنی اینکه این تغییر در سطح View باشه یا ViewModel ، فرق دارن .
اگه ViewModel مون را تغییر ندیم که اصلا ارور کمپایلر میده و برنامه اجرا نمیشه .


نه، تفکیک درستی نیست. چیزی که مربوط به View است باید داخل View باشه، چیزی که مربوط به ViewModel ئه، داخل ViewModel. قرار نیست ViewModel کاری رو انجام بده که باید مربوط به View باشه. اگر قراره برای MouseEnter کاری انجام بشه خود View باید برایش فکری بکنه. اگر لازمه موقع MouseEnter برای مقدار دهی چیزی به ViewModel رجوع کنه کار خوبی کرده ولی ViewModel خودش نباید برای MouseEnter که به تعامل کاربر مربوطه فکری بکنه.

بله متوجه شدم .
---------------------------------------------
میگم استاد ، استفاده کردن یا نکردن از binding (و در نتیجه command ها) ، باعث نقض کردن mvvm نمیشه . یعنی اگه از binding ها استفاده نکنیم ، باعث نمیشه که mvvm نقض بشه . mvvm صرفا میگه که view با model نباید رابطه ی مستقیمی با هم داشته باشند ، حالا میخوایم از binding و command ها استفاده کنیم یا نه .
اما استفاده ی حداکثری از binding ها ، باعث کارایی بیشتر در mvvm میشه .
درست میگم؟

البته من زیاد طرفدار استفاده از command های شخصی سازی شده نیستم (منظورم command هایی هست که در متد execute شون کد مینویسیم یا به دلیگیت Action متصل شون میکنیم) . هر چند استفاده ازشون باعث میشه چون دلیگیت Action توی ViewModel میره ، اگه حذف شون هم کنیم ، باعث میشه فقط لایه ی ViewModel را تغییر بدیم . و تغییرات در View را کمتر میکنه . اما نسبت به رویداد ، اندکی دردسرهای بیشتر داره مخصوصا اگه بخوایم پارامتری ارسال کنیم .
کدها که بیشتر بشن و در پروژه ی بزرگ ، پیچیدگی کدهاشون بیشتر میشه به نظرم .
البته شاید خودم هم در پروژه ام از command ها استفاده کنم (شاید هم نه) ولی کلا پیچیدگی اش در پروژه های بزرگ ، زیادتر میشه مخصوصا موقع ارسال پارامتر .
نظر شما چیه؟


خیلی ممنون استاد از توضیحات مفصل تون .
همچنین از پست های دیگه تون که راهنمایی بسیار خوبی بهم کردین :rose:
 
آخرین ویرایش:

the_king

مدیرکل انجمن
سلامی مجدد
واقعا خیلی ممنون استاد از توضیحات مفصل تون . :rose:
خیلی از نکات برام روشن شد . هر چند اندکی هنوز مونده که در حال پیگیری و پرسش ام . :)




خیلی ممنون استاد . کد خیلی خوبی دادید . در واقع با این میشه یکی از مشکل مهم command ها را تا حد زیادی پوشش داد .
البته در حاشیه بگم که من هنوز متوجه نشدم که این جستجوها را چجوری انجام میدین . یعنی فرضا چجوری متوجه شدین که برای این کار ، یه کلاسی بنام EventToCommand در mvvmlight وجود داره؟
برای هر چیزی که جستجو نمی کنم اما به هر حال کلمات کلیدی ای مثل wpf mvmlight event command قاعدتا باید برای جستجو مناسب باشه.

حالا من پروژه ی قبلی را یه چیزهای دیگه ای بهش اضافه کردم که در زیر پیوست کردم .
همونطور که در توضیحات تون گفتید ، در دکمه ی آخری ، یه x:Array برای پروپرتیِ EventToCommand.CommandParameter ساختم .
اما مشکل اینجاست که در هندلر (متد) مربوط به این command ، یعنی درون MyParameteredCommandHandler ، چجوری به مقدار این پروپرتیِ EventToCommand.CommandParameter دسترسی داشته باشم؟
کار تون یک ایرادی داره، از RelayCommand استفاده کرده اید که پارامتر نداره، برای همین با این مشکل مواجه شده اید. شرح RelayCommand رو بخونید :
public class RelayCommand
Member of GalaSoft.MvvmLight.CommandWpf

Summary:
A command whose sole purpose is to relay its functionality to other objects by invoking delegates. The default return value for the CanExecute method is 'true'. This class does not allow you to accept command parameters in the Execute and CanExecute callback methods.​
بجایش باید از <RelayCommand<T استفاده می کردید.
اگه برای EventToCommand ، یک نام (x:Name) در نظر بگیرم و بعدا در متد MyParameteredCommandHandler بهش دسترسی داشته باشم که کار خراب میشه (یعنی مناسب برای این هدف نیست . نه اینکه نشه چون برای چند تا دکمه ی متفاوت میخوام پروپرتیِ CommandParameter شون را مقداردهی کنم) .
بله. روش درستی نیست.
اگه یه پروپرتی (از نوع object) در StudentViewModel بسازم و پروپرتیِ EventToCommand.CommandParameter را بهش binding کنم (مثلا binding ئه دو طرفه یا یک طرفه از source به سمت target) ، در این صورت مشکل اینه که باز چجوری به CommandParameter مقدار بدم؟
اگر کار رو به درستی انجام بدهید، پارامتری که به اون Execute ارسال میشه (یا همون متد <Action<T در <RelayCommand<T) همون مقدار Bind شده است.

سئوال دیگه اینکه در کد بالا ، اون تگ <i:Interaction.Triggers> ، اون پروپرتیِ Triggers ، از نوع attached property هست؟
بله، یک TriggersProperty از نوع DependencyProperty ئه که بصورت static تعریف شده.
چرا مستندات کلاس system.windows.interactivity.interaction و اعضاش ، یه جوری هست؟ یعنی توضیحات خاصی نداره . مثلِ مثلا المنت های wpf نیست که خوب مستندسازی کرده باشه .
چون جزئی از WPF نیست، صرفا بخشی از یک SDK محصول مجزایی بوده که مایکروسافت دیگه توسعه اش نمیده و خود محصول منسوخ شده چه برسه به SDK و مستنداتش. برای همین اون بخش XAML Behaviors که مورد توجه کاربران قرار گرفته بود بصورت یک پکیج جداگانه توسعه داده میشه که قاعدتا مستنداتش در حدی خواهد بود که دنبال کننده هایش زحمت تهیه اش رو بکشند :
البته نوشته اسناد مربوط به Expression Blend SDK for Silverlight هست که دقیق نمیدونم چیه.
Silverlight هم مثل Adobe Flash با اومدن HTML5 منسوخ شد، برای همین الزاما برای افزونه هاش مستند سازی کاملی پیدا نمیشه.
 

the_king

مدیرکل انجمن
اما به این معنی نیست که با تغییر Model یا View ، نیازی نیست که ViewModel مون را هم تغییر بدیم . بلکه در این صورت ، ViewModel مون نیاز به تغییر داره .
درست میگم؟
اگر Model یا View تغییر یافته همون ویژگی های نسخه قبلی رو داشته باشند طبعا ViewModel هم نیازی به تغییر نداره، اما فرضا اگر قابلیت جدیدی به قبلی اضافه شده باشه احتمالش هست که ViewModel هم تغییراتی داشته باشه، الزاما تغییر نمی کنه ولی ممکنه لازم باشه.
میگم استاد ، استفاده کردن یا نکردن از binding (و در نتیجه command ها) ، باعث نقض کردن mvvm نمیشه . یعنی اگه از binding ها استفاده نکنیم ، باعث نمیشه که mvvm نقض بشه . mvvm صرفا میگه که view با model نباید رابطه ی مستقیمی با هم داشته باشند ، حالا میخوایم از binding و command ها استفاده کنیم یا نه .
اما استفاده ی حداکثری از binding ها ، باعث کارایی بیشتر در mvvm میشه .
درست میگم؟
مشخص نکرده اید که اگر از Binding استفاده نکنید بجایش چه کاری می کنید. اون کاری که انجام می دهید تعیین می کنه که قواعد MVVM نقض میشه یا نه.
البته شاید خودم هم در پروژه ام از command ها استفاده کنم (شاید هم نه) ولی کلا پیچیدگی اش در پروژه های بزرگ ، زیادتر میشه مخصوصا موقع ارسال پارامتر .
نظر شما چیه؟
نظر خاصی ندارم.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد استاد .
خیلی ممنون از توضیحات خیلی خوب تون . :rose:

یه ListView ای هم به این پروژه اضافه کردم که در این پست پیوست میکنم، این آخرین تغییراتش بود . بی زحمت چک میکنین که همه چیز برای MVVM درسته یا نه؟


درباره ی اون پیاده سازی کردن اینترفیس INotifyPropertyChanged برای کلاس های لایه ی ViewModel ، زمانی که شی جدیدی به پروپرتی هایی که در StudentViewModel هستند و بهشون binding کردیم (و میخوایم به binding مون این تغییر را اطلاع بدیم) ، لازم هست .
همونطور که زمانی که شی جدیدی به پروپرتی هایی که در Model هستند و بهشون binding کردیم (و میخوایم به binding مون این تغییر را اطلاع بدیم) ، لازم هست .
در متد MouseEnterChangeStudentMemberValueButtonMouseEnterMethod در StudentViewModel ، شی جدیدی به پروپرتی Student داده شد (که نیاز به اطلاع رسانی به binding در set ئه این پروپرتی هست) و بعد اعضاش مقداردهی شدن .


قبلا درباره ی این میگفتم که تعجب میکنم که چرا در کالکشن ها ، (زمانی که میخوان آیتم های مربوط به ListView و اینها را مقداردهی کنن) ، مستقیما فرضا اعضای کلاس Student ها را برای اون آیتم ها به عنوان binding نام میبردن .
جوابش اینجاست :


WPF automatically assigns the DataContext of the ListBoxItem and all it's contents to the corresponding instance of the Data Item.

چون wpf ، مقدار DataContext ئه آیتم های یه ItemsControl (مثل ListBox یا ListView و ...) (یعنی اشیاهایی که به عنوان آیتم در پروپرتیِ Items یا ItemsSource و ... که تعیین میکنیم) را متفاوت از خودِ اون کنترل های ItemsControl (مثل ListBox یا ListView و ...) که تنظیم کردیم ، مقداردهی میکنه .

یعنی در مثال این پروژه :

XML:
        <ListView Margin="0, 240, 0, 0" ItemsSource="{Binding Path=StudentsForListView}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Width="200" Header="First Name" DisplayMemberBinding="{Binding FirstName}" />
                        <GridViewColumn Width="200"  Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

درسته که DataContext ئه ListView را شی ای از StudentViewModel تنظیم کردیم و هست اما wpf بصورت اتوماتیک ، آیتم های این ListView که همون GridViewColumn هستند را به شی ای که در جنریکِ Source (پروپرتیِ ItemsSource) اش که مشخص کردیم ، یعنی به شیِ Student مقداردهی میکنن .
درست میگم؟

همچنین خیلی ممنون درباره ی اون نکته ی استفاده از متد جنریکِ RelayCommand برای ارسال مقدار پارامتر (پروپرتیِ CommandParameter ئه EventToCommand) به متدمون در ViewModel .
و همچنین بقیه ی راهنمایی هاتون .
تشکر :rose:
 

پیوست ها

  • MVVM_Practies.rar
    1.9 مگایابت · بازدیدها: 1

جدیدترین ارسال ها

بالا