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:

editor window example

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 viewmodels 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:

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

Popular posts from this blog

sql - invalid in the select list because it is not contained in either an aggregate function -

Angularjs unit testing - ng-disabled not working when adding text to textarea -

How to start daemon on android by adb -