نه، فرضا یک Command دارید که View رو به شیوه ای Full Screen می کنه، هیچ کاری هم با Model نداره، پس دلیلی نداره که همه Command ها با Model کاری داشته باشند.
سلامی مجدد
خیلی ممنون استاد .
بله . این رو که میدونم .
منظورم کامندِ MouseMoveAssociateEventToCommand و متدِ MouseMoveAssociateEventToCommandHandler (در پروژه ی قبلی) بود که اطلاعاتی که x و y ئه موس روی دکمه هست را به Model میخواد منتقل کنه .
اون Command ای هم که در View است و میخواد داده ای رو به Model ارسال کنه باید با چی در ارتباط باشه؟ ViewModel، نه Model. اینکه روال مشخصی داره.
بله .
بله، دقیقا و هر چقدر متد عمومی تری بنویسید که بدرد Command ها یا روال بیشتری بخوره طراحی بهتری انجام داده اید، یعنی سعی کنید حجم و پیچیدگی کمتری در ViewModel داشته باشید،
منظورتون اینه که برای ViewModel ، صرفا در حد انتقال داده ، کدمون را بنویسیم . نه اینکه کار و کدهای Model را انجام بده (یعنی منطق و محاسباتی را انجام بده) و نه کاری با کنترل ها که کار View هست ، داشته باشه.
متوجه صورت مساله نمی شوم، DataContext از چی به چی و چرا باید تغییر کنه؟
پروژه را دوباره برای اینکه منظورم را از تغییرِ DataContext برسونم نوشتم که در زیر آپلود میکنم .
به رویدادِ MyMainWindow_Loaded در View نگاه کنید . باید DataContext ئه اون کنترل و المنت ای که میخوایم به پروپرتی ای که در View هست ، binding کنیم ، تغییر بدیم دیگه . چون DataContext ئه پیش فرض را در Window ، شیِ ViewModel (در اینجا همون StudentViewModel) گرفتیم که به عنوانِ DataContext ئه همه ی کنترل ها و المنت های زیر فرزندان و نوادگانِ Window هم استفاده میشه و Source ئه تمامِ binding ها که همون DataContext شون میشه .
منظورم اینه که مثل مقدارِ :
C#:
private void MyMainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.AssociateEventToCommandButton.DataContext = this;
}
که تغییر دادیم ، باید تمامِ DataContext های کنترل های دیگه ای که میخوایم به Command هایی که درون View مینویسیم را هم تغییر بدیم .
علاوه بر این کد ، جلوه ی نامرتبی هم میگیره که فرضا یه بخشی از Command ها درون View باشن و بخشی دیگه درون ViewModel باشن . نه؟
البته نمیدونم در کد xaml ، چرا وقتی بجای کد بالا ، از کد زیر استفاده میکنم :
XML:
<Button DataContext="{Binding ElementName=MyMainWindow}" Name="AssociateEventToCommandButton" Content="Mouse Move__Associate Event To Command" Margin="0, 140, 0, 0" Padding="5, 2" HorizontalAlignment="Left" VerticalAlignment="Top" Height="50" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<command:EventToCommand x:Name="AssociateEventToCommand" Command="{Binding Path=MouseMoveAssociateEventToCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
در صورتی که نام Window را مقدار "MyMainWindow" میگیرم ، DataContext ئه Button ام کار نمیکنه (ارور نمیده ولی کار هم نمیکنه) . نمیدونم در xaml چجوری میشه مقدار DataContext ئه Button را برابر با Window ای که توش هستیم داد . شما میدونید استاد؟
و همچنین استاد در کد زیر :
XML:
<i:EventTrigger EventName="MouseMove">
<command:EventToCommand x:Name="AssociateEventToCommand" Command="{Binding Path=MouseMoveAssociateEventToCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
اگه در کدهای سی شارپ (نه کدهای xaml) ، بخوایم اون EventToCommand ای که نام اش AssociateEventToCommand هست ، برای پروپرتیِ Command اش (یعنی برای پروپرتیِ Command ئه EventToCommand) ، در کدهای سی شارپ همین Binding ای که در همین کدِ xaml انجام شد را انجام بدیم ، کدش چجوری میشه؟
چون کلاسِ EventToCommand ، مثل المنت ها و کنترل هایی مثل Button نیست که متد SetBinding را برای تنظیم کردن بایندینگ داشته باشه . در این صورت باید چی کار کرد؟
چه ضروری و چه غیر ضروری داده بین View و ViewModel و بین ViewModel و Model رد و بدل میشه، که عادی و بدون ایراد ئه. و هر انتقال اجزاء واسط کاربری هم به ViewModel چه ضروری و چه غیر ضروری ایراد داره و MVVM رو نقض می کنه، اگر بخواهید MVVM رو رعایت کنید بهانه ای برای استخراج داده از کنترل ها در ViewModel ندارید.
چرا با انتقال واسط کاربری و المنت و کنترل ها به ViewModel ، معماری MVVM نقض میشه؟
توی مقالاتی که یه کم درباره ی MVVM خوندم ، فکر نکنم جایی در این باره حرفی زده باشن و اینکه در این صورت ، نقض کنه یا نکنه ، حرفی زده باشن .
چون اگه مثل کامندِ MouseMoveAssociateEventToCommand در پروژه ام ، بخوایم بخشی از Command ها را که فقط برای ارتباط با واسط کاربری ، درون View بیاریم و از ViewModel جدا کنیم ، علاوه بر اون قضیه ی کدهای DataContext ، دو پارگی و دو قسمت شدنِ Command ها ، باعث پیچیدگیِ بیشترِ کد میشه .
در این صورت ، به احتمال زیاد ، استفاده از رویداد (بجای استفاده از Command ای که درونِ اون رویداد میخوایم اجرا کنیم) ، ترجیح داده شه .
=======================================
استاد در مقاله ی زیر :
Ian Griffiths' Weblog
www.interact-sw.co.uk
برخلافِ اون لینکی که در پست 720 داده بودم و میگفت (وقتی Path را یه کالکشن انتخاب میکنیم) ، DataContext ئه همه ی آیتم های ItemControl (مثل ListView) ، یعنی DataContext ئه ListViewItem هاش را برابر با نوعِ داده ایِ اعضای اون کالکشن انتخاب میکنه .
اما در این مقاله ، یه کدی داد :
YAML:
<DockPanel DataContext="{x:Static Fonts.SystemFontFamilies}">
<TextBlock DockPanel.Dock="Top"
Text="{Binding Baseline}" />
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True" />
</DockPanel>
که میگه Fonts.SystemFontFamilies ، کالکشنی از نوع FontFamily هست . و در پروپرتیِ Text ئه TextBlock ، به پروپرتیِ Baseline (که عضئی از کلاسِ FontFamily هست ، نه عضوی از کالکشن ها) ، binding انجام میده .
همچنان که دقت دارین ، DockPanel از نوع panel هست و نه ItemControl که ListViewItem یا ListBoxItem و اینها داشته باشه (که در اون لینک پست 720 در این باره صحبت کرد) .
بلکه در این مقاله میگه :
However, the TextBlock has a binding expression that seems to make no sense – it tries to read the Baseline property, and yet the source, an array, has no such property. When WPF encounters a binding to a non-existent property on a collection, WPF has a fallback strategy: it looks for the named property on the current item. Consequently, the TextBlock shows the Baseline property of the currently selected font.
وقتی که wpf به binding در یه کالکشن ای (به عنوان DataContext یا Source ئه Binding)
برخورد میکنه که پروپرتی مورد نظر وجود نداره ، wpf برای نام اون پروپرتی (ای که موجود نبود)
در current item و آیتم انتخاب شده ی اون کالکشن برای اون پروپرتی
جستجو میکنه .
الان این درست هست . درست میگم؟ اون قبلی که گفت ، درست نیست . چون در اینجا DockPanel که از نوع ItemControl نیست و در Text ئه TextBlock هم این نوع binding انجام شد .
----------------------
اما اینکه منظورش از current item یا آیتم انتخاب شده در کالکشن چی هست ، من یه کم جستجو کردم و یه چیزهای سطحی متوجه شدم اما نه کامل :
در مقاله ی بالا میگه :
The current item can theoretically be different from whichever the user has selected in a collectionview bound to a Selector but let's concentrate on situations when current means selected item.
The way to make sure the current item and selected item match one another is to use the Selector.IsSynchronizedWithCurrentItem property.
که میگه current item میتونه به معنای هر چیزی که کاربر در CollectionView ای که به کنترلِ Selector بایندینگ شده باشه ، نباشه (اما میتونه هم باشه) .
که با تنظیم پروپرتیِ Selector.IsSynchronizedWithCurrentItem به true میشه از همگامیِ این دو با هم مطمئن شد .
اما سئوال این هست که اگه کالکشن مون از نوع کلاسِ CollectionView نباشه (فرضا از نوع List یا ObservableCollection و ... باشه) ، و همچنین بخوایم عضوی از این کالکشن (فرضا current item از این کالکشن) را به پروپرتیِ یه المنتی جز Selector (مثلا پروپرتیِ یه Button یا مثل کد بالا ، به پروپرتیِ یه TextBlock) متصل کنیم ، در این صورت این current item ئه کالکشن مون ، اولا مقدارش را از کجا میگیره و از کجا مقداردهی میشه . یعنی از کجا متوجه بشیم که current item ئه کالکشن مون ، مقدارش به کدوم عضو از کالکشن مون اشاره میکنه (در صورتی که کالکشن مون CollectionView نباشه و فرضا از نوع List یا ObservableCollection باشه)؟
--------------------------------
بعد این CollectionView (و کلاس های فرزندش) وقتی استفاده میشه که بخوایم به شیِ binding مون از مرتب سازی ، فیلترینگ یا گروه بندی (که دقیقا این دو تای آخر را نمیدونم چیه) ای که در کالکشن مون اتفاق افتاد ، اطلاع بدیم؟
میشه بجاش از همون ObservableCollection استفاده کنیم و کالکشن مون را مرتب کنیم و کالکشنِ مرتب شده را بهش مجددا شیِ جدیدی بدیم . (هر چند اصولی شاید نباشه) . درست میگم؟
ببخشید استاد که زیاد شد .
تشکر استاد .