wpf - Where should the crud logic be implemented in mvvm? -
in mvvm light application search in customer list. search narrows customer list displayed in master/detail view datagrid (the master customersearchresultview) , separately defined usercontrol firstname, lastname, address etc, etc (the detail - customersearchdetailview). here main content of master/detail view:
<stackpanel minwidth="150" > <textblock text="customer search result list" /> <grid> <datagrid name="customerlist" itemssource="{binding searchresult}" selecteditem="{binding selectedrow, mode=twoway}" > ..... </datagrid> </grid> <grid grid.column="2"> <textblock text="customer details" style="{staticresource heading2}" margin="30,-23,0,0"/> <content:customersearchdetail datacontext="{binding selectedrow}" /> </grid> </grid> </stackpanel>
both have corresponding viewmodels. please remark dc customersearchdetail, selectedrow - property on customersearchresultviewmodel , defined this:
private customer _selectedrow; ... public customer selectedrow { { return _selectedrow; } set { _selectedrow = value; raisepropertychanged("selectedrow"); } } ...
because of have not defined dc on customersearchdetailview - set in binding on "master" view (as shown above) , seems work ok.
in model folder have created customer class in use here. implements observableobject , idataerrorinfo , have public properties raisepropertychanged events.
i run application , seems ok. note: viewmodel customersearchdetailview (that customersearchdetailviewmodel.cs) @ stage empty shell , not in use (as far can see ... constructor never accessed)
now want add save/update functionality customer in detail view. ok, add save button customersearchdetailview this:
<button content="save" command="{binding path = savecommand}" width="80" margin="0,0,15,0"/>
i create "savecommand" relaycommand property in customersearchdetailviewmodel - never accessed.
hmmmmm ... after googling , forth come this:
<button content="save" command="{binding source={staticresource mycustdetails}, path = savecommand}" width="80" margin="0,0,15,0"/>
i defined "mycustdetails" resource in view pointing customersearchdetailviewmodel. , voila! hit method when debugging ... alas, customer of course "null". (in fact spent 2 hours implementing commandparameter here , binding "selectedrow" property on master view - customer still "null").
more googling , searching mvvm examples, , implemented "savecommand" on customer class (the model object). , guess what? edited customer got passed along - send ef layer , seems ok ....
and - if still me - here comes questions:
1.) - , thought "proper mvvm way" of doing things - have crud/repository accessing in viewmodel. how can in scenario?
2.) have crud in place via model class (customer) - should bother question 1? in fact have deleted customersearchdetailviewmodel , runs ok. feel have invented view - model (mv) framework ... :-p
i feedback on - , apologize "wall of text".
assuming dc means datacontext
just opinion:
- first question doing special
selectedrow
incustomersearchresultviewmodel
?
if answer no, rid of property , have customsearchdetailview
bind directly datagrid
using {binding elementname=customerlist, path=selecteditem}
- now save / update commands need used
button
's incustomersearchdetailview
. instantly i'd inclined using separate vm view , have these command's defined there.
now mentioned these commands not accessed. answer because in program you're never creating customersearchdetailviewmodel
.
normal operation view's datacontext
it's vm(if requires one. in case imo cos need hold commands)
looking @ code i'd guess using mvvm light. in viewmodellocator
have main
property , in main view, got datacontext
set using main
property , source={staticresource locator}
locator viewmodellocator
created in app.xaml resources. thereby creates viewmodel view defining datacontext. can ofcourse same in code-behind let's not go off topic.
so in case got datacontext set selectedrow
of type customer
, binding's resolved using datacontext
, that's why when command's defined in customer
works fine when it's in vm did not.
so why did work when had commands in vm , used
<button content="save" command="{binding source={staticresource mycustdetails}, path = savecommand}" width="80" margin="0,0,15,0"/>
^^ worked because datacontext
not used since source
has been specified explicitly. , where-ever mycustdetails
defined in resources, there vm got created.
so worked what's wrong that?
well it's quite big mess. mentioned customer
details in vm null. hope can guess why now. it's because vm created in resources via x:key="mycustdetails"
nothing in ever used or set apart when binding's referred explicitly
in system got commands refer either model plain wrong or vm created resource purpose. datacontext
heavily linked "searchresults" view making not easy future extensions or layout updates.
if keep view <-> vm 1 <-> 1 relattion can avoid confusion. in summary can answer both question's together. while works, please don't let code , tweak better expansion future , comply basic guidelines.
so how that?
approach 1:
in
customersearchdetail
view, add dependencyproperty of typecustomer
lets callselectedcustomer
.now replace
datacontext="{binding selectedrow}"
selectedcustomer="{binding selectedrow}"
incustomersearchresultview
now set datacontext of
customerserachdetailview
it's vm similar howcustomerserachresultsview
links it's vm(guessing through datacontext binding in xaml usingviewmodellocator
)now can have commands in
button
's ofcustomerserachdetailview
<button command="{binding savecommand}" ...
finally because
selectedrow
no longerdatacontext
ofcustomerserachdetailsview
, bindings firstname, lastname, address appear stop working.
we got plenty of options address this.
first in each binding use relativesource findancestor binding pointing customerserachdetailsview
, there via currentcustomer dp(dependencyproperty
) created before appropriate field.
eg:
<textblock text={binding relativesource={relativesource findancestor, ancestortype={x:type local:customerdetailsview}}, path=currentcustomer.firstname}" />
now if have multiple properties gonna start getting annoying type. pick common ancestor(say 3 of these textblock
s grouped under stackpanel
) , apply it's datacontext currentcustomer
element via similar binding ^^. stackpanel
's children datacontext customer element in each of binding's don't have whole relativesource thing , can mention {binding path=firstname}
, on.
that's it. got 2 view's own respective vm , model(customer
) , each have respective tasks.
great, done? err not quite yet.
while approach 1 better started it's still "meh". better.
approach 2
mvvmlight has messenger
class allow communicate between different classes in weak dependent format. need if haven't already.
so messenger
?
pretty simple:
in setter of
selectedrow
incustomersearchresultsviewmodel
we'll send message new incomingvalue
customersearchdetailsviewmodel
.now in
customersearchresultsviewmodel
we'll add propertycurrentcustomer
, assign incoming value.in
customerserachdetailsview
no longer create dp. means no longer setselectedrow
anything(datacontext or dp) incustomerserachdetailsview
customersearchresultsview
( sweet less work :) )as way assign
datacontext
ofcustomerserachdetailsview
or way bindbutton.command
- remain same approach 1finally actual "firstname" , binding's.
currentcustomer
property ofcustomersearchdetailsviewmodel
. binding how button bind's it's commands
^^ works fine cos datacontext
textblock
vm , property currentcustomer
exists in it.
Comments
Post a Comment