ViewModel for nested control not refreshing in Catel MVVM for WPF - catel

I have a nested control (NC1) which contains 3 instances of NC2. When the containing view is first opened, NC1 will be null and so the three instances of NC2 will be null. This appears to work correctly based on debugging through my code and the framework.
When a selection is made in the containing view NC1 is properly set (and the ViewModel is (re)created) and values for its properties (exposed through Fody.Expose) appear in the view. However, none of the ViewModels for NC2 are (re)created and they do not reflect the values provided by their respective models.
I am not sure exactly what information to provide without uploading a lot of content, so I will take a stab.
In the NC1 view, I have the following
<localViews:NC2 DataContext="NC2Entry1"/>
<localViews:NC2 DataContext="NC2Entry2"/>
<localViews:NC2 DataContext="NC2Entry3"/>
NC2EntryX are properties on the NC1 ViewModel that return a specific instance of an NC2 model from a list. The NC2EntryX properties is NOT registered with RegisterProperty.
As a note, I have discovered that I must have an empty parameter constructor for the NC1 and NC2 view models. If I do not, then I receive a MissingMethod exception when the view model is being created when the TypeFactory attempts to create the ViewModel with the Activator instead of using the injection path. The injection path is not used because the call at line 591 of TypeFactory returns false because the NC2 model passed is 'not registered'. (Not sure if it should be or how to make it so.)
I am using Catel version 3.9.0

The NC2EntryX property changes must be reflected to the view somehow. You can do this by making it Catel properties, or by calling RaisePropertyChanged("NC2EntryX") yourself when setting the property value. Only in that case the view will be updated (this is just standard WPF / MVVM behavior).
About the missing method exception: it is a first chance exception where the TypeFactory tries to fall back to Activator.CreateInstance when it fails to create the type with dependency injection. No need to worry about this. If you don't want view models to be alive without a model, don't create an empty constructor.

Related

Xpages: Dynamic View Panel and DominoViewCustomizer bean

I have dynamic view panel and I am using a customizer bean to hide columns based on column names. However, I need to:
Know which view is loaded in the customizer bean
get document handle in the bean
add additional column in the bean
Why I need this: in my application I am dealing with document mappings. I want to create a column for mapped document details. as there can be different document types mapped, there can be multiple columns.
The work of seeing which view you're working with and generating the column defs (normally the same as the ones in the view, but you could add others) is done via the ViewFactory object that is returned by #getViewFactory in the customizer bean. You can see an example of overriding the method and returning a customized factory here. You can also find the source of the default one in the ExtLib here for another example. The job of the ViewFactory is to emit a ViewDef containing a series of ColumnDefs - basically, an abstract representation of the view design. That will cover 1 and 3.
Getting a handle on the document in question for number 2 is a bit more indirect. Since the customizer bean executes only during the initialization of the view, it has no direct hook to the process of rendering each row (which is where you can get the document). You can, however, set properties or content to method/value bindings that, themselves, access the document, so that they're executed per row. I do this in order to get color columns working: I create an SSJS binding for the style property that can then see the viewEntry object. If you modify that code, you could write some SSJS like #{javascript:var doc = viewEntry.getDocument(); ...other stuff here...}. If you do that, you should make sure to either always use "viewEntry" as the var name in the view or use panel.getVar() to find the variable name dynamically.

How to get UIComponent value in Java?

I have a method in my JSF controller that is invoked by an ajax tag nested inside a visual component (really irrelevant which one). The method takes a single argument of type AjaxBehaviorEvent, from which I can obtain a Java representation of the invoking HTML visual component as a UIComponent and also downcast it to its specific corresponding type (e.g. h:inputText corresponding to HtmlInputText).
I understand that, in most cases, the value of the HTML visual component would be retrieved easily by referencing either the controller or entity [g|s]etters to which the form fields are mapped in the view. However, in my particular case, I would like to fetch the value of the visual component (in my case a form field) through its Java object rendering. While studying the faces API, I found ways to read various properties of the object, such as the ID or context but not the value that the component currently holds in the view.
Can anybody explain whether I am just not finding the right way to read it or it is so by design? If the latter, can you explain why it is designed like that? Is it to disable "backdoor" access to form fields as opposed to going through the view mapping?
There are a multitude of ways to pull values off a component. Going by what you already have UIInputt#getValue() and UIInput#getSubmittedValue() will provide the value.
The UIInput#getSubmittedValue() is fit for the purpose only between the APPLY_REQUEST_VALUES and VALIDATE phases of the JSF request. All other phases after, use the UIInputt#getValue(). You'll be using UIInput instead of the raw UIComponent you pulled from the event (UIInput extends UIComponent and it's the parent class for all input components that accept user-edited values). What you'll have will eventually look like:
UIInput theInput = (UIInput)event.getSource();
Object theValue = theInput.getValue();
There are other ways (not as clean) to get values within the request lifecycle also

How to get UIVisualizer to use the View Model passed to it

I'm attempting to create view/viewModel pair to act as a MessageBox replacement which will be called by the UIVisualizer Service. The viewModel has five different constructors, one being the default, and the others to define the different attributes of the MessageBox(e.g. Type, Image, Message, Title, etc.). I will be creating the viewModel using one of the four non-Default constructors each time I desire a MessageBox to popup. I am doing this versus using the built-in MessageService is because I'm using third party controls for my application and I want the MessageBox look-and-feel to match the rest of the application.
My problem is that even though I'm creating the viewModel, Catel is not using the viewModel I pass in to UIVisualizer, but is creating a new viewModel from the default constructor.
Does anybody know how to get this behavior to stop.
The best thing to do is create your own version of the IMessageService (create new class deriving from MessageService and override the Show method).
Catel should re-use the passed in view model. If you think you have found a bug, please report it at http://www.catelproject.com/support/issue-tracker

ViewModel navigation with TabBarPresenter

I have made a HomeViewModel which contains some other ViewModels to be presented in a TabParPresenter. This works fine and the ViewModels associated Views are presented correctly in their corresponding tabs. However on of the ViewModels have a check in the ctor that tells it in when some conditions apply it needs to navigate to SettingsViewModel , which is not a part of the ViewModels contained in HomeViewModel.
The navigation event gets caught by the TabBarPresenterHost, which is simply the Presenter of the application and if a TabBarPresenter is present and not null it is passed to the TabBarPresenter.ShowView() method. All this happens before the TabBarPresenter is done loading and SelectedViewController is set in ViewDidLoad. Hence the TabBarPresenter relies on using the SelectedViewController to push new Views I obviously get a Null Reference Exception.
In short what I want is:
if (conditionForShowingHome == true)
GoToHome();
else
GoToSettings();
Then when inside SettingsViewModel I have set the stuff I need when going back you return to HomeViewModel.
What breaks this is that the ViewModels are loaded before the View is done loading and the navigation event is executed before the View is done loading. Any ideas how to go around this?
I'm afraid that putting this code inside a ViewModel constructor is likely to lead to problems.
The ViewModel constructors are called during navigations - and not all platforms will support starting new navigations while existing ones are still in progress.
To workaround this, I personally opt to put this type of behaviour in the code before the navigation.
In the case of a normal navigation, this can go inside an MvxCommand execution handler.
In the case of application startup, I do this in a custom app start - see some notes in https://speakerdeck.com/cirrious/appstart-in-mvvmcross

Assigning individual View Models to Nested Views?

I have a MainView which has an associated MainViewModel in my WPF application, assigned via its contructor.
App > start up uri > MainWindow.xaml
public MainWindow()
{
InitializeComponent();
var viewModel = new MainViewModel();
DataContext = viewModel;
}
My MainView holds as many as four nested views or child views who are hidden and displayed based upon the button that has been clicked on the MainView. So we toggling the visibility property vi a binding which gets updated via command bindings assigned to each button on the MainView.
Each nested View does not have an associated ViewModel, all bindings found on child views find their information in the MainViewModel. So binding system ends waling up the UI tree of the app to find out that the parent 'MainView' has an associated ViewModel.
So overall there is 'ONE' -> ViewModel. This seems to work fine and but as expected this VM has gotten to big and needs re-factoring. It holds information that contextually it should not. But this is a proof concept application. So i decided to keep it simple and make sure it was do-able.
PROBLEM:
When i tried assigning a empty view with an empty view model I noticed binding errors in the output window and as expected weird and broken behaviour. Which makes no sense ... Is there a more clear and concise way of letting WPF know how to handle any bindings it finds in a nested view control? I thought if each view's constructor assigned itself a corresponding VM as shown above, then it should work as this logically makes sense. Unfortunately all buttons on the MainView stop working when the corresponding view it is designated to switch on and hide the others has an associated ViewModel. On some buttons it works and the others it does not? This is really weird?
As mentioned in my answer above, the problem was that WPF binding system was struggling to to resolve bindings at run time. The main view has its associated view model instantiated and assigned via the Main View contructor and this pattern is repeated for all nested views that the MainView also houses.
By default, I tend to use the implied binding syntax which means that without explicitly specifiying a source the binding system will try to resolve the name you supply in the binding. So it's all implied and nothing is explicitly set!
Upgrading each nested view to have its own view model makes this auto discovery/resolution of bindings go a little crazy and I have not explicitly told the binding system where to find the property I am looking for hence the output window binding errors.
This leads to unexpected behaviour as the output window was telling that it was trying to resolve binding expressions in nested views --> viewmodels. When in actual fact that VM it is looking in, IS EMPTY!
So clearly the binding system is good when you do not explicitly set a source property inside the binding syntax. It is clever enough to find things on its own. In my case it needed some extra help, as it was unsure as to where to find things.
SOLUTION:
Remove the constructor declaration for the MainViewModel in the MainView constructor.
Scope in an xmlns for ViewModels namesapce into your MainView.xaml
Create a window resource inside the MainView .xaml
Give the resource a key.
Upgrade all your bindings in the MainView xaml file to include source property.
Give the source property a static resource binding that points to your ViewModel key value set up in step 4.
Only do step 6 for bindings who refer to the ViewModel that is associate with the MainView.
All nested views are to be left alone, they should handle their own bindingds in their own xaml files. The MainView simply instantiates them and places them onto the UI. For me this was the case, I did not have any more bindings concerning my nested views. All Bindings that lived on the MainView.xaml file referred to data in the MainViewModel.cs. This makes alot easier when viewing your problem.
For some reason the editor was being awkward so I chose to omit any sample code but the steps above are descriptive enough to follow the steps that I took. Above is what worked for me.
ANOTHER WAY OF SUMMING UP THIS PROBLEM
Most books teach the shorter binding syntax
What happens when more than one data context is available?
How is the binding system supposed to know how to resolve your short hand binding expressions.

Resources