c# - How can I display hierarchical-based objects in an UserControl and present Commands using MVVM? -
[as question mvvm thoughts, i'm using pseudo-code , -xaml in question short , precise possible.]
i've ran mvvm problem wasn't able solve yet using best practice recommendations.
imagine you're trying create editor program something. we're going use library model (i couldn't come better one):
- a library contains 1 or many books
- a book contains 1 or more chapters
- a chapter contains 1 or more paragraphs
the ui might this. might designed badly, example after all:
now came mainwindow
definition:
<window datacontext="{pseudoresource editorvm}"> <dockpanel> <controls:allbookscontrol books="{binding books}" addcommand="{binding addbookcommand}" selectcommand="{binding selectbookcommand}" dockpanel.dock="left" /> <controls:editbookscontrol books="{binding selectedbooks}" deletebookcommand="{binding deletebookcommand}" addchaptercommand="{binding addchaptercommand}" deletechaptercommand="{binding deletechaptercommand}" addparagraphcommand="{binding addparagraphcommand}" deleteparagraphcommand="{binding deleteparagraphcommand}" /> </dockpanel> </window>
while still looks neat, can't seem implement required behaviour in usercontrol
itself:
<usercontrol x:class="editbookscontrol" x:name="root"> <usercontrol.resources> <datatemplate datatype="book"> <stackpanel orientation="vertical"> <button content="remove" command="{binding deletebookcommand, elementname=root}" commandparameter="{binding}" /> <textbox content="{binding title}" /> <wrappanel itemssource="{binding chapters}" (ignoring additional add tile here) /> </stackpanel> </datatemplate> <datatemplate datatype="chapter" (template chapter tiles)> <stackpanel orientation="vertical"> <button content="remove" command="{binding deletechaptercommand, elementname=root}" commandparameter="{binding}" commandparameter2="... need pass chapter's parent book here, there's no such second command parameter, ..." /> </stackpanel> </datatemplate> </usercontrol.resources> <tabcontrol itemssource="{binding books, elementname=root}" /> </usercontrol>
things start complicated walk down book's hierarchical tree. example i'd have pass 3 command parameters mainwindow
when deleting paragraph (in book?, in chapter?, paragraph?).
i able solve of getting rid of usercontrol
's dependencyproperties
, placing tabcontrol directly in mainwindow , adding separate viewmodel
s child controls. way editbookcontrol
can make required changes itself:
(everything in mainwindow) public list<control> editcontrols; <tabcontrol itemssource="{binding editcontrols}" /> selectbookcommand_executed { editcontrols.add(new editbookcontrol(new bookvm(e.commandparameter book))); }
as read, not way go; best practice using 1 viewmodel
per window
described here:
- so: how bind wpf commands between usercontrol , parent window
- so: mvvm + usercontrol + dependency property
i can't imagine 1 viewmodel per window allowed. visual studio written using wpf - did used 1 viewmodel tons , tons of features?
i'd know how can solve dilemma and writing clean , nice code.
mvvm easy once understand it.
one thing need know in datatemplate datacontext object on apply datatemplate.
so in libraryv.xaml's itemscontrol, apply datatemplate collection of bookvm, datacontext in bookv related bookvm.
it makes easy information want.
a simple (not complete) version of problem:
libraryvm.cs:
public class libraryvm{ public libraryvm(librarymodel model) { _model = model } #region cmdremove private delegatecommand _cmdremove; public delegatecommand cmdremove { { return _cmdremove ?? (_cmdremove = new delegatecommand(remove, canremove)); } } private void remove(object parameter) { bookvm booktoremove = (bookvm)parameter; books.remove(booktoremove); } private void canremove(object parameter) { bookvm booktoremove = parameter bookvm; return booktoremove != null && books.contains(booktoremove); } #endregion private readonly librarymodel _model; public list<bookvm> books {get {return _model.books.select(b => new bookvm(b)).tolist();}} }
bookvm.cs:
public class bookvm{ public libraryvm(bookmodel model) { _model = model } private readonly bookmodel _model; public string title {get {return _model.title;}} public list<chaptervm> chapters {get {return _model.chapters.select(c => new chaptervm(c)).tolist();}} }
bookv.xaml:
<usercontrol ...> <stackpanel orientation="vertical"> <button><!-- button remove book library --> <textblock text="remove" command="{binding relativesource={relativesource findancestor, ancestortype={x:type view:libraryv}}, path=datacontext.cmdremove}" commandparameter="{binding mode=onetime}"/> </button> <textblock text="{binding title, mode=oneway}"/> <itemscontrol itemssource="{binding chapters, mode=oneway}"> <itemscontrol.itemtemplate> <datatemplate datatype="{x:type local:chaptervm}"> <view:chapterv/> </datatemplate> </itemscontrol.itemtemplate> </itemscontrol> </stackpanel> </usercontrol>
libraryv.xaml:
<usercontrol ...> <stackpanel orientation="vertical"> <itemscontrol itemssource="{binding books, mode=oneway}"> <itemscontrol.itemtemplate> <datatemplate datatype="{x:type local:bookvm}"> <view:bookv/> </datatemplate> </itemscontrol.itemtemplate> </itemscontrol> </stackpanel> </usercontrol>
the datacontext need set libraryv's datacontext here.
Comments
Post a Comment