MVVM - Summary/Detail - xaml

So I'm trying to wrap my head around MVVM and I'm finding that I have more questions than answers. The tutorials don't go far enough for me when it comes to the next step...
Basically I want a list of items and then a way to get the detail of each item.
Below is the examples that I've found online and they work great for displaying the list, but I need to know how I can use my VM to get the detail of this item.
private IList<item> m_items;
private IList<item> m_Item;
private IList<item> getItemDetail(Int32 iId)
{
var myItem =
from i in items
where i.iId == iId
select i;
m_Item = new List<item>();
foreach (var item in myItem)
{
m_Item.Add(item);
}
return m_Item;
}
public myViewModel()
{
m_items = new List<item>
{
new item(1, "test,),
new item(2, "test2"),
new item(3, "test1")
};
m_Item = new List<item>();
m_Item = getItemDetail(iId);
}
Update:
I updated my View Model code above. I think what I've done is I have added another List where when the user navigates to a detail page the view model gets called with the specific ID which then populates the detail List. I probably don't need a list here but I wanted to try to keep it consistent with the main page code.
In my detail page I'm setting up the VM this way:
itemViewModel VM = new itemViewModel((Int32)navigationParameter);
DataContext = VM;
When I break on the VM variable I see my 2 lists. However, my binding doesn't work on the XAML. If I need to post some sample XAML let me know. I can do that but I'm hoping there is something I'm missing here.
I'm trying to learn MVVM and I want to do things right. So instead of continuing down a wrong path I would really like to know the "right" way of doing things. So if you see errors, please let me know.
Thanks!

right, i'd suggest you go back to basics first and watch Laurent's Mix sessions about MVVM, you find links to then from the mvvmlight site on codeplex.
but to break it down you've hit a few of the hurdles I did when I first started.
1: (the biggest gotcha) for databinding to work, you must expose data using a property (get and set pattern), just a list variable won't work. this goes for everything you want to bind to. The alternative is to set item sources directly in code but you will loose all the features of databinding, including updates.
2: to understand databinding you need to understand the INotifyProperty changed pattern, this is the underlying gubbins (technical term ;-D) to enable binding to work properly. as suggested start a new "master/details" project template and walk through it, from the viewmodels holding the data to the views (pages) looking at the data in the view model
3: use observablecollections for lists, they are just better for binding and are basically just lists with extras
4: remember you can also bind the "selecteditem" or "selectedindex" of a listbox to capture what the user has selected, just be sure to set the binding to "twoway" so the view can push data to the viewmodel and not just read, like this: {binding myselecteditem, mode="twoway"}
hope this helps but if your still stuck Laurents videos are the best, just watch them a few times and follow what he does line by line

I believe you want to use what Microsoft calls the "master-detail binding scenario", where several controls bind to a single collection or to its selected item. To do so, you need to bind to a so called collection view, which is just a layer on top of the collection used by WPF. For instance, you can bind a ListBox's ItemSource and a ContentControl's Content to the same collection view, and the ContentControl will display the details of the the ListBox's selected item.
see: http://msdn.microsoft.com/en-us/library/ms752347.aspx#master_detail_scenario
P.S. you may want to use an ObservableCollection instead of a List, and make sure your view model implements INotifyPropertyChanged.

Related

How To Properly Pass Model Class In Navigation Of Jetpack Compose

I have a pretty basic application in Jetpack Compose, where:
First screen has a lazyColumn with many items
Users can click on an item and be moved to a screen with details about the clicked item
Each item is modeled using a data class that is serializable
I've tried passing in the model class with the navigation using:
val modelDataAsString = Json.encodeToString(MODEL_CLASS.serializer(), modelData)
navController.currentBackStackEntry?.arguments?.putString("KEY", modelDataAsString)
navController.navigate("ROUTE/${modelDataAsString}")
But when I do this, I get the following exception:
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/... cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0x78da56c6) route=main}
Now, I understood that this is because my Model class in String form is way too long (relevant SO question).
So, searching SO, I found that the solution provided was using a SharedViewModel.
I have done this and everything works well, but something doesn't feel right passing around the viewModel.
What I want to know (and understand) if this is the proper way of
solving this issue. I am not looking for an opinion or a
recommendation, but a definitive answer.
At the bottom line, what I want to achieve is the ability to allow the onClick method on an item in my LazyColumn move the user to a screen of the item clicked.

Initialize WebElements for part of a page

I'm following the Page Object model approach. I’m working on implementing a SearchResultsPage where a bunch of search results are displayed. In thinking about this page, I would like to implement it in such a way that it would support a getSearchResultByIndex(int index) method. Ideally, I would like the return type of this method to be a SearchResult, which would be a mini-page object (aka panel) that encapsulates the functionality found on a search result item since there are a number of attributes of a search result that the user can interact with. I don’t see how to accomplish this though. I was hoping to find a method like PageFactory.initElements() that would take in the WebDriver, a WebElement or selector (that identified an individual search result), and an instance of my SearchResult, but haven’t seen anything.
For clarity. Here's the basic structure of a SearchResults page.
<div class="searchResultsContainer">
<div class="searchResult">various internal fields to interact with/inspect</div>
<div class="searchResult">various internal fields to interact with/inspect</div>
...
<div class="searchResult">various internal fields to interact with/inspect</div>
</div>
It seems like this has to be a common problem out there that people have solved. I've used this "panel" notion for other common page elements like header, footer, etc, but never in the case where multiple instances of the same panel type are on the same page.
Any thoughts would be appreciated. Thanks.
If it were me I would approach it differently. I would split this into 2 page object classes. One for SearchResults, and one for SearchResultPage. The SearchResults would be the generic results list and actions you can take on those results. Within that class you would add a method to click on an individual search result, to pop up the details of that result, that would be what returns your SearchResultPage object.
Here is a rough sketch of what that method could look like inside your SearchResults page object. Not sure what language you are using but this is in C# (Java would be similar, Python much different but you'll get the general idea):
public SearchResultPage GetSearchResult()
{
// do something to click and show search details
return new SearchResultPage(_driver);
}
And then a skeleton of the SearchResultPage class object itself:
public class SearchResultPage
{
IWebDriver _driver;
// add whatever elements you want to work with specific to that single record view
//constructor
public SearchResultPage(IWebDriver driver)
{
_driver = driver;
}
// add whatever methods you want to interact with the elements in that view
}
The good thing about keeping the page objects separate in this case is SearchResults could actually be used in other areas of the application as well, if there are results on other pages that use the same elements etc. I find myself taking out common page elements (drop down menus, grids, etc) into their own objects all the time. Otherwise you end up repeating a lot of code if you stick to strict Page Object model where common functionality exists on multiple pages.
I think I've got this solved. I ended up abandoning PageFactory.initElements(), which I think I've learned is really key and likely an old-school way of implementing the page/object model. Adopting the use of By rather than FindBy seems to work much better as long as the appropriate conditional WebDriverWait.until(ExpectedConditions.elementToBeClickable(elementLocator)) is used.
After coming to this understanding, introducing the concept of a panel locator in my base Panel class allowed me to combine that locator with a nth-of-type(idx) locator to get things wired up and working as expected. Here's a simplified example of that in use in my SearchResultsPage:
public SearchResult getSearchResult(int idx) {
SearchResult res = new SearchResult(getWebDriver(),
By.cssSelector(".searchResultsContainer .seachResult:nth-of-type(" + idx + ")"));
return res;
}
My SearchResult class then just has a number of By locators defined that essentially call new ByChained(panelLocator, locator);
So glad to have solved this!

Force reapply of ItemTemplateSelector in WinRT

I have a GridView as my zoomed out view in a SemanticZoom control. This GridView uses a custom DataTemplateSelector as the ItemTemplateSelector. It shows an item for each content group that my app shows.
The template is different depending on whether the group is empty or not. This works fine on load, but it doesn't update when a group becomes empty or stops being empty.
I've found that the ItemTemplateSelector is only run when the page is first shown. How can I force the DataTemplateSelector get run again.
The WPF questions on this topic all suggest triggers, but these aren't available in WinRT XAML.
I've found an answer to a similar WPF question that answers this in a way that works in WinRT:
https://stackoverflow.com/a/11327087/31569
Basically you set the ItemTemplateSelector to null and then set it again. Like this:
var templateSelector = MyGroupView.ItemTemplateSelector;
MyGroupView.ItemTemplateSelector = null;
MyGroupView.ItemTemplateSelector = templateSelector;
This works, but happy to be told if there is a better way to do this.
I find it easier to just just remove the item in need updating from the collection and adding it back. This forces the GridView or ListView to apply the template. This is easy to do in MVVM world.
var itemToReload; //The object who's template needs updating
var reloadIndex = this.SomeCollection.IndexOf(itemToReload);
this.SomeCollection.Remove(itemToReload);
this.SomeCollection.Insert(reloadIndex, itemToReload);
One thing to note, is that if the item is a "Selected" item, you'll need to reapply that selection.

Use locator to manage multiple view-viewmodel pairs in Panorama Page

I'm new to Silverlight/MVVM. I tried some example of MVVM Light, it looks great.
For my scenario, I want to create a Panorama Page, for each Panorama Item, showing my usercontrol, a item list for a customer.
I've built usercontrol(view), viewmodel and WCF service model and works well in a single Panorama Item(Only use first customer).
Also, I use Locator of MVVM Light shown in MIX10 demo, it enables me to make design time data for Expression Blend.
My viewmodel will receive a parameter of customer ID then exchange data with WCF based on this ID.
And the customer list also comes from WCF. So I can't actually makes viewmodels in Locator's static constructor.
If viewmodels are built in runtime by calling Locator, how to make data binding?
The only way I think about is to make viewmodel object in usercontrol's constructor and make it datacontext.
Is there a better solution?
If you want to keep the same declarative model in the XAML, you can put a CurrentCustomerViewModel property on the locator and then set property to the right viewmodel before you navigate to the page.
Personally though for pages like that I typically put a viewmodel factory method on the locator (so it can cache them, etc) and call it from the OnNavigatedTo method, something like this.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string id = NavigationContext.QueryString["customerID"];
vm = ViewModelLocator.GetCustomerViewModel(id);
DataContext = vm;
base.OnNavigatedTo(e);
}
Then I just use Blend's sample data capabilities for design time data. This way also helps support pinning the page to the start screen since that will be the entry point to the app and I won't necessarily get a good chance to set the "CurrentCustomerVM" property anyway.

Dynamically changing the ViewModel of a View in an MVVM, MEF implementation

I have a usercontrol that i want to use throughout my Silverlight MEF MVVM solution.
I want to be able to link it up with one of a number of ViewModels depending on which module i am in. What this control does is list the records of a given entity so i can Add, Edit or Delete. I realized i would be using this control in multiple locations - to update several lookup tables, so i decided to make it's ViewModel dynamic. As seen below, I am using the Galasoft MVVM plugin.
if (!GalaSoft.MvvmLight.ViewModelBase.IsInDesignModeStatic)
{
// set DataContext
DataContext = PluginCatalogService.Instance.FindSharedPlugin(ViewModelTypes.ViewModelMT, PluginType.ViewModel);
_viewModel = (ViewModelMT)DataContext;
}
My question is how can i dynamically change from ViewModelMT to ViewModelCT to allow me to independently display lookup tables e.g. Maintenance Types and Contract Types on an instance of this same usercontrol? I took a look at the Viewmodel locator, but I'm still not sure how to get this done.
Thank you
I don't think this is really a ViewModel thing. It's more of a Service problem.
Your ViewModel for the control will not change but you'll dynamically slot in the required service to maintain your list. ie. MaintenanceTypeService and ContractTypesService will implement IListMaintenanceService which exposes an list of items and Add,Delete and Edit commands.