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

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد .

اینها را قبلا گفتید . میدونم .
الان شما میگید که کار کردن با کنترل ها ، فقط وظیفه ی View هست و نباید شی ای از کنترل ها به ViewModel ارسال بشه . بلکه نهایتا باید داده (شون) ارسال بشه .
بنابراین اگه کنترلی از View به ViewModel ارسال بشه ، MVVM را نقض کرده . درسته؟ (تا اینجا حرف شماست) .
بله.
بنابراین به طبع ، باید حالت برعکسش هم وجود داشته باشه . یعنی مواردی باشن که اگه در ایجادِ ارتباط برقرار کردن بین لایه ی ViewModel با Model انجام بشه و شی ای بین شون ارسال بشه، باعث نقضِ MVVM بشه . درست میگم؟
بله، منطقی ئه، فرضا چیزی که مربوط به وظایف اختصاصی Model ئه، مثلا SqlConnection یا SqlTransaction و موارد مشابه که داده نیستند و مربوط به وظایف Model اند نباید داخل ViewModel بشوند.
(چون مواردی وجود داره که اگه در ارتباط برقرار کردن بین ViewModel با View انجام بدیم ، یعنی اگه شیِ کنترل را از View به ViewModel ارسال کنیم ، باعث نقض MVVM شدیم ، پس باید در ارتباط با ViewModel با Model هم همچین مواردی وجود داشته باشه) .
بله. خلاصه اش اینه که هر چیزی که تقسیم وظایف این سه جزء رو بهم بزنه ناقض قواعد MVVM ئه.
اگه درست میگم پس این موارد چه چیزهایی هستن (در ارتباط برقرار کردن بین ViewModel با Model که اگه انجام بدیم و شی ای را بین شون ارسال کنیم ، باعث نقض MVVM میشیم)؟
چیزهایی که وظایف ViewModel ئه و در Model رخ داده یا چیزهایی که وظایف Model ئه و در ViewModel رخ داده، اینکه نکته مبهمی نداره، نیازی به ذکر جز به جز هم نداره، هزاران نوع شیء هست که ممکنه مربوط به وظایف یکی از این اجزاء بشه، دلیلی نداره که بخواهیم هزاران شیء رو موردی بررسی کنیم که انتقال کدوم شون بین فلان و بهمان قواعد تفکیک وظایف رو نقض می کنه و کدومشون نمی کنه، قواعد مشخص ئه، بر اساس کارکرد هر شیء مشخص میشه.
اگه همچین مواردی در ارتباط برقرار کردن بین ViewModel با Model وجود نداره که باعث بشه MVVM نقض بشه ، پس چطور در ارتباط با View و ViewModel وجود داره؟
بودنش که هست، ولی استدلال تون منطقی نیست، یعنی در قواعد معماری ها تقارن رابطه ها الزامی نیست، ممکنه در یک معماری قاعده ای برای جزء ای باشه که در جزء دیگری مصداق مشابهش نباشه. دلیلی نداره که حتما همچین تقارنی باشه، چه در قواعد و چه در روابط. شما دو تا گوش دارید، دو تا چشم دارید، دو تا ابرو دارید، دو تا گونه دارید، دو تا دست دارید، دو تا پا دارید، دو تا کلیه دارید، بعد به قلب که رسید شد یکی؟ به انگشتان که رسید شد پنج تا؟
خیلی ممنون .
من بیشتر میخوام منبعی برای این قضیه پیدا کنم .
منبعی برای چی پیدا کنید؟ باید بدونید چطور ازش استفاده کنید، نه اینکه چطوری نوشته شده. برنامه نویسان در سراسر دنیا از Binding استفاده می کنند، نیازی هم ندارند که بدونند کدش چیه و چطور کار می کنه. اگر بخواهید بدونید چطور نوشته شده هم منبع مشخص ئه، کد اش. کد اش رو بررسی می کنید و متوجه می شوید چطور نوشته شده و کار می کنه. چه منبعی بهتر از کد اش میتونسته وجود داشته باشه؟
یعنی شما گفتید که یه Binding ، میتونه به همه ی اشیاء Binding ئه دیگه ای که Source (یا همون DataContext) ئه مشترک دارن ، اطلاع از تغییر مقدارش را بده ؛ منبع یا مقاله ای در این باره میخوام .
نمیدونم چرا کسی باید در مورد هر متد یا رخداد مقاله ای بنویسه، کلاس x زمانی میتونه و قطعا میتونه به سایر اشیاء خارجی تغییری که ازش مطلع هست رو اطلاع بده که یک رخداد داشته باشه که موقع بروز تغییر Invoke اش کنه، Raise اش کنه و سایر اشیاء خارجی که میخوان مطلع بشوند هم به اون رخداد متد متصل کنند. این نیازی به مقاله و منبع خاص برای کلاس x هم نداره، چون بدیهیات زبان #C ئه که وقتی اون رخداد فراخوانی شد، همه اون متد ها متصل اجرا می شوند و باخبر می شوند که تغییری رخ داده. کلاس Binding رو هم که در سایت مایکروسافت ببینید، دقیقا همون رخداد های مورد نیاز رو داره، هم میدونه منبع داده چیه، هم میدونه مشخصه هدف چیه و ... پس فقط کافیه سر هر Binding اون متد ها به رخداد مناسب متصل بشه تا به محض تغییر مطلع بشوند.
منظورم اینه که شما از کجا میگید؟ در مقاله یا جایی خوندید یا اینکه مثلا در چند جا ، چیزهای متفاوتی درباره ی Binding خوندین که بعد جمع بندی کردین و به این نتیجه رسیدین یا چیز دیگه ای هست؟
نه، اینها اصلا روش خوبی نیست. شناخت تکنولوژی های نرم افزاری با روش های حدسی، فرضی، تجربی و بر پایه جمع بندی نظریات شهرام و بهرام و حسن و حسین و ... منطقی نیست. نرم افزار مثل حیات وحش نیست که با چیزی طرف باشیم که خالق اش انسان نباشه و ندونیم قواعدش چیه و چطور کار می کنه و مجبور باشیم با آزمایش و جمع بندی و خوندن نظریات و تحلیل تجربیات و ... به نتیجه برسیم که فرضا زنبور چطور کندو اش رو شناسایی می کنه، نه، اینها برای علم کامپیوتر مناسب نیست. قبلا هم بهتون گفتم، نرم افزار رو انسان نوشته، یک ساختار عجیب و ناشناخته نیست. شناختش نیازی به جمع بندی و نتیجه گیری از نظرات مقالات این و اون نداره، قواعدش کشف کردنی نیست، تجربی نیست، حدس زدنی نیست. شما کد یک چیزی رو دارید، نگاه می کنید و می بینید چطور کار می کنه، ساختار مخفی و ناشناخته ای که نیست. کد و ساختار Binding هم مشخص و واضح میگه چه قابلیت هایی داره و چطور کار می کنه.
مثلا اگه جایی نوشته ، میخوام در این باره بیشتر بخونم که چطور و با چه روال و روندی این کار را انجام میده .
اگر هدف تون واقعا این باشه، اون چیزی که باید بخونید مقالات این و اون نیست، بررسی کد ئه.
 

SajjadKhati

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

بله، منطقی ئه، فرضا چیزی که مربوط به وظایف اختصاصی Model ئه، مثلا SqlConnection یا SqlTransaction و موارد مشابه که داده نیستند و مربوط به وظایف Model اند نباید داخل ViewModel بشوند.

بله. خلاصه اش اینه که هر چیزی که تقسیم وظایف این سه جزء رو بهم بزنه ناقض قواعد MVVM ئه.

چیزهایی که وظایف ViewModel ئه و در Model رخ داده یا چیزهایی که وظایف Model ئه و در ViewModel رخ داده، اینکه نکته مبهمی نداره، نیازی به ذکر جز به جز هم نداره، هزاران نوع شیء هست که ممکنه مربوط به وظایف یکی از این اجزاء بشه، دلیلی نداره که بخواهیم هزاران شیء رو موردی بررسی کنیم که انتقال کدوم شون بین فلان و بهمان قواعد تفکیک وظایف رو نقض می کنه و کدومشون نمی کنه، قواعد مشخص ئه، بر اساس کارکرد هر شیء مشخص میشه.

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

منبعی برای چی پیدا کنید؟ باید بدونید چطور ازش استفاده کنید، نه اینکه چطوری نوشته شده. برنامه نویسان در سراسر دنیا از Binding استفاده می کنند، نیازی هم ندارند که بدونند کدش چیه و چطور کار می کنه. اگر بخواهید بدونید چطور نوشته شده هم منبع مشخص ئه، کد اش. کد اش رو بررسی می کنید و متوجه می شوید چطور نوشته شده و کار می کنه. چه منبعی بهتر از کد اش میتونسته وجود داشته باشه؟

سلامی مجدد
خیلی ممنون استاد .

پس مشکلی نیست که فرضا یه سری از Command ها توی ViewModel نوشته باشیم و یه سری را توی View ؟
آخه همچین چیزی را جایی ندیدم (هر چند ، ندیدن ، ربطی به اون طور نشدن نداره و عدم وجود چیزی نداره) .

نمیدونم چرا کسی باید در مورد هر متد یا رخداد مقاله ای بنویسه، کلاس x زمانی میتونه و قطعا میتونه به سایر اشیاء خارجی تغییری که ازش مطلع هست رو اطلاع بده که یک رخداد داشته باشه که موقع بروز تغییر Invoke اش کنه، Raise اش کنه و سایر اشیاء خارجی که میخوان مطلع بشوند هم به اون رخداد متد متصل کنند. این نیازی به مقاله و منبع خاص برای کلاس x هم نداره، چون بدیهیات زبان #C ئه که وقتی اون رخداد فراخوانی شد، همه اون متد ها متصل اجرا می شوند و باخبر می شوند که تغییری رخ داده. کلاس Binding رو هم که در سایت مایکروسافت ببینید، دقیقا همون رخداد های مورد نیاز رو داره، هم میدونه منبع داده چیه، هم میدونه مشخصه هدف چیه و ... پس فقط کافیه سر هر Binding اون متد ها به رخداد مناسب متصل بشه تا به محض تغییر مطلع بشوند.

نه، اینها اصلا روش خوبی نیست. شناخت تکنولوژی های نرم افزاری با روش های حدسی، فرضی، تجربی و بر پایه جمع بندی نظریات شهرام و بهرام و حسن و حسین و ... منطقی نیست. نرم افزار مثل حیات وحش نیست که با چیزی طرف باشیم که خالق اش انسان نباشه و ندونیم قواعدش چیه و چطور کار می کنه و مجبور باشیم با آزمایش و جمع بندی و خوندن نظریات و تحلیل تجربیات و ... به نتیجه برسیم که فرضا زنبور چطور کندو اش رو شناسایی می کنه، نه، اینها برای علم کامپیوتر مناسب نیست. قبلا هم بهتون گفتم، نرم افزار رو انسان نوشته، یک ساختار عجیب و ناشناخته نیست. شناختش نیازی به جمع بندی و نتیجه گیری از نظرات مقالات این و اون نداره، قواعدش کشف کردنی نیست، تجربی نیست، حدس زدنی نیست. شما کد یک چیزی رو دارید، نگاه می کنید و می بینید چطور کار می کنه، ساختار مخفی و ناشناخته ای که نیست. کد و ساختار Binding هم مشخص و واضح میگه چه قابلیت هایی داره و چطور کار می کنه.

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

توی متد OnItemsSourceChanged گشتم ، چیز خاصی در این باره که کد زیر چجوری میشه که کار میکنه و با انتخاب آیتمی در ListBox ئه کد زیر ، اون TextBlock چجوری متوجه میشه ، گیرم نیومد :


XML:
<DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
  <TextBlock DockPanel.Dock="Top"
             Text="{Binding Baseline}" />
  <ListBox ItemsSource="{Binding}"
           IsSynchronizedWithCurrentItem="True" />
</DockPanel>

کدهاش هم زیاد و حدودا پیچیده ان .
درک نحوه ی کارکرد این کد xaml برام مهم هست که در عمل ، زیاد متوجه نشدم . حالا بگذریم .

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

استاد ، میگم برای چیدمان در wpf در پروژه مون ، تا جایی که ممکنه بهتره که از مقدار صریح دادن به width و height امتناع کنیم . درست میگم؟

بجاش از margin و padding و alignment و اینها استفاده کنیم .
اگه هم نیاز داریم فرضا width (یا height) ئه چند یا یک کنترل با هم برابر با یه مقدار خاصی باشن ، بجاشون یه کنترل کنترل panel مثل Grid را ستودن بندی (یا سطر بندی) کنیم و به اون ستون یا سطری که اون کنترل ها را توشون میذاریم ، صرفا مقدارِ width یا height ئه سطر یا ستونِ اون Grid را مقدار ثابتی بدیم (و مقدار alignment ئه کنترل هایی که داخل سطر و ستون این Grid میذاریم را روی مقدار strech بذاریم) .
تا با اینکار سعی کنیم اندازه ی یه کنترل ، برابر با محتواش بشه . یا اگه مقدار ثابتی (برای width یا height) هم در نظر میگیریم ، روی یه سطر یا ستونِ Grid اِعمال کنیم تا لازم نباشه هر بار روی تمامِ کنترل های داخلِ اون سطر و ستونِ Grid که میذاریم ، مقداری برای width یا height شون اِعمال کنیم .
درست میگم؟

تشکر استاد .
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد .

پس مشکلی نیست که فرضا یه سری از Command ها توی ViewModel نوشته باشیم و یه سری را توی View ؟
ابدا مشکلی نداره، Command و event و delegate و object و <List<T و فیلد و مشخصه و ... چیزهایی نیستند که به وظیفه یک جزء خاص مربوط باشند، مادامی که در کد کاری نکنید که به وظیفه جزء دیگری مربوط باشه هیچ ایرادی نیست.
آخه همچین چیزی را جایی ندیدم (هر چند ، ندیدن ، ربطی به اون طور نشدن نداره و عدم وجود چیزی نداره) .
چون دنبالش نگشتید، وگرنه در اینترنت انواع کد ها هست، از منظم و اصولی گرفته تا شلخته و بی قاعده.
استاد ، میگم برای چیدمان در wpf در پروژه مون ، تا جایی که ممکنه بهتره که از مقدار صریح دادن به width و height امتناع کنیم . درست میگم؟
ایراد اصولی که نداره، بر اساس نیاز تصمیم می گیرید که باید اندازه ثابت تعیین بشه یا بر اساس موارد دیگری ابعاد نسبی و متغیر باشه.
یک زمانی هدف همون اندازه ثابت ئه، مخصوصا وقتی که ابعاد فضای طراحی تون هم ثابت ئه و میدونید در چه کادری باید قرار بگیره، میدونید که بزرگتر یا کوچکتر نمیشه، یا ممکنه متغیر باشه ولی میدونید ظاهرش در ابعاد دیگه قشنگ نیست و روی اون ابعاد ثابت تاکید دارید.
اما ممکنه در یک پروژه اینطور نباشه، فرضا یک ظاهری طراحی می کنید که محل نمایش اش ابعاد از قبل مشخصی نداره، قراره در ابعاد متغیر و غیر قابل پیشبینی دیده بشه، اینجور مواقع اون تعیین صریح اندازه ممکنه روش مناسبی نباشه، فقط همین.
بجاش از margin و padding و alignment و اینها استفاده کنیم .
ممکنه از همه اینها استفاده کنید، همه چی بستگی به این داره که در طراحی ملاک موقعیت و ابعاد چی بوده، میخواستید نسبت به شیء دیگری تعیین اش کنید یا میخواستید با مقادیر صریح تعیین اش کنید. اینها هیچکدوم به خودی خود مزیت نیستند، وقتی مزیت هستند که برای اون ملاک شما انتخاب درستی باشند.
اگه هم نیاز داریم فرضا width (یا height) ئه چند یا یک کنترل با هم برابر با یه مقدار خاصی باشن ، بجاشون یه کنترل کنترل panel مثل Grid را ستودن بندی (یا سطر بندی) کنیم و به اون ستون یا سطری که اون کنترل ها را توشون میذاریم ، صرفا مقدارِ width یا height ئه سطر یا ستونِ اون Grid را مقدار ثابتی بدیم (و مقدار alignment ئه کنترل هایی که داخل سطر و ستون این Grid میذاریم را روی مقدار strech بذاریم) .
بستگی به این داره که اون ابعاد ثابت چیزی ئه که میخواهید یا صرفا میخواهید یک فضا رو بین چند کنترل بصورت مساوی تقسیم کنید؟ یک زمانی هست که میخواهید اون کنترل فلان ابعاد رو داشته باشه، کاری به این ندارید که فضای خالی چقدر هست، اما یک زمانی هست که کاری ندارید که اندازه ها دقیقا چیه، فقط یک فضا دارید که میخواهید اون کنترل ها با اندازه مساوی بین هم تقسیم اش کنند. مهم انتخاب درست بر اساس ملاک شما است، وگرنه هیچ انتخابی به خودی خود خوب یا بد نیست. اگر ندونید طراح چی میخواسته، نمی توانید بگید انتخابی که کرده مناسب ئه یا نه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
نمیدونم چرا کسی باید در مورد هر متد یا رخداد مقاله ای بنویسه، کلاس x زمانی میتونه و قطعا میتونه به سایر اشیاء خارجی تغییری که ازش مطلع هست رو اطلاع بده که یک رخداد داشته باشه که موقع بروز تغییر Invoke اش کنه، Raise اش کنه و سایر اشیاء خارجی که میخوان مطلع بشوند هم به اون رخداد متد متصل کنند. این نیازی به مقاله و منبع خاص برای کلاس x هم نداره، چون بدیهیات زبان #C ئه که وقتی اون رخداد فراخوانی شد، همه اون متد ها متصل اجرا می شوند و باخبر می شوند که تغییری رخ داده. کلاس Binding رو هم که در سایت مایکروسافت ببینید، دقیقا همون رخداد های مورد نیاز رو داره، هم میدونه منبع داده چیه، هم میدونه مشخصه هدف چیه و ... پس فقط کافیه سر هر Binding اون متد ها به رخداد مناسب متصل بشه تا به محض تغییر مطلع بشوند.

سلامی مجدد
خیلی ممنون استاد .

در لینک زیر :


در بخشِ using a default view ، انگار درباره ی همچین چیزی توضیح داده :


If you bind directly to a collection, WPF binds to its default view. This default view is shared by all bindings to the same collection, so a change made to a default view by one bound control or code (such as sorting or a change to the current item pointer, discussed later) is reflected in all other bindings to the same collection.

انگار این طوره که هر بایندینگ ای که به هر کالکشن ای انجام میدیم ، اگه خودمون براش CollectionView نسازیم ، خودِ wpf ، یه CollectionView ئه پیش فرض براش میسازه .

این CollectionView ئه پیش فرض ، با تمامِ اشیای binding ای که به همون کالکشن متصل میشن، به اشتراک گذاشته میشه .
پس هر تغییری که در این CollectionView اتفاق بیفته (توسط کنترل یا کدی که به این CollectionView ، بایند شده) ، به همه ی اشیاء binding ای که به همین کالکشن متصل هستند ، انعکاس و خبر داده میشه .

درست میگم؟
تشکر استاد .
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد .

در لینک زیر :


در بخشِ using a default view ، انگار درباره ی همچین چیزی توضیح داده :




انگار این طوره که هر بایندینگ ای که به هر کالکشن ای انجام میدیم ، اگه خودمون براش CollectionView نسازیم ، خودِ wpf ، یه CollectionView ئه پیش فرض براش میسازه .

این CollectionView ئه پیش فرض ، با تمامِ اشیای binding ای که به همون کالکشن متصل میشن، به اشتراک گذاشته میشه .
پس هر تغییری که در این CollectionView اتفاق بیفته (توسط کنترل یا کدی که به این CollectionView ، بایند شده) ، به همه ی اشیاء binding ای که به همین کالکشن متصل هستند ، انعکاس و خبر داده میشه .

درست میگم؟
بله. قاعدتا اون مطلب رو قبلا خونده بودید (پست 164#)
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
خیلی ممنون استاد .
نه . اون موقع ، تا ابتدای Data conversion را خونده بودم .
چند روز پیش ، Binding به Collection (و از جمله CollectionView) را خونده بودم اما بعضی از مطالبش را متوجه نشدم مفهومش چیه . تا اینکه شما در چند پست قبل ، قضیه ی اشتراک گذاری بین Binding ها را که گفتید (قضیه ی همون کدی که در پست 742 و قبل اش دادم) ، بعد دیشب که دوباره اون مطلب را خوندم ، یه کم الان انگار متوجه شدم که منظورش چیه .


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


استاد ، من چیزهایی که از CollectionView را میخونم ، برداشتم را اینجا میگم . ممنون میشم اگه چیزی غلط بود ، اشاره کنید .
منبع هم :


==================

1) اول اینکه CollectionView چیه؟

A collection view is a layer on top of a binding source collection
Because views do not change the underlying source collections, each source collection can have multiple views associated with it.
WPF also creates a default collection view for every collection used as a binding source. If you bind directly to a collection, WPF binds to its default view. This default view is shared by all bindings to the same collection, so a change made to a default view by one bound control or code (such as sorting or a change to the current item pointer, discussed later) is reflected in all other bindings to the same collection.

برداشت من این طوره که شیِ CollectionView ، اولا هر وقت Binding Source مون یه کالکشن باشه ، ایجاد میشه و هر کالکشن مون که Binding اش میکنیم (به عنوان Binding Source)، یا خودمون اون شیِ CollectionView را برای اون کالکشنِ Binding Source مون ایجاد میکنیم (که در این صورت میتونیم حتی چندین شیِ CollectionView برای یک کالکشنِ Binding Source مون ایجاد کنیم) یا اگه خودمون شیِ CollectionView ئه سفارشی ایجاد نکنیم ، wpf بصورت اتوماتیک ، یه شیِ CollectionView ی پیش فرض (Default CollectionView) را برای کالکشنِ Binding Source مون ایجاد میکنه .

نکته ی مهم اینجاست که وقتی Binding ای به کالکشن ای انجام میدیم (یا همون کالکشنِ Binding Source مون) ، Binding مون بصورت مستقیم با کالکشنِ Binding Source مون انجام نمیشه . بلکه ابتدا Binding به CollectionView انجام میشه و مجددا خودِ CollectionView ، به کالکشنِ Binding Source مون ، Binding انجام میده .

مثل مثال زیر که ItemsSource ئه ListBox ، به شیِ CollectionViewSource متصل هست و خودِ CollectionViewSource هم به کالکشنِ AuctionItems در شیِ Application (ئه جاری) متصل هست .

The CollectionViewSource class is the XAML proxy of a class that inherits from CollectionView.

اگه بخوایم در xaml از CollectionView استفاده کنیم ، بجاش باید از CollectionViewSource استفاده کنیم . اما در کدهای سی شارپ از CollectionView استفاده میکنیم :

XML:
<Window.Resources>
    <CollectionViewSource
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

و زمان استفاده :

XML:
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

البته من هر چی در کلاس Application میگردم ، عضوی بنام AuctionItems را پیدا نمیکنم . جریانش چیه؟

بصورت خلاصه اینکه CollectionView ، برای ارتباط با شیِ کالکشنِ Binding Source مون مورد استفاده قرار میگیره . وقتی به هر کالکشن ای ، Binding ای انجام میدیم ، ابتدا به شیِ CollectionView ، و بعد CollectionView مون ، به شیِ کالکشن (به عنوان کالکشنِ Binding Source که تعیین کرده بودیم) ، متصل میشه .
خواه CollectionView ئه سفارشی ساخته باشیم یا اصلا CollectionView نساخته باشیم که در این صورت از CollectionView ئه پیش فرض استفاده میشه .


==================

2) اما CollectionView ، به چه کار میاد؟

that allows you to navigate and display the source collection based on sort, filter, and group queries, without having to change the underlying source collection itself.

With the use of views, you can display that same data in different ways. For example, on the left side of your page you may want to show tasks sorted by priority, and on the right side, grouped by area.

CollectionView در 2 مورد کاربرد اساسی داره :
اول اینکه زمانی که یه کالکشنی داریم و بخوایماون کالکشنِ مون ثابت بمونه اما بخوایم یه تغییراتی روی اون اِعمال کنیم و صرفا اون تغییرات را در کنترل های مون ، نمایش بدیم . مثلا اعضای مرتب شده ، یا گروه بندی شده یا فیلتر شده از کالکشن مون را در ItemsControl ای نمایش بدیم .

این کار (استفاده از CollectionView) ، باعث میشه اولا کالکشنِ اصلی مون
(که به عنوان Binding Source استفاده کرده بودیم) ، دست نخورده باقی بمونه و همچنین هر چند تا دلمون میخواد ، شیِ CollectionView از یک شیِ واحد از کالکشنِ Binding Source مون درست میکنیم که فرضا اعضای یک CollectionView اش را بر اساس ترتیب خاصی مرتب کنیم و درون یه شی از ItemsControl نمایش بدیم و باز مجددا یه شیِ دیگه ی CollectionView از همون کالکشنِ Binding Source را بر اساس گروه بندی ، مرتب کنیم و در یه شیِ دیگه از ItemsControl نمایش بدیم .

در واقع CollectionView ، شبیه به View ها در دیتابیس (که یک جدول مجازی اند) ، عمل میکنه :


 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
دوم و از همه مهمتر اینکه ، هر CollectionView ، یه پروپرتی ای بنام CollectionView.CurrentItem داره که میشه باهاش آیتم خاصی از کالکشن مون را انتخاب کنیم :


The current item of the collection is bound to automatically if the target of a binding is a singleton value. If the target is an ItemsControl, the current item is synchronized with the selected item.

A collection view also maintains a pointer to the current item in the collection.

Because WPF binds to a collection only by using a view (either a view you specify, or the collection's default view), all bindings to collections have a current item pointer.

اما اگه مقدار این پروپرتی را خودمون تنظیم نکنیم (با متد SetCurrent میشه مقدار این پروپرتی را تنظیم کرد) ، یعنی اگه wpf این پروپرتی بصورت اتوماتیک تنظیم کنه (مثلا زمانی که از Default CollectionView استفاده میشه) ، اگه (پروپرتیِ) Target Binding مون از نوع کنترلِ ItemsControl باشه (مثلا پروپرتیِ ItemsControl.ItemsSource باشه) و همچنین اگه Binding Source مون هم یه کالکشن باشه ، مقدار CurrentItem (ئه CollectionView) مون برابر با آیتم انتخاب شده در کنترلِ ItemsControl میشه .

ببینید اینی که میگم ، درسته؟ :
اگه (پروپرتیِ) Target Binding مون ، یه پروپرتیِ تک مقداره باشه و از نوعِ کالکشن نباشه (مثل پروپرتیِ TextBlock.Text در مثال پست 742) ، پروپرتیِ CurrentItem ، به اون پروپرتی (TextBlock.Text) مون Binding میشه .

(سه تا مونده به آخری در نقلِ قولِ پستِ بالا ، منظورش اینی هست که گفتم) ؟
اگه هست ، پس در جای دیگه میگه که :

When binding to a view, the slash ("/") character in a Path value designates the current item of the view.

که یعنی برای Binding کردن به پروپرتیِ CollectionView.CurrentItem ، باید در مقدارِ پروپرتیِ Path ئه Binding مون ، کاراکترِ اسلش "/" را بذاریم . مثل مثال زیر :

کد:
<Button Content="{Binding Path=/}" />

اما اگه این طور نیست ، من هنوز سناریوی کد پست 742 یا کد در چند پاراگراف زیر را متوجه نشدم .

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

در واقع ، اگه یه پروپرتیِ Target Binding ای را (که از نوعِ کالکشن نباشه مثل پروپرتیِ TextBlock.Text در اوم مثال) Binding کنیم که Binding Source اش ، یه شیِ کالکشن باشه (و میدونیم هم که هر کالکشن ای که Binding میشه ، حداقل یه شیِ CollectionView ئه مربوط به خودش را داره) ، در واقع Path ئه اون Binding ، اتوماتیک به پروپرتیِ CollectionView.CurrentItem متصل و Binding میشه . حتی بدون کاراکتر اسلش (که بدون کاراکتر اسلش ، غلط هست اما بدون این کاراکتر ، من سناریوی کد زیر را متوجه نمیشم) .
درست میگم؟

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

در واقع در مثال زیر ، سناریوی خودم را میگم (که احتمالا چون برای Binding کردن به پروپرتیِ Baseline با Text ئه TextBlock ، کاراکتر اسلش را نذاشت ، سناریو ام غلط هست) و شما درستی اش را بررسی کنید . اگه اشتباه میکنم ، پس سناریو اش چجوری میشه؟ :

XML:
<DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
  <TextBlock DockPanel.Dock="Top"
             Text="{Binding Baseline}" />
  <ListBox ItemsSource="{Binding}"
           IsSynchronizedWithCurrentItem="True" />
</DockPanel>

مقدار DataContext یا همون Source ئه همه ی کنترل های فرزندش که Binding انجام میده ، از نوع کالکشنی از FontFamily که همون Fonts.SystemFontFamilies باشه ، هست .
خودِ کالکشنِ Fonts.SystemFontFamilies که چون به عنوان Binding Source داره استفاده میشه و ما هم CollectionView ای براش تعریف نکردیم ، پس wpf برای این کالکشنِ Fonts.SystemFontFamilies مون ، یه شیِ پیش فرضِ CollectionView تعریف میکنه .

در واقع پروپرتی های ItemsSource ئه ListBox و Text ئه TextBlock مون در کد بالا ، مستقیما به کالکشنِ Fonts.SystemFontFamilies مون Binding نمیشن . بلکه هر دوی این پروپرتی ها به شیِ پیش فرضِ CollectionView مون (که در کد بالا نیست و wpf بصورت اتوماتیک ساخت) ، Binding میشن و CollectionView ئه پیش فرض مون هم به کالکشنِ Fonts.SystemFontFamilies مون Binding میشه .

چون مقدارِ Path در Binding ئه پروپرتیِ ItemsSource.ListBox مشخص نشد ، پس کل کالکشنِ Fonts.SystemFontFamilies (در واقع شیِ پیش فرضِ CollectionView) به این پروپرتی Binding میشه که بدیهی هست .

اما نکته ی مهم اینجاست که چون پروپرتیِ Text ئه TextBlock ، از نوعی نیست که کالکشن ای را قبول کنه ، پس Path ئه Binding در پروپرتیِ Text ئه TextBlock ، به پروپرتیِ CollectionView.CurrentItem (در شیِ پیش فرضِ CollectionView که اتوماتیک توسط wpf ساخته میشه) ، Binding میشه .

از طرفی هم که هر تغییر انتخابِ آیتم هادرونِ کنترلِ ListBox (تغییر انتخاب آیتم ها توسط کاربر نهایی یا برنامه نویس برنامه توسط کدها) ، باعث میشه که مقدارِ پروپرتیِ CollectionView.CurrentItem ای که بهش Binding شده بود ، تغییر کنه و از طرفی هم پروپرتیِ Text ئه TextBlock هم از همین شی از CollectionView استفاده میکنه و به پروپرتیِ CurrentItem از همین شیِ CollectionView مون Binding شده بود ، باعث میشه که پروپرتیِ Text ئه TextBlock هم تغییر کنه .
درست میگم؟

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

XML:
<TextBlock DockPanel.Dock="Top"
Text="{Binding /Baseline}" />

که در پنلِ Error List ، ارور زیر را میده :

Type 'ICollection`1' is not a collection.

اما با کمال تعجب ، هم برنامه به درستی اجرا میشه و هم پروپرتیِ Text ئه TextBlock ام همون مقداری میشه که وقتی اسلش نمیذاشتم بود . یعنی به درستی کار میکنه .
جالب تر اینکه وقتی بجای کاراکترِ اسلش "/" ، کاراکترِ بک اسلش "\" را میذارم ، همین ارور را هم نمیده و نتیجه اش هم مثل قبلی هاست .
جریان این چجوری هست و چرا این جوری هست؟


===================


پی نوشت :

- برای گرفتنِ شیِ پیش فرضِ CollectionView ، از متد CollectionViewSource.GetDefaultView باید استفاده کنیم .

- در این مقالات (که لینک داده شد) ، در جاهایی که درباره ی CollectionView صحبت میکنه ، در اغلبِ جاهایی که نامِ View را میبره ، منظورش شی های CollectionView هستند . منظورش از View ، کنترل ها و المنت ها و کلا لایه ی View ای که در MVVM میشناسیم ، نیست .

- با متدهای CollectionView.MoveCurrentToPrevious و CollectionView.MoveCurrentToNext ، میتونیم مقدار پروپرتیِ CollectionView.CurrentItem هر چی که باشه را یه دونه به جلو یا عقب در شیِ CollectionView مون انتخاب کنیم .

- عملیات sorting یا filtering روی مقدار CurrentItem از CollectionView مون میتونه تاثیر میذاره .

- مقدارِ مقدار CurrentItem ئه CollectionView ، بصورت پیش فرض ، مقدارِ صفر اُمین عضو از کالکشنِ Binding شده هست (بجز موقع sorting یا filtering یا اینکه کاربر نهایی ، در کنترلِ Binding شده ی مورد نظر ، آیتمی را انتخاب کنه یا برنامه نویس تغییری بده) .



درست گفتم؟
ببخشید خیلی زیاد شد .
خیلی ممنون استاد .
 
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
در بخش Master-detail binding scenario به نکته ی زیر که در پست بالا مشکوک بودم ، اشاره کرد :


مثالش :

XML:
<Window.Resources>
    <CollectionViewSource
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"  
      x:Key="listingDataView" />
</Window.Resources>

XML:
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}"
                Margin="9,0,0,0"/>

This binding works because when a singleton object (the ContentControl in this case) is bound to a collection view, it automatically binds to the CurrentItem of the view.

(که اشاره میکنه که در این مثال) ، Binding (درست) کار میکنه چون وقتی که یه شیِ تکی (که اگه اشتباه نکنم ، منظورش اینه که Target Property مون ، یه پروپرتی ای از نوع کالکشن نیست . یعنی در اینجا ، پروپرتیِ Content ئه ContentControl هست) ، به یک CollectionView مون Binding میشه ، اتوماتیک به پروپرتیِ CurrentItem ئه CollectionView مون Binding میشه .

دقت کنید که در اینجا نگفت که برای Binding شدن برای پروپرتیِ Content ئه ContentControl (در این شرایطِ کد بالا) ، نیاز هست که برای پروپرتیِ Path مون ، بک اسلش بکار ببریم . اما در چیزی که در پست قبل گذاشته بودم ، گفته بود که نیاز هست .


درست میگم؟
اگه درست میگم ، پس چرا در کد بالا ، حرفی از قضیه ی بک اسلش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView نزد (و کدش مثل کد پست 742 هست) اما در پست قبلی ام ، گفته بود که بک اسلش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView لازم هست؟
و همچنین چرا من وقتی با بک اسلش و بدون بک اسلش امتحان میکنم ، اون اروری که پست قبلی دادم را میده؟
همچنین چرا چیزی که در اینجا گفت (درباره ی اسلش چیزی نگفت) با چیزی که در پست قبل نوشتم و گفته بودن (درباره ی قضیه ی اسلش و لزومش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView) ، تناقض دارن؟
اگه این سناریو درست باشه ، سناریوی دوم در پست قبلی هم که بهش مشکوک بودم ، طبعا باید درست باشه .
اگه درست نمیگم ، درستش چیه و مجددا چرا گفته هاشون با هم تناقض دارن؟


============================

پی نوشت :

The CollectionViewSource objects automatically synchronize currency and selection.

- اگه از شیِ CollectionViewSource (که طراحی شده برای زمانی که در xaml کد مینویسیم) (مثل کد بالا) استفاده کنیم ، اتوماتیک آیتم انتخاب شده در کنترل با پروپرتی CurrentItem ئه CollectionView ، همگام میشه (طبق سناریویی که در این پست و پست قبل گفته شد که باید منبع شون ، یه کالکشن باشه و بقیه ی شرایط گفته شده) .

If your list control is not bound to a CollectionViewSource object as in this example, then you would need to set its IsSynchronizedWithCurrentItem property to true for this to work.

- اگه (فرضا در کد بالا) (در شیِ Selector ای مثل ListBox) ، از شیِ CollectionViewSource استفاده نکنیم (و فرضا در کد سی شارپ) ، بجاش از شیِ CollectionView استفاده کنیم ، برای اینکه همین حالت کار کنه ، یعنی برای اینکه اتوماتیک آیتم انتخاب شده در کنترل Selector با پروپرتی CurrentItem ئه CollectionView ، همگام بشه ، مقدار پروپرتیِ Selector.IsSynchronizedWithCurrentItem را باید true کنیم .

درست گفتم؟
تشکر استاد .
 
آخرین ویرایش:

the_king

مدیرکل انجمن
البته من هر چی در کلاس Application میگردم ، عضوی بنام AuctionItems را پیدا نمیکنم . جریانش چیه؟
جریان خاصی نداره، داره در مورد مثال Data binding demo حرف می زنه، فایل App.cs
در واقع CollectionView ، شبیه به View ها در دیتابیس (که یک جدول مجازی اند) ، عمل میکنه :

دیگه نه، در اون حد نیست.
اما اگه مقدار این پروپرتی را خودمون تنظیم نکنیم (با متد SetCurrent میشه مقدار این پروپرتی را تنظیم کرد) ، یعنی اگه wpf این پروپرتی بصورت اتوماتیک تنظیم کنه (مثلا زمانی که از Default CollectionView استفاده میشه) ، اگه (پروپرتیِ) Target Binding مون از نوع کنترلِ ItemsControl باشه (مثلا پروپرتیِ ItemsControl.ItemsSource باشه) و همچنین اگه Binding Source مون هم یه کالکشن باشه ، مقدار CurrentItem (ئه CollectionView) مون برابر با آیتم انتخاب شده در کنترلِ ItemsControl میشه .
بله.
اگه (پروپرتیِ) Target Binding مون ، یه پروپرتیِ تک مقداره باشه و از نوعِ کالکشن نباشه (مثل پروپرتیِ TextBlock.Text در مثال پست 742) ، پروپرتیِ CurrentItem ، به اون پروپرتی (TextBlock.Text) مون Binding میشه .
ربطی به Text و TextBlock نداره. CurrentItem ئه چون Baseline رو در خود مجموعه پیدا نمی کنه و در CurrentItem پیدا می کنه.
XML:
        <DockPanel>
            <DockPanel.DataContext>
                <x:Array Type="system:Char">
                    <system:Char>a</system:Char>
                    <system:Char>b</system:Char>
                    <system:Char>c</system:Char>
                </x:Array>
            </DockPanel.DataContext>
            <TextBlock DockPanel.Dock="Top" Text="{Binding Length}"/>
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>
در واقع ، اگه یه پروپرتیِ Target Binding ای را (که از نوعِ کالکشن نباشه مثل پروپرتیِ TextBlock.Text در اوم مثال)
اولا Text یک string ئه که مجموعه ای از char ها است، ثانیا این مساله ربطی به Text نداره که Collection باشه یا نباشه.
در واقع در مثال زیر ، سناریوی خودم را میگم (که احتمالا چون برای Binding کردن به پروپرتیِ Baseline با Text ئه TextBlock ، کاراکتر اسلش را نذاشت ، سناریو ام غلط هست) و شما درستی اش را بررسی کنید . اگه اشتباه میکنم ، پس سناریو اش چجوری میشه؟ :

XML:
<DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
  <TextBlock DockPanel.Dock="Top"
             Text="{Binding Baseline}" />
  <ListBox ItemsSource="{Binding}"
           IsSynchronizedWithCurrentItem="True" />
</DockPanel>

مقدار DataContext یا همون Source ئه همه ی کنترل های فرزندش که Binding انجام میده ، از نوع کالکشنی از FontFamily که همون Fonts.SystemFontFamilies باشه ، هست .
خودِ کالکشنِ Fonts.SystemFontFamilies که چون به عنوان Binding Source داره استفاده میشه و ما هم CollectionView ای براش تعریف نکردیم ، پس wpf برای این کالکشنِ Fonts.SystemFontFamilies مون ، یه شیِ پیش فرضِ CollectionView تعریف میکنه .

در واقع پروپرتی های ItemsSource ئه ListBox و Text ئه TextBlock مون در کد بالا ، مستقیما به کالکشنِ Fonts.SystemFontFamilies مون Binding نمیشن . بلکه هر دوی این پروپرتی ها به شیِ پیش فرضِ CollectionView مون (که در کد بالا نیست و wpf بصورت اتوماتیک ساخت) ، Binding میشن و CollectionView ئه پیش فرض مون هم به کالکشنِ Fonts.SystemFontFamilies مون Binding میشه .

چون مقدارِ Path در Binding ئه پروپرتیِ ItemsSource.ListBox مشخص نشد ، پس کل کالکشنِ Fonts.SystemFontFamilies (در واقع شیِ پیش فرضِ CollectionView) به این پروپرتی Binding میشه که بدیهی هست .
تا اینجا درسته.
اما نکته ی مهم اینجاست که چون پروپرتیِ Text ئه TextBlock ، از نوعی نیست که کالکشن ای را قبول کنه ، پس Path ئه Binding در پروپرتیِ Text ئه TextBlock ، به پروپرتیِ CollectionView.CurrentItem (در شیِ پیش فرضِ CollectionView که اتوماتیک توسط wpf ساخته میشه) ، Binding میشه .
نه. Text رو ول کنید، چی داره Binding میشه؟ {Binding Baseline}، اون Baseline در خود مجموعه هست؟ نیست، برای همینه که میره سراغ زیر مجموعه هاش، وگرنه اگر {Binding} خالی بود خود مجموعه رو بصورت ToString تحویل Text میداد.
XML:
        <DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
            <TextBlock DockPanel.Dock="Top" Text="{Binding}" />
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>
وقتی Count رو داخل خود منبع داده پیدا کنه سراغ زیر مجموعه ها نمیره و همون رو تحویل Text میده، دلیلی نداره که بخاطر Text بودن و نبودن کار خاصی انجام بده :
XML:
        <DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
            <TextBlock DockPanel.Dock="Top" Text="{Binding Count}" />
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>
از طرفی هم که هر تغییر انتخابِ آیتم هادرونِ کنترلِ ListBox (تغییر انتخاب آیتم ها توسط کاربر نهایی یا برنامه نویس برنامه توسط کدها) ، باعث میشه که مقدارِ پروپرتیِ CollectionView.CurrentItem ای که بهش Binding شده بود ، تغییر کنه و از طرفی هم پروپرتیِ Text ئه TextBlock هم از همین شی از CollectionView استفاده میکنه و به پروپرتیِ CurrentItem از همین شیِ CollectionView مون Binding شده بود ، باعث میشه که پروپرتیِ Text ئه TextBlock هم تغییر کنه .
صرفا در همون مثال، نه در سایر مثال ها. حالت کلی که نداره. وقتی Text به خود مجموعه Bind کنه دیگه تغییر CurrentItem تاثیری روی اون نداره.
درست میگم؟

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

XML:
<TextBlock DockPanel.Dock="Top"
Text="{Binding /Baseline}" />

که در پنلِ Error List ، ارور زیر را میده :



اما با کمال تعجب ، هم برنامه به درستی اجرا میشه و هم پروپرتیِ Text ئه TextBlock ام همون مقداری میشه که وقتی اسلش نمیذاشتم بود . یعنی به درستی کار میکنه .
جالب تر اینکه وقتی بجای کاراکترِ اسلش "/" ، کاراکترِ بک اسلش "\" را میذارم ، همین ارور را هم نمیده و نتیجه اش هم مثل قبلی هاست .
جریان این چجوری هست و چرا این جوری هست؟
خطای Designer ئه که توقع داره اون داده منبع یک ObservableCollection باشه. ممکنه در آپدیت های بعدی دیگه این خطا رو نشون نده.

(که اشاره میکنه که در این مثال) ، Binding (درست) کار میکنه چون وقتی که یه شیِ تکی (که اگه اشتباه نکنم ، منظورش اینه که Target Property مون ، یه پروپرتی ای از نوع کالکشن نیست . یعنی در اینجا ، پروپرتیِ Content ئه ContentControl هست) ، به یک CollectionView مون Binding میشه ، اتوماتیک به پروپرتیِ CurrentItem ئه CollectionView مون Binding میشه .
منظورش اینه که ContentControl بصورت خودکار سراغ CurrentItem میره.
دقت کنید که در اینجا نگفت که برای Binding شدن برای پروپرتیِ Content ئه ContentControl (در این شرایطِ کد بالا) ، نیاز هست که برای پروپرتیِ Path مون ، بک اسلش بکار ببریم . اما در چیزی که در پست قبل گذاشته بودم ، گفته بود که نیاز هست .
درست میگم؟
اگه درست میگم ، پس چرا در کد بالا ، حرفی از قضیه ی بک اسلش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView نزد (و کدش مثل کد پست 742 هست) اما در پست قبلی ام ، گفته بود که بک اسلش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView لازم هست؟
و همچنین چرا من وقتی با بک اسلش و بدون بک اسلش امتحان میکنم ، اون اروری که پست قبلی دادم را میده؟
اگر توضیحات هر کدوم رو بخونید می بینید که داره در مورد یک شرایط خاص و قابلیت ویژه حرف می زنه، برای همین نباید با هم قاطی شون کنید.
مواقعی هست که هم مجموعه یک موردی رو داره و هم زیر مجموعه هاش، مثلا همون خودش Count داره و هم زیر مجموعه هاش. طبعا در اون شرایط با اسلش تصمیم میگیرید که منظور Count خود مجموعه است یا Count در زیر مجموعه ها.
همچنین چرا چیزی که در اینجا گفت (درباره ی اسلش چیزی نگفت) با چیزی که در پست قبل نوشتم و گفته بودن (درباره ی قضیه ی اسلش و لزومش برای Binding کردن به پروپرتیِ CurrentItem ئه CollectionView) ، تناقض دارن؟
نه. تناقض ندارن.
- اگه (فرضا در کد بالا) (در شیِ Selector ای مثل ListBox) ، از شیِ CollectionViewSource استفاده نکنیم (و فرضا در کد سی شارپ) ، بجاش از شیِ CollectionView استفاده کنیم ، برای اینکه همین حالت کار کنه ، یعنی برای اینکه اتوماتیک آیتم انتخاب شده در کنترل Selector با پروپرتی CurrentItem ئه CollectionView ، همگام بشه ، مقدار پروپرتیِ Selector.IsSynchronizedWithCurrentItem را باید true کنیم .
اینکه می گویید "اتوماتیک آیتم انتخاب شده در کنترل Selector با پروپرتی CurrentItem ئه CollectionView ، همگام بشه" درسته ولی ربطی به اینکه از CollectionViewSource استفاده می کنید یا نمی کنید نداره، توضیح مشخصه اش رو بخونید.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
جریان خاصی نداره، داره در مورد مثال Data binding demo حرف می زنه، فایل App.cs

سلامی مجدد
خیلی ممنون استاد .

راستی درباره ی این قضیه ی دانلود از اون لینک (زیر) :


خیلی وقته میخواستم بپرسم که اگه بخوایم کل برنامه را با همه ی فایل های برنامه ای که در اون صفحه لیست کرد را یکجا دانلود کنیم ، چی کار باید کنیم؟ من دکمه ی دانلود برنامه را در صفحه نمیبینم .


ربطی به Text و TextBlock نداره. CurrentItem ئه چون Baseline رو در خود مجموعه پیدا نمی کنه و در CurrentItem پیدا می کنه.
XML:
        <DockPanel>
            <DockPanel.DataContext>
                <x:Array Type="system:Char">
                    <system:Char>a</system:Char>
                    <system:Char>b</system:Char>
                    <system:Char>c</system:Char>
                </x:Array>
            </DockPanel.DataContext>
            <TextBlock DockPanel.Dock="Top" Text="{Binding Length}"/>
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>

اولا Text یک string ئه که مجموعه ای از char ها است، ثانیا این مساله ربطی به Text نداره که Collection باشه یا نباشه.

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

نه. Text رو ول کنید، چی داره Binding میشه؟ {Binding Baseline}، اون Baseline در خود مجموعه هست؟ نیست، برای همینه که میره سراغ زیر مجموعه هاش، وگرنه اگر {Binding} خالی بود خود مجموعه رو بصورت ToString تحویل Text میداد.
XML:
        <DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
            <TextBlock DockPanel.Dock="Top" Text="{Binding}" />
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>

وقتی Count رو داخل خود منبع داده پیدا کنه سراغ زیر مجموعه ها نمیره و همون رو تحویل Text میده، دلیلی نداره که بخاطر Text بودن و نبودن کار خاصی انجام بده :
XML:
        <DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
            <TextBlock DockPanel.Dock="Top" Text="{Binding Count}" />
            <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
        </DockPanel>

صرفا در همون مثال، نه در سایر مثال ها. حالت کلی که نداره. وقتی Text به خود مجموعه Bind کنه دیگه تغییر CurrentItem تاثیری روی اون نداره.

خطای Designer ئه که توقع داره اون داده منبع یک ObservableCollection باشه. ممکنه در آپدیت های بعدی دیگه این خطا رو نشون نده.

خیلی ممنون استاد . نکات خیلی خوبی گفتین .
پس در کل نکته ی مهم اینه که ، هر جا در پروپرتیِ Path (که به یه کالکشن ، یا در واقع بصورت ضمنی به یه CollectionView ای Binding کردیم) ، اگه نام پروپرتی ای را بدیم ، اول میگرده ببینه که توی کلِ (شیِ) اون کالکشن مون همچین پروپرتی ای وجود داره یا نه .
اگه وجود نداشت ، میره به پروپرتیِ CollectionView.CurrentItem ، عملِ Binding را انجام میده .
درست گفتم؟

منظورش اینه که ContentControl بصورت خودکار سراغ CurrentItem میره.

چی شد؟
شما الان گفتین که به پروپرتی ربط نداره .
اگه نامِ اون پروپرتی ای که به Path ئه Binding میدیم ، در کالکشن مون وجود نداشت ، بصورت خودکار سراغ CurrentItem ئه CollectionView میره که .

اگر توضیحات هر کدوم رو بخونید می بینید که داره در مورد یک شرایط خاص و قابلیت ویژه حرف می زنه، برای همین نباید با هم قاطی شون کنید.

بله ، منظور من هم در اون کد و در اون شرایط بود .

مواقعی هست که هم مجموعه یک موردی رو داره و هم زیر مجموعه هاش، مثلا همون خودش Count داره و هم زیر مجموعه هاش. طبعا در اون شرایط با اسلش تصمیم میگیرید که منظور Count خود مجموعه است یا Count در زیر مجموعه ها.

آها.
یعنی گذاشتنِ اسلش و نذاشتن اش زمانی مهم هست که هم کلِ کالکشن و هم عضوِ CurrentItem از CollectionView مون ، عضو و پروپرتی ای با یک نام داشته باشند که با کاراکترِ اسلش ، منظورمون را میرسونیم که منظورمون ، اون پروپرتی از CurrentItem هست (نه اون پروپرتی از کلِ کالکشن) .

بنابراین در شرایطی که اگه اون نام از پروپرتی ای را که در Path ئه Binding مون مشخص میکنیم ، فقط به عنوانِ یکی از اعضای کالکشن و یا CurrentItem از CollectionView وجود داشته باشه ، در اینجا ، گذاشتن یا نذاشتنِ کاراکترِ اسلش ، فرقی نداره (چه بذاریم یا نذاریم) .
درست میگم؟

نه. تناقض ندارن.

اینکه می گویید "اتوماتیک آیتم انتخاب شده در کنترل Selector با پروپرتی CurrentItem ئه CollectionView ، همگام بشه" درسته ولی ربطی به اینکه از CollectionViewSource استفاده می کنید یا نمی کنید نداره، توضیح مشخصه اش رو بخونید.

البته توی همون متن نوشته بود که اگه بجای CollectionViewSource ، از CollectionView استفاده میکنید ، برای اطمینان از همگام شدن ، مقدار پروپرتیِ Selector.IsSynchronizedWithCurrentItem را true کنیم .

خیلی ممنون استاد .
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد .

راستی درباره ی این قضیه ی دانلود از اون لینک (زیر) :


خیلی وقته میخواستم بپرسم که اگه بخوایم کل برنامه را با همه ی فایل های برنامه ای که در اون صفحه لیست کرد را یکجا دانلود کنیم ، چی کار باید کنیم؟ من دکمه ی دانلود برنامه را در صفحه نمیبینم .
اگر بخواهید از ابزار های مربوط به github استفاده کنید که بحثش جدا است، چون فرضا برنامه داره، برای ویژوال استدیو Extension داره.
اما اگر بخواهید همونجا مستقیم دانلودش کنید، از صفحه اون فایل بر می گردید عقب تر به صفحه خانه کد و با دکمه Code و Download ZIP دانلودش می کنید.
code.png
خیلی ممنون استاد . نکات خیلی خوبی گفتین .
پس در کل نکته ی مهم اینه که ، هر جا در پروپرتیِ Path (که به یه کالکشن ، یا در واقع بصورت ضمنی به یه CollectionView ای Binding کردیم) ، اگه نام پروپرتی ای را بدیم ، اول میگرده ببینه که توی کلِ (شیِ) اون کالکشن مون همچین پروپرتی ای وجود داره یا نه .
اگه وجود نداشت ، میره به پروپرتیِ CollectionView.CurrentItem ، عملِ Binding را انجام میده .

درست گفتم؟
بله، و توجه کنید که داریم فقط خود Binding رو میگیم، نه عملکرد کنترلی که داده رو دریافت کرده، ممکنه شما با Binding بهش مجموعه رو بدهید ولی خود کنترل بنا به طراحی خاص خودش زیر مجموعه اون منبع رو نشون بده، مثل اون ContentControl ئه.
چی شد؟
شما الان گفتین که به پروپرتی ربط نداره .
اگه نامِ اون پروپرتی ای که به Path ئه Binding میدیم ، در کالکشن مون وجود نداشت ، بصورت خودکار سراغ CurrentItem ئه CollectionView میره که .
دو تا موضوع رو قاطی نکنید، جای نقل قول رو ببینید، ببینید پاسخی که دادم مربوط به کدوم پاراگراف شما است، در مورد چیه. اون موضوع Path برای وقتی بود که Path مشخص شده و در مجموعه نیست و در زیر مجموعه اش هست، اینکه ربطی به اون مثال ContentControl نداره. مگه در مثال ContentControl شما Path می بینید؟ در مثال ContentControl ئه Path ای در Binding مشخص نشده :
"Content="{Binding Source={StaticResource listingDataView}}
اینکه اینجا ContentControl کارکرد خاصی داره نه ربطی داره به Path ای که نیست و نه ربطی داره به Content. مربوط به خود ContentControl ئه، پاسخ اش با توصیف یک موضوع دیگه جور در نمیاد.
آها.
یعنی گذاشتنِ اسلش و نذاشتن اش زمانی مهم هست که هم کلِ کالکشن و هم عضوِ CurrentItem از CollectionView مون ، عضو و پروپرتی ای با یک نام داشته باشند که با کاراکترِ اسلش ، منظورمون را میرسونیم که منظورمون ، اون پروپرتی از CurrentItem هست (نه اون پروپرتی از کلِ کالکشن) .
دلایلش متعدد ئه، یا تمایلی به جستجوی خودکار اون Binding ندارید، می خواهید صریحا مشخص کنید که چی رو میخواهید، یا در حالت عادی خود مجموعه انتخاب میشه ولی شما زیر مجموعه رو می خواهید، یا می خواهید از مقدار یک مشخصه خاص که در Path وارد میشه استفاده کنید، در همون توضیحاتش مثال هست و ...
بنابراین در شرایطی که اگه اون نام از پروپرتی ای را که در Path ئه Binding مون مشخص میکنیم ، فقط به عنوانِ یکی از اعضای کالکشن و یا CurrentItem از CollectionView وجود داشته باشه ، در اینجا ، گذاشتن یا نذاشتنِ کاراکترِ اسلش ، فرقی نداره (چه بذاریم یا نذاریم) .
درست میگم؟
در نتیجه آره، فرق خاصی نداره، اما از نظر سرعت اجرا نمی توانیم بگیم یکسان هستند، طبعا اون جستجوی خودکار و رسیدن به زیر مجموعه بدون سربار نیست، شاید اصلا تفاوت محسوسی نباشه اما به هر حال وقتی صریحا مسیرش رو مشخص نکردید به ناچار کار اضافی می کنه تا پیداش کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد .
استاد ، میگم که میشه بجای Validate در Binding ها (Binding هایی که مثلا دو طرفه هستند) ، درون رویدادِ تغییرِ اون پروپرتی ، کد مد نظرمون را بنویسیم . درست میگم؟
فرضا در رویداد TextChanged ئه TextBox ، موارد و کدهای Validate مون را بنویسیم و فرضا در صورتی که مقدار مورد نظر ، خارج از چارچوبِ مورد نظرمون بود ، به کاربر پیام بدیم و یا هر کار دیگه ای (بجای اینکه از Validate در Binding ها استفاده کنیم) .
درست میگم؟


=======================


بعد اینکه استاد ، فرض کنید پروپرتیِ Text ئه TextBox ای را به پروپرتیِ مورد نظرمون (فرضا در لایه ی Model مون و نام این پروپرتی هم FirstName هست) ، Binding ئه دو طرفه انجام دادیم .
یعنی میخوایم هر تغییری که در یکی اِعمال شد ، در اون یکی تاثیر بذاره .
اما از طرفی هم دیتابیس برای ذخیره ی این مقدار هست که در چند رویداد در برنامه مون (مثلا رویداد Load ئه Window یا قبل از اون) ، اطلاعاتش را از دیتابیس فراخونی و به پروپرتیِ مربوط به همین ، وارد و پر میکنیم .

حالا ، مشکل اینه که فرضا کاربر نهایی اومد و درون Text ئه TextBox مون یه چیزی نوشت که چون Binding ئه دو طرفه هست ، باعث میشه پروپرتیِ FirstName ما به روز و تغییر مقدار بده . ما هم فرضا در قسمتِ Set ئه این پروپرتیِ FirstName ، بیایم و دو کد را بنویسیم :

یکی متد RaiseFirstName که باعث میشه به Binding مون در View ، اطلاعی داده بشه تا تغییر کنه . در صورتی که خودِ مقدارِ FirstName ، توسط View مون تغییر کرد و در این صورت ، نیازی نیست که مجددا مقدار View (یا همون Text ئه TextBox مون) مجددا تغییر کنه .

دوم اینکه متدی که دیتابیسِ مربوط به این پروپرتی را به روزرسانی میکنه را فراخونی کنیم . که اینجا هم زمانی که در رویدادِ Load ئه Window ، روالِ واکشیِ اطلاعاتِ مربوط به این پروپرتی را از دیتابیس فراخونی و مقداردهی کنیم ، باز قسمتِ Set ئه این پروپرتی اجرا میشه که باز مججدا باعث میشه که روالِ به روزرسانیِ مربوط به این پروپرتی درون دیتابیس انجام و دیتابیس مجددا به روزرسانی بشه .

بنابراین این روالی که گفتم ، برای Binding ، خوب در نمیاد و باعث میشه مکررا یه چیز ، بی مورد تکرار بشه . ما به یه روالی برای تغییر مقدارِ پروپرتیِ FirstName نیاز داریم که متوجه بشه که تغییرِ مقدارش ، توسطِ View انجام شده (تا در این حالت ، فقط دیتابیس را به روز رسانی کنه) و یا اینکه تغییرِ مقدارش توسط دیتابیس انجام شده (تا با متد RaiseFirstName ، باعث بشه View مقدارش را به روزرسانی کنه) و این طوری ، صرفا یکی از این حالت ها اجرا بشه .
و حتی اینکه متوجه بشه که تغییر مقدارِ پروپرتی ، توسط درونِ خودِ Model انجام شده یا نه .
اما برای این حالت ، نمیدونم چه روالی را باید به کار برد؟
به نظرتون بهترین روال برای این قضیه ، چجوری میشه؟

تشکر استاد .
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
استاد ، میگم که میشه بجای Validate در Binding ها (Binding هایی که مثلا دو طرفه هستند) ، درون رویدادِ تغییرِ اون پروپرتی ، کد مد نظرمون را بنویسیم . درست میگم؟
نه. ابدا. هیچکدوم جای اون یکی رو نمی توانند بگیرند، دو جور واکنش متفاوت در لایه های متفاوت و با شرایط متفاوت ئه. اشکال کار اینه که قبل از اینکه سوال بکنید، کد نمی نویسید و اجرای کد ها رو نمی بینید. و فرضا قبل از اینکه سوال کنید نمی بینید که وقتی مقدار در محدوده مجاز ValidationRules نیست آیا Binding مقدار رو انتقال میده و مشخصه تغییر مقدار میده یا نه.
فرضا در رویداد TextChanged ئه TextBox ، موارد و کدهای Validate مون را بنویسیم و فرضا در صورتی که مقدار مورد نظر ، خارج از چارچوبِ مورد نظرمون بود ، به کاربر پیام بدیم و یا هر کار دیگه ای (بجای اینکه از Validate در Binding ها استفاده کنیم) .
درست میگم؟
نه. TextChanged یعنی چی؟ یعنی عوض شد، نه اینکه میخواد عوض بشه. یعنی بعد از اینکه داده نامعتبر توسط Binding ارسال شد تازه میخواهید برای خراب کاری که قبلا انجام شده کاری بکنید. Validation برای اینه که از ورود داده نامعتبر به اون فرضا مشخصه Age در MyDataSource جلوگیری بشه، پیشگیری بشه، نه اینکه بعدا بخواهید درستش کنید. وقتی داده نامعتبر توسط Binding ثبت شد در TextChanged جز عذر خواهی از MyDataSource و تغییر مقدار Text هیچ کار دیگه ای نمی شه کرد.
بعد اینکه استاد ، فرض کنید پروپرتیِ Text ئه TextBox ای را به پروپرتیِ مورد نظرمون (فرضا در لایه ی Model مون و نام این پروپرتی هم FirstName هست) ، Binding ئه دو طرفه انجام دادیم .
یعنی میخوایم هر تغییری که در یکی اِعمال شد ، در اون یکی تاثیر بذاره .
اما از طرفی هم دیتابیس برای ذخیره ی این مقدار هست که در چند رویداد در برنامه مون (مثلا رویداد Load ئه Window یا قبل از اون) ، اطلاعاتش را از دیتابیس فراخونی و به پروپرتیِ مربوط به همین ، وارد و پر میکنیم .

حالا ، مشکل اینه که فرضا کاربر نهایی اومد و درون Text ئه TextBox مون یه چیزی نوشت که چون Binding ئه دو طرفه هست ، باعث میشه پروپرتیِ FirstName ما به روز و تغییر مقدار بده . ما هم فرضا در قسمتِ Set ئه این پروپرتیِ FirstName ، بیایم و دو کد را بنویسیم :

یکی متد RaiseFirstName که باعث میشه به Binding مون در View ، اطلاعی داده بشه تا تغییر کنه . در صورتی که خودِ مقدارِ FirstName ، توسط View مون تغییر کرد و در این صورت ، نیازی نیست که مجددا مقدار View (یا همون Text ئه TextBox مون) مجددا تغییر کنه .

دوم اینکه متدی که دیتابیسِ مربوط به این پروپرتی را به روزرسانی میکنه را فراخونی کنیم . که اینجا هم زمانی که در رویدادِ Load ئه Window ، روالِ واکشیِ اطلاعاتِ مربوط به این پروپرتی را از دیتابیس فراخونی و مقداردهی کنیم ، باز قسمتِ Set ئه این پروپرتی اجرا میشه که باز مججدا باعث میشه که روالِ به روزرسانیِ مربوط به این پروپرتی درون دیتابیس انجام و دیتابیس مجددا به روزرسانی بشه .

بنابراین این روالی که گفتم ، برای Binding ، خوب در نمیاد و باعث میشه مکررا یه چیز ، بی مورد تکرار بشه . ما به یه روالی برای تغییر مقدارِ پروپرتیِ FirstName نیاز داریم که متوجه بشه که تغییرِ مقدارش ، توسطِ View انجام شده (تا در این حالت ، فقط دیتابیس را به روز رسانی کنه) و یا اینکه تغییرِ مقدارش توسط دیتابیس انجام شده (تا با متد RaiseFirstName ، باعث بشه View مقدارش را به روزرسانی کنه) و این طوری ، صرفا یکی از این حالت ها اجرا بشه .
و حتی اینکه متوجه بشه که تغییر مقدارِ پروپرتی ، توسط درونِ خودِ Model انجام شده یا نه .
اما برای این حالت ، نمیدونم چه روالی را باید به کار برد؟
به نظرتون بهترین روال برای این قضیه ، چجوری میشه؟
در set پیش از هر کاری مقدار value رو با مقدار فعلی مشخصه مقایسه کنید، اگر برابر بودند (یعنی تغییری در مقدار مشخصه رخ نمیده) return کنید، یعنی هیچ کاری نکنید.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
نه. ابدا. هیچکدوم جای اون یکی رو نمی توانند بگیرند، دو جور واکنش متفاوت در لایه های متفاوت و با شرایط متفاوت ئه. اشکال کار اینه که قبل از اینکه سوال بکنید، کد نمی نویسید و اجرای کد ها رو نمی بینید. و فرضا قبل از اینکه سوال کنید نمی بینید که وقتی مقدار در محدوده مجاز ValidationRules نیست آیا Binding مقدار رو انتقال میده و مشخصه تغییر مقدار میده یا نه.

نه. TextChanged یعنی چی؟ یعنی عوض شد، نه اینکه میخواد عوض بشه. یعنی بعد از اینکه داده نامعتبر توسط Binding ارسال شد تازه میخواهید برای خراب کاری که قبلا انجام شده کاری بکنید. Validation برای اینه که از ورود داده نامعتبر به اون فرضا مشخصه Age در MyDataSource جلوگیری بشه، پیشگیری بشه، نه اینکه بعدا بخواهید درستش کنید. وقتی داده نامعتبر توسط Binding ثبت شد در TextChanged جز عذر خواهی از MyDataSource و تغییر مقدار Text هیچ کار دیگه ای نمی شه کرد.

سلامی مجدد استاد .
خیلی ممنون .

بله متوجه شدم .
البته یه مقدار باید بیشتر توضیح میدادم .
هر چند برای validate ، میشه یه کلاسی بسازیم که از کلاس ValidationRule ارث بری کنه و متد Validate اش را override کنیم و در پروپرتیِ Binding.ValidationRules از شی اش استفاده کنیم (با ذکر نکته درباره ی پروپرتی ValidatesOnTargetUpdated که در لینک زیر برای Validation rule example گفت) :


و البته در سایت مایکروسافت هم توضیح داده .
هر چند برای اینکه هر بار کلاسی جدید برای این قضیه برای هر Binding نسازیم ، میتونیم دلیگیت ای تحویل بگیریم و در متد Validate ، اون دلیگیت را اجرا کنیم . درسته؟

=========================

اما منظور من اعتبارسنجی از یه لحاظ دیگه بود .
ببینید ، اعتبار سنجی زمانی کاربرد داره که در Binding مون ، فقط یه Binding Target (مثل TextBox که کاربر نهایی قراره توش چیزهایی بنویسه) ، بخواد Binding Source Property مون (مثلا پروپرتی FirstName در کلاس Student) را به روز کنه .
یعنی معمولا بر عکسِ این حالت برای اعتبار سنجی ، یا اتفاق نمیافته یا بسیار به ندرت اتفاق میافته که کاری نداریم .
درسته؟

حالا در این حالت ، میایم پروپرتیِ Binding.UpdateSourceTrigger را روی مقدارِ UpdateSourceTrigger.LostFocus تنظیم میکنیم (که انگار برای پروپرتیِ TextBox.Text ، بصورت پیش فرض روی همین حالت هست) برای اینکه تا زمانی که فوکوس از شیِ TextBox مون خارج نشد ، Binding Source مون را به روزرسانی نکنه .

بنابراین اینجا فقط کافیه که قبل از ، از دست دادنِ فوکوسِ TextBox ، مقدارش را خودمون اعتبارسنجی کنیم . مثلا در رویدادِ KeyDown ئه TextBox که هر کلیدی فشار داده میشه را دریافت میکنیم . (حتی در این رویداد هم TextBox.Text ، مقداردهی نمیشه و مقدارِ قبلیِ خودش را داره) .

و همچنین در رویدادِ TextChanged ئه TextBox ، بعد از تغییر مقدارِ TextBox.Text ، میتونیم کارهای دیگه را انجام بدیم .
مثلا در این رویداد میتونیم TextBox.Text را کاملا پاک کنیم (اما در اون رویداد ، با این کار ، یه کاراکتر ممکنه باقی بمونه) .
و همچنین میدونید که وقتی پروپرتیِ Binding.UpdateSourceTrigger را روی مقدارِ UpdateSourceTrigger.LostFocus تنظیم کردیم ، صرفا با تغییرِ مقدارِ TextBox.Text ، باعث نمیشه که Binding Source مون به روزرسانی بشه (بنابراین در این حالت ، ما فرصت داریم که روی TextBox.Text کنترل و محدودیت و اعتبارسنجی های مون را انجام و دوباره مقدارش را تغییر بدیم) .
درست میگم؟

و همچنین اینکه شما در winform ، برای اعتبارسنجیِ Text ئه TextBox ها (و بقیه ی کنترل ها) ، از چه روشی استفاده میکردید؟


در set پیش از هر کاری مقدار value رو با مقدار فعلی مشخصه مقایسه کنید، اگر برابر بودند (یعنی تغییری در مقدار مشخصه رخ نمیده) return کنید، یعنی هیچ کاری نکنید.

نه .
منظورم این نیست .
ببینید ، کد FirstName در کلاس Student مون اینه و در View هم یه TextBox داریم که Text اش ، به FirstName مون Binding شده :

C#:
        private string firstName;
      
        public string FirstName
        {
            get
            {
                return this.firstName;
            }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    this.RaisePropertyChanged();
                }
            }
        }

تغییرِ مقدار که توسط کاربر در Text ئه TextBox صورت بگیره (و بعد از خارج شدن از فوکوسِ TextBox) (فرضا کاربر نهایی ، مقدارش را "Mohsen" گذاشت) ، بخشِ set ئه FirstName اجرا میشه و (مقدار FirstName به "Mohsen" تغییر پیدا میکنه) و بعد متد RaisePropertyChanged اجرا میشه که باعث میشه مجددا تغییرِ مقدارِ FirstName مون را به Binding مون ، اطلاع رسانی کنه .
پس با فراخونیِ این متد RaisePropertyChanged ، مجددا باعث میشه که Text ئه TextBox مون ، با اونکه خودش مقدارِ "Mohsen" را داره اما دوباره باز هم به همین مقدارِ "Mohsen" ، تنظیم و set بشه (البته من نمیدونم روالِ Binding آیا بررسی میکنه که مقدارش همون نباشه و بعدا تنظیم کنه) .

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

اما بجز این ، با تغییر مقدار FirstName ، میخوایم فیلد (و رکورد های) دیتابیس مربوط به این پروپرتی هم به روزرسانی بشه .
بنابراین به کد بالا ، همچین متدی را اضافه میکنیم :

C#:
        private string firstName;
      
        public string FirstName
        {
            get
            {
                return this.firstName;
            }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    this.RaisePropertyChanged();
                    this.UpdateDatabase();
                }
            }
        }

فعلا از بدنه ی متد UpdateDatabase که باعث میشه مقدار فیلد مورد نظر در دیتابیس برای پروپرتیِ مربوطه ، به روزرسانی بشه ، صرف نظر میکنیم .
آیا این روش درسته که مثلا در بدنه ی set ئه پروپرتی مون (FirstName) ، بیایم و متد UpdateDatabase را بکار ببریم یا روال دیگه ای بهتر از این هست؟

خیلی ممنون استاد .
 
آخرین ویرایش:

the_king

مدیرکل انجمن
هر چند برای اینکه هر بار کلاسی جدید برای این قضیه برای هر Binding نسازیم ، میتونیم دلیگیت ای تحویل بگیریم و در متد Validate ، اون دلیگیت را اجرا کنیم . درسته؟
متوجه هر بار نمی شوم، وقتی موضوع اعتبار سنجی متفاوتی هست، اختصاص کلاس جدید به اون Validation اصولی ئه، نه ایرادی داره و نه یک کار اضافی است.
اما منظور من اعتبارسنجی از یه لحاظ دیگه بود.
ببینید ، اعتبار سنجی زمانی کاربرد داره که در Binding مون ، فقط یه Binding Target (مثل TextBox که کاربر نهایی قراره توش چیزهایی بنویسه) ، بخواد Binding Source Property مون (مثلا پروپرتی FirstName در کلاس Student) را به روز کنه .
یعنی معمولا بر عکسِ این حالت برای اعتبار سنجی ، یا اتفاق نمیافته یا بسیار به ندرت اتفاق میافته که کاری نداریم .
وقتی Binding در FirstName یک عبارت نامعتبر رو نوشت، دیگه تموم ئه، مقدار نامعتبر که نباید ارسال میشد از View به ViewModel منتقل شد و تمام، کاری که نباید اتفاق می افتاد افتاد. فرضا کاربر در اون TextBox شما یک مقدار نامعتبر از نظر FirstName رو نوشت، عبارتی که غیر قابل قبول ئه و نباید در FirstName قرار بگیره. اینکه اون طرف مشخصه FirstName و Model در واکنش به این مقدار نامعتبر چه رفتاری داره بحثش جدا است، اما اعتبار سنجی در View درست عمل نکرده، حالا شما می گویید که در رویداد TextChanged این اعتبار سنجی انجام بشه که توضیح دادم کار نمی کنه، ناموفق ئه، چون بعد از اینکه Binding مقدار رو منتقل کرد تازه در TextChanged متوجه می شوید.
اینکه می گویید برعکس این حالت اعتبار سنجی، متوجه نمی شوم اعتبار سنجی برعکس منظورتون چیه. در ضمن کاری نداریم یعنی چی؟ یا چیزی درست ئه یا نیست، کمی درست یا کمی اشتباه نمیشه. اگر موردی هست که با نظریه تون جور در نمیاد نقض اش می کنه، کلا نظریه اشتباه میشه، چه مورد نادر باشه و چه نباشه، نمی توانید کنارش بذارید و بگید بهش کاری نداریم.
حالا در این حالت ، میایم پروپرتیِ Binding.UpdateSourceTrigger را روی مقدارِ UpdateSourceTrigger.LostFocus تنظیم میکنیم (که انگار برای پروپرتیِ TextBox.Text ، بصورت پیش فرض روی همین حالت هست) برای اینکه تا زمانی که فوکوس از شیِ TextBox مون خارج نشد ، Binding Source مون را به روزرسانی نکنه.
بنابراین اینجا فقط کافیه که قبل از ، از دست دادنِ فوکوسِ TextBox ، مقدارش را خودمون اعتبارسنجی کنیم . مثلا در رویدادِ KeyDown ئه TextBox که هر کلیدی فشار داده میشه را دریافت میکنیم . (حتی در این رویداد هم TextBox.Text ، مقداردهی نمیشه و مقدارِ قبلیِ خودش را داره) .

و همچنین در رویدادِ TextChanged ئه TextBox ، بعد از تغییر مقدارِ TextBox.Text ، میتونیم کارهای دیگه را انجام بدیم .
مثلا در این رویداد میتونیم TextBox.Text را کاملا پاک کنیم (اما در اون رویداد ، با این کار ، یه کاراکتر ممکنه باقی بمونه) .
و همچنین میدونید که وقتی پروپرتیِ Binding.UpdateSourceTrigger را روی مقدارِ UpdateSourceTrigger.LostFocus تنظیم کردیم ، صرفا با تغییرِ مقدارِ TextBox.Text ، باعث نمیشه که Binding Source مون به روزرسانی بشه (بنابراین در این حالت ، ما فرصت داریم که روی TextBox.Text کنترل و محدودیت و اعتبارسنجی های مون را انجام و دوباره مقدارش را تغییر بدیم) .
درست میگم؟
نه. اولا شما شرط LostFocus رو بر این اساس قرار می دهید که باید حتما Focus از اون کنترل بره تا مقدار Bind شده ثبت بشه، این مساله بر اساس منطق طراح اون View باید باشه، نه اینکه روش ابداعی تون برای Validation درست کار کنه، برای طراح View که نباید شرایط تحمیل کنید. فرضا در حالتی که کنترل قابل Focus دیگری نباشه LostFocus رخ نمیده. ثانیا اینکه کنترلی قابلیت مشخص کردن قالب ورودی و یا فیلتر کردن کاراکتر های ورودی رو داشته باشه، جایگزین Validation برای Binding نیست، فرضا در TextBox ای که تاریخ دریافت می کنه اینکه متن رو به قالب تاریخ دربیاورید اصلا چیز بدی نیست، خیلی هم خوبه، اما این اصلا ربطی به اینکه تاریخ مورد نظر قابل قبول هست یا نه نداره.
ثالثا شما دارید برای اینکه Validation در Binding رو کنار بذارید راهکار پیدا می کنید، برای چی؟ با چه منطقی باید برای اینکه در Binding ئه Validation نباشه راهکار پیدا کنید؟
و همچنین اینکه شما در winform ، برای اعتبارسنجیِ Text ئه TextBox ها (و بقیه ی کنترل ها) ، از چه روشی استفاده میکردید؟
اعتبار سنجی هر بخش جدا است، هیچکدوم هم جای دیگری رو نمی گیره. فرضا TextBox قرار بوده اسم Filename دریافت کنه، پس کاراکتر هایی مثل * در KeyPress و TextChanged فیلتر می شوند. در کلیک دکمه OK باید اعتبار اون Filename تایید بشه، فرضا اگر رشته تهی بود یا اون فایل وجود نداشت عملیات ثبت انجام نشه و به کاربر پیام خطا نشون بده. در set مشخصه ای که اون Filename رو دریافت می کنه فرضا اگر value ئه null باشه با ArgumentNullException واکنش نشون بده. در متد مربوط به ثبت تنظیمات اگر Filename برابر BackupFilename باشه با ArgumentOutOfRangeException واکنش نشون بده و ...
نه .
منظورم این نیست .
ببینید ، کد FirstName در کلاس Student مون اینه و در View هم یه TextBox داریم که Text اش ، به FirstName مون Binding شده :

C#:
        private string firstName;
  
        public string FirstName
        {
            get
            {
                return this.firstName;
            }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    this.RaisePropertyChanged();
                }
            }
        }

تغییرِ مقدار که توسط کاربر در Text ئه TextBox صورت بگیره (و بعد از خارج شدن از فوکوسِ TextBox) (فرضا کاربر نهایی ، مقدارش را "Mohsen" گذاشت) ، بخشِ set ئه FirstName اجرا میشه و (مقدار FirstName به "Mohsen" تغییر پیدا میکنه) و بعد متد RaisePropertyChanged اجرا میشه که باعث میشه مجددا تغییرِ مقدارِ FirstName مون را به Binding مون ، اطلاع رسانی کنه .
پس با فراخونیِ این متد RaisePropertyChanged ، مجددا باعث میشه که Text ئه TextBox مون ، با اونکه خودش مقدارِ "Mohsen" را داره اما دوباره باز هم به همین مقدارِ "Mohsen" ، تنظیم و set بشه (البته من نمیدونم روالِ Binding آیا بررسی میکنه که مقدارش همون نباشه و بعدا تنظیم کنه) .
اولا اینکه RaisePropertyChanged در کلاس موقع تغییر مقدار مشخصه رخ بده عادیه، کلاس خودش کاری نداره که چند دریافت کننده اون طرف منتظر این RaisePropertyChanged هستند، الزاما فقط یک Binding منتظر نیست، نمی توانید بقیه رو بی خبر بذارید، چون نمی خواهید اون Binding با خبر بشه، به این بخش کاری نداشته باشید، کار اش درسته، بهش دست نزنید.
و اون طرف حتی اگر مقدار تکراری در Text قرار بگیره مشکلی پیش نمیاد، NET. مملو از ثبت دوباره مقادیر یکسان در مشخصه ها است.
اما بجز این ، با تغییر مقدار FirstName ، میخوایم فیلد (و رکورد های) دیتابیس مربوط به این پروپرتی هم به روزرسانی بشه .
بنابراین به کد بالا ، همچین متدی را اضافه میکنیم :

C#:
        private string firstName;
  
        public string FirstName
        {
            get
            {
                return this.firstName;
            }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    this.RaisePropertyChanged();
                    this.UpdateDatabase();
                }
            }
        }

فعلا از بدنه ی متد UpdateDatabase که باعث میشه مقدار فیلد مورد نظر در دیتابیس برای پروپرتیِ مربوطه ، به روزرسانی بشه ، صرف نظر میکنیم .
آیا این روش درسته که مثلا در بدنه ی set ئه پروپرتی مون (FirstName) ، بیایم و متد UpdateDatabase را بکار ببریم یا روال دیگه ای بهتر از این هست؟
بستگی به منطق Model داره، ممکنه Model بخواد اول مقادیر فیلد ها رو بگیره و همه فیلد های یک رکورد رو یکجا ثبت کنه، یا هر فیلد رو مجزا همون موقع دریافت ثبت کنه، یا برای ثبت منتظر رخداد خاصی بشه و بعدا ثبت اش کنه یا با فواصل زمانی خاصی بصورت دوره ای ثبت اش کنه و ...
 

SajjadKhati

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

سلامی مجدد .
خیلی ممنون استاد .

منظورم این هست که همونطور که Command ها هم همین شرایط را دارن و با پیاده سازیِ اینترفیس ICommand ، برای هر نوعی از پروپرتیِ Command ای که میسازیم ، یه کلاس جدید از Command ها درست میکنیم و متد execute ئه مربوط به خودش را مینویسیم ، اما کلاس RelayCommand اومد با گرفتنِ دلیگیت (Action) ، این نیازمندی را حل کرد و دیگه لازم نیست هر بار کلاسِ Command درست کنیم . فقط کافیه که از کلاسِ RelayCommand استفاده و متدی که در کلاسِ جاری مون نوشتیم ، با دلیگیتِ Action برابر باشه .

بنابراین همین کار را میتونیم با متدِ Validate در کلاسی که از ValidationRule ارث بری میکنیم ، انجام بدیم (یعنی در این متد ، یه دلیگیت ای که از کاربر میگیریم را اجرا کنیم) . مثل کاری که RelayCommand در متد Execute میکنه .

اینکه می گویید برعکس این حالت اعتبار سنجی، متوجه نمی شوم اعتبار سنجی برعکس منظورتون چیه. در ضمن کاری نداریم یعنی چی؟ یا چیزی درست ئه یا نیست، کمی درست یا کمی اشتباه نمیشه. اگر موردی هست که با نظریه تون جور در نمیاد نقض اش می کنه، کلا نظریه اشتباه میشه، چه مورد نادر باشه و چه نباشه، نمی توانید کنارش بذارید و بگید بهش کاری نداریم.

این تیکه را اول جواب بدم .
منظورم از برعکسِ این حالت در اعتبارسنجی ، اینه که مثلا با تغییرِ اعضای Model (و در نتیجه به روزرسانیِ View) ، نمیایم در این حالت ، اعتبارسنجی بذاریم . چون معمولا Model را برنامه نویس خودش تغییر میده .

مثلا وقتی که پروپرتیِ FirstName در خودِ Model توسط برنامه نویس تغییر کرد (منظورم وقتی که مقدار Text ئه TextBox تغییر و در نتیجه FirstName تغییر کنه ، نیست . یعنی منظورم وقتی که View باعثِ تغییرِ مقدار FirstName بشه ، نیست) ، و بعد بخواد View را توسط Binding تغییر بده (یعنی باعث تغییر مقدار Text ئه TextBox بشه) ، در این حالت که اعتبارسنجی ، معنی نداره و انجام نمیشه .
فقط زمانی که View ، تغییر کنه (و بخواد مقدار اعضای Model را تغییر بده) ، اعتبار سنجی معنا داره . درسته؟

وقتی Binding در FirstName یک عبارت نامعتبر رو نوشت، دیگه تموم ئه، مقدار نامعتبر که نباید ارسال میشد از View به ViewModel منتقل شد و تمام، کاری که نباید اتفاق می افتاد افتاد. فرضا کاربر در اون TextBox شما یک مقدار نامعتبر از نظر FirstName رو نوشت، عبارتی که غیر قابل قبول ئه و نباید در FirstName قرار بگیره. اینکه اون طرف مشخصه FirstName و Model در واکنش به این مقدار نامعتبر چه رفتاری داره بحثش جدا است، اما اعتبار سنجی در View درست عمل نکرده، حالا شما می گویید که در رویداد TextChanged این اعتبار سنجی انجام بشه که توضیح دادم کار نمی کنه، ناموفق ئه، چون بعد از اینکه Binding مقدار رو منتقل کرد تازه در TextChanged متوجه می شوید.

نه. اولا شما شرط LostFocus رو بر این اساس قرار می دهید که باید حتما Focus از اون کنترل بره تا مقدار Bind شده ثبت بشه، این مساله بر اساس منطق طراح اون View باید باشه، نه اینکه روش ابداعی تون برای Validation درست کار کنه، برای طراح View که نباید شرایط تحمیل کنید. فرضا در حالتی که کنترل قابل Focus دیگری نباشه LostFocus رخ نمیده. ثانیا اینکه کنترلی قابلیت مشخص کردن قالب ورودی و یا فیلتر کردن کاراکتر های ورودی رو داشته باشه، جایگزین Validation برای Binding نیست، فرضا در TextBox ای که تاریخ دریافت می کنه اینکه متن رو به قالب تاریخ دربیاورید اصلا چیز بدی نیست، خیلی هم خوبه، اما این اصلا ربطی به اینکه تاریخ مورد نظر قابل قبول هست یا نه نداره.

بله . نکته ی خوبی گفتید . مخصوصا اینکه اگه کنترلِ قابلِ فوکوسِ دیگه ای در اون پنل یا windows نباشه ، رویداد LostFocus ئه اون کنترل رخ نمیده و در نتیجه Binding اتفاق نمیافته و داده منتقل نمیشه .
البته حتی اگه کنترل هایی که فوکوس شون هم غیر فعال شده باشه ، باز هم همین مشکل وجود داره . فرضا اگه بعد از نوشتن در TextBox ، روی کنترل یا دکمه ای که فوکوس اش غیر فعال شده باشه کلیک کنیم ، بنابراین Binding ای رخ نمیده .

اما پس چرا Binding ای که برای Text ئه TextBox انجام میدیم ، بصورت پیش فرض روی این حالتِ LostFocus گذاشتن؟
هر چند احتمال این واقعه کمتر هست اما خیلی هم کم نیست که همچین مواردی اتفاق بیفته .

البته نکات دیگه ای هم هست که هم به این قسمت مربوط میشد اما بیشتر به ادامه ی متن هایی که مطرح کردید (متن های زیر) ، مربوط میشه . پس در ادامه میگم .

ثالثا شما دارید برای اینکه Validation در Binding رو کنار بذارید راهکار پیدا می کنید، برای چی؟ با چه منطقی باید برای اینکه در Binding ئه Validation نباشه راهکار پیدا کنید؟

راه کار که فرضا به عنوان جایگزینش هم بشه بهش نگاه کرد مثل همین چیزی که شما هم در winform در متن زیر بهش اشاره کردید .
اما نه اینکه از اینها استفاده نشه .

اعتبار سنجی هر بخش جدا است، هیچکدوم هم جای دیگری رو نمی گیره. فرضا TextBox قرار بوده اسم Filename دریافت کنه، پس کاراکتر هایی مثل * در KeyPress و TextChanged فیلتر می شوند. در کلیک دکمه OK باید اعتبار اون Filename تایید بشه، فرضا اگر رشته تهی بود یا اون فایل وجود نداشت عملیات ثبت انجام نشه و به کاربر پیام خطا نشون بده. در set مشخصه ای که اون Filename رو دریافت می کنه فرضا اگر value ئه null باشه با ArgumentNullException واکنش نشون بده. در متد مربوط به ثبت تنظیمات اگر Filename برابر BackupFilename باشه با ArgumentOutOfRangeException واکنش نشون بده و ...

خوب ، اینی که گفتید ، همون راهکاری هست که جایگزینِ استفاده از کلاس ValidationRule در wpf هست دیگه .
که من هم منظورم همین بود که در متن بالاتر ، من را زیاد به این روش توصیه نمیکردید .
البته میگم ، منظورم این نیست که از Validation در wpf استفاده نشه . صرفا به عنوان یه راهکار دیگه هم میشه بهش نگاه کرد .

اولا اینکه RaisePropertyChanged در کلاس موقع تغییر مقدار مشخصه رخ بده عادیه، کلاس خودش کاری نداره که چند دریافت کننده اون طرف منتظر این RaisePropertyChanged هستند، الزاما فقط یک Binding منتظر نیست، نمی توانید بقیه رو بی خبر بذارید، چون نمی خواهید اون Binding با خبر بشه، به این بخش کاری نداشته باشید، کار اش درسته، بهش دست نزنید.
و اون طرف حتی اگر مقدار تکراری در Text قرار بگیره مشکلی پیش نمیاد، NET. مملو از ثبت دوباره مقادیر یکسان در مشخصه ها است.

بستگی به منطق Model داره، ممکنه Model بخواد اول مقادیر فیلد ها رو بگیره و همه فیلد های یک رکورد رو یکجا ثبت کنه، یا هر فیلد رو مجزا همون موقع دریافت ثبت کنه، یا برای ثبت منتظر رخداد خاصی بشه و بعدا ثبت اش کنه یا با فواصل زمانی خاصی بصورت دوره ای ثبت اش کنه و ...

خیلی ممنون استاد .
 

the_king

مدیرکل انجمن
سلامی مجدد .
خیلی ممنون استاد .

منظورم این هست که همونطور که Command ها هم همین شرایط را دارن و با پیاده سازیِ اینترفیس ICommand ، برای هر نوعی از پروپرتیِ Command ای که میسازیم ، یه کلاس جدید از Command ها درست میکنیم و متد execute ئه مربوط به خودش را مینویسیم ، اما کلاس RelayCommand اومد با گرفتنِ دلیگیت (Action) ، این نیازمندی را حل کرد و دیگه لازم نیست هر بار کلاسِ Command درست کنیم . فقط کافیه که از کلاسِ RelayCommand استفاده و متدی که در کلاسِ جاری مون نوشتیم ، با دلیگیتِ Action برابر باشه .
بله.
بنابراین همین کار را میتونیم با متدِ Validate در کلاسی که از ValidationRule ارث بری میکنیم ، انجام بدیم (یعنی در این متد ، یه دلیگیت ای که از کاربر میگیریم را اجرا کنیم) . مثل کاری که RelayCommand در متد Execute میکنه .
نمی توانید بگید می توانیم، چون باید در نظر بگیرید که قراره Validate بر اساس یکسری مشخصه هایی Validation رو انجام بده که در delegate مشخص نمی شه. برای همین فقط برخی اوقات می توانید، نه همیشه. دلیلش اینه که ICommand و ValidationRule در نحوه استفاده شباهت کمی بهم دارند، ICommand رو با RelayCommand جایگزین می کنید چون RelayCommand و <RelayCommand<T هر آنچه که لازم دارید ارائه می کنه، اما ValidationRule برای پارامتر های Validation قابلیت خاصی نداره، برای همینه که abstract ئه. اگر وراثتی نداشته باشه برای تعیین اون فرضا مشخصه های Min و Max در XAML نمیشه کاری کرد و طبعا رفتار متد Validate به اون Min و Max وابسته است. شما یک کلاس عمومی از ValidationRule می سازید با این فلسفه که انواع Validation ها رو با کمک تعداد زیادی متد Validate انجام بده، در صورتی که هر کدوم از اون Validate ها پارامتر های خاص خودشون رو دارند که در اون کلاس عمومی شما نیست.
این تیکه را اول جواب بدم .
منظورم از برعکسِ این حالت در اعتبارسنجی ، اینه که مثلا با تغییرِ اعضای Model (و در نتیجه به روزرسانیِ View) ، نمیایم در این حالت ، اعتبارسنجی بذاریم . چون معمولا Model را برنامه نویس خودش تغییر میده .
حالا متوجه شدم که منظورتون از برعکس چیه. ValidationRule کاری به برعکس اش نداره. ValidationRule فقط به داده ورودی کاربر کار داره، کاری به داده ای که میخواد در کنترل به کاربر نشون داده بشه نداره. مگه شما تصور می کنید که Binding اگر دو طرفه باشه اون ValidationRule ئه برای طرف دوم هم Validation انجام میده؟
مثلا وقتی که پروپرتیِ FirstName در خودِ Model توسط برنامه نویس تغییر کرد (منظورم وقتی که مقدار Text ئه TextBox تغییر و در نتیجه FirstName تغییر کنه ، نیست . یعنی منظورم وقتی که View باعثِ تغییرِ مقدار FirstName بشه ، نیست) ، و بعد بخواد View را توسط Binding تغییر بده (یعنی باعث تغییر مقدار Text ئه TextBox بشه) ، در این حالت که اعتبارسنجی ، معنی نداره و انجام نمیشه .
اعتبار سنجی معنی داره، اما به ValidationRule ربطی نداره. اون TextBox باید بر اساس ظاهرش و قالبی که برای نمایش در نظر گرفته تصمیم بگیره که اون مقدار دریافت شده از Binding رو به شکلی نشون بده یا اصلا اگه معتبر نمی دونه میتونه نشون نده. این میشه اعتبار سنجی برای داده ای که فرضا از FirstName میاد. اما چرا میگم ربطی به ValidationRule نداره؟ چون اون Validation رو برای شیوه نمایش داده به کاربر می خواهید، ولی ValidationRule برای اعتبار سنجی داده ای است که از کاربر دریافت می کنید، یعنی داده ورودی کاربر رو اعتبار سنجی می کنه، داده ای که از Model و ViewModel میاد داده ورودی کاربر نیست.
Provides a way to create a custom rule in order to check the validity of user input
فقط زمانی که View ، تغییر کنه (و بخواد مقدار اعضای Model را تغییر بده) ، اعتبار سنجی معنا داره . درسته؟
اعتبار سنجی در همه دریافت مقادیر معنی داره، چه در Model و چه View و چه ViewModel و مستقل از اینکه معماری چیه. هیچ جایی نیست که معنی نداشته باشه، اما ValidationRule اختصاصی برای اعتبار سنجی داده ورودی از کاربر ئه، یعنی داده ای که در View از کاربر دریافت میشه، نه چیز دیگری.
اما پس چرا Binding ای که برای Text ئه TextBox انجام میدیم ، بصورت پیش فرض روی این حالتِ LostFocus گذاشتن؟
روی چیزی تنظیم می کنند که در بیشتر حالات مناسب ئه، چون معمولا مادامی که کاربر داره متن رو تغییر میده کار اش تموم نشده و قصد ارسال اش رو نداره. الزاما قرار نیست رفتار پیشفرض کنترل همیشه انتخاب مورد تمایل شما باشه، مثلا مادامی که دارید در NumericUpDown مقدار تایپ می کنید، مقدار اش به decimal تبدیل نمیشه و تاثیری در Value نداره و رخداد ValueChanged رخ نمیده، یعنی در ظاهر کاربر مقدار رو عوض کرده ولی برنامه طوری رفتار می کنه که انگار متوجه نیست، یا باید Focus رو جابجا کنید یا Enter رو فشار دهید تا تاثیر بذاره.
خوب ، اینی که گفتید ، همون راهکاری هست که جایگزینِ استفاده از کلاس ValidationRule در wpf هست دیگه .
نه. هیچکدوم جایگزین نیست، شما دائم می خواهید بین موارد انتخابی مثل انتخاب بین قاشق یا چنگال کنید، هر کدوم جای خودشون مفید هستند.
 

SajjadKhati

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

سلامی مجدد
خیلی ممنون استاد از جواب کامل تون .
نه ، همچین فرضی نکردم .

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

استاد توی قضیه ی Routed Event ها ، اگه بخوایم برای رویدادی که استراتژیِ Bubble داره ، در والدش یه هندلری بنویسیم که اگه فرزندانِ اون والد ، اگه اون Routed Event را داشته باشن اما ما نخوایم فرزندانشون به عنوانِ Routed Event از اون رویداد استفاده کنن و بخوایم که اون Routed Event را فقط همون والد ازش استفاده و فراخونی کنه ، فقط چاره ی کار اینه که در اون Routed Event ، شرطِ :

C#:
if (sender != e.Source)
    return;

را بنویسیم؟
تا اگه e.Source که همون کنترلی هست که روش کلیک انجام شد و باعثِ فراخونیِ رویداد شد ، با کنترلی که هندلرمون (که همین CheckBoxRoutedEvent_Click در مثال زیر هست) را براش نوشتیم ، یک کنترل نبودن ، از رویداد بیرون بیاد و ادامه ی کدها را اجرا نکنه . درسته؟

مثلا در مثال زیر :

XML:
        <CheckBox Click="CheckBoxRoutedEvent_Click" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top">
            <Button  Width="70" Height="20" Content="salam"/>
        </CheckBox>

اگه بخوایم فقط زمانی که اون CheckBox مون براش رویداد Click اتفاق افتاد ، فقط در این صورت ، این رویدادِ CheckBoxRoutedEvent_Click اجرا بشه (یعنی زمانی که رویدادِ Click ئه فرزندش که Button هست اتفاق افتاد ، اون رویدادِ CheckBoxRoutedEvent_Click اجرا نشه) ، در این صورت فقط یک راه حل داریم اون هم این شرطی هست که در رویدادش در کد زیر بنویسیم؟ :

C#:
        private void CheckBoxRoutedEvent_Click(object sender, RoutedEventArgs e)
        {
            if (sender != e.Source)
                return;

            MessageBox.Show(e.Source.ToString());
        }

یا اینکه روش دیگه ای هم هست؟
نمیدونم قبلا در مسائل Routed Event ها این را پرسیدم یا نه .

تشکر استاد .
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد از جواب کامل تون .
نه ، همچین فرضی نکردم .

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

استاد توی قضیه ی Routed Event ها ، اگه بخوایم برای رویدادی که استراتژیِ Bubble داره ، در والدش یه هندلری بنویسیم که اگه فرزندانِ اون والد ، اگه اون Routed Event را داشته باشن اما ما نخوایم فرزندانشون به عنوانِ Routed Event از اون رویداد استفاده کنن و بخوایم که اون Routed Event را فقط همون والد ازش استفاده و فراخونی کنه ، فقط چاره ی کار اینه که در اون Routed Event ، شرطِ :

C#:
if (sender != e.Source)
    return;

را بنویسیم؟
تا اگه e.Source که همون کنترلی هست که روش کلیک انجام شد و باعثِ فراخونیِ رویداد شد ، با کنترلی که هندلرمون (که همین CheckBoxRoutedEvent_Click در مثال زیر هست) را براش نوشتیم ، یک کنترل نبودن ، از رویداد بیرون بیاد و ادامه ی کدها را اجرا نکنه . درسته؟

مثلا در مثال زیر :

XML:
        <CheckBox Click="CheckBoxRoutedEvent_Click" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top">
            <Button  Width="70" Height="20" Content="salam"/>
        </CheckBox>

اگه بخوایم فقط زمانی که اون CheckBox مون براش رویداد Click اتفاق افتاد ، فقط در این صورت ، این رویدادِ CheckBoxRoutedEvent_Click اجرا بشه (یعنی زمانی که رویدادِ Click ئه فرزندش که Button هست اتفاق افتاد ، اون رویدادِ CheckBoxRoutedEvent_Click اجرا نشه) ، در این صورت فقط یک راه حل داریم اون هم این شرطی هست که در رویدادش در کد زیر بنویسیم؟ :

C#:
        private void CheckBoxRoutedEvent_Click(object sender, RoutedEventArgs e)
        {
            if (sender != e.Source)
                return;

            MessageBox.Show(e.Source.ToString());
        }

یا اینکه روش دیگه ای هم هست؟
نمیدونم قبلا در مسائل Routed Event ها این را پرسیدم یا نه .

تشکر استاد .
قبلا پرسیدید، در استراتژی Bubble رخداد در کنترلی که میزبان کلیک ئه رخ میده و بعد رخداد زنجیر وار به والد و به سمت ریشه هدایت میشه. حالا اگه کنترل یا کنترل های والد بخوان در جایی این زنجیره هدایت رو قطع کنند، با e.Handled = true زنجیره رو قطع می کنند تا دیگه به والدشون چیزی گزارش نشه. پس اگر در رخداد کلیک Button فرزند اینکار رو انجام بدهید، کلیک اش به والد گزارش نمیشه و هر کلیکی که در والد رخ میده حتما کلیک خود والد ئه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
قبلا پرسیدید، در استراتژی Bubble رخداد در کنترلی که میزبان کلیک ئه رخ میده و بعد رخداد زنجیر وار به والد و به سمت ریشه هدایت میشه. حالا اگه کنترل یا کنترل های والد بخوان در جایی این زنجیره هدایت رو قطع کنند، با e.Handled = true زنجیره رو قطع می کنند تا دیگه به والدشون چیزی گزارش نشه. پس اگر در رخداد کلیک Button فرزند اینکار رو انجام بدهید، کلیک اش به والد گزارش نمیشه و هر کلیکی که در والد رخ میده حتما کلیک خود والد ئه.

خیلی ممنون استاد .
فرض کنید که (در کد پست قبل) ، اون CheckBox (یا هر کنترل دیگه ای) ، یه Panel داشته باشه که توی اون پنل ، فرضا صد ها کنترل و ده ها Button وجود داشته باشه .
در وجهه ی برنامه ریخت و پاش ایجاد میشه که برای اون ده ها Button ، رویدادِ کلیک ایجاد کنیم و توش e.Handled = true را بنویسیم بخاطر این قضیه .
تازه رویدادهای دیگه ی Routed Event هم هستند که بخوایم این کار را کنیم .

اون کد ، جمع و جورتر نیست؟
لازم هم نیست که دونه دونه برای هر کنترل ، همون رویداد را متصل کنیم .

اصلا یکی از اهداف اینکه routed event ها اومدن ، این هست که اگه خواستیم یه رویداد مشترک تعریف کنیم ، نخوایم ، هندلرِ اون رویداد واحد را هر بار به هر کنترل متصل کنیم . درست میگم؟

تشکر استاد .
 

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

بالا