MVVMCross MvxDialogFragment Restore Issue - Does not have MvxFragmentPresentationAttribute - xamarin

I have upgraded to the latest version of MvvmCross (6.4.1) from 4.2.3. I and using Xamarin Android not Xamarin forms
In the view which initiates the dialog I do the following
Create dialog fragment derived from MvxDialogFragment
Assign a view model to it
Then call ShowView on the fragment
However when I rotate the device it fails in OnCreate with the message
Your fragment is not generic and it does not have MvxFragmentPresentationAttribute attribute set!
This did not happen in 4.2.3. The reason I create dialog this way is that I want it to use different view models depending on where I need this dialog. For example I want to show a different list of data, but in the same format in the dialog.
It seems this will only work if we apply the MvxFragmentPresentationAttribute which needs the type of view model to be defined at design time rather than run time.
Is there anything I can do to achieve this
Any help will be appreciated

If you somehow need to specify the ViewModel type at runtime, you can instead of decorating the class with the MvxFragmentPresentationAttribute let it implement, IMvxOverridePresentationAttribute and return it there with the appropriate ViewModel to be presented in.
Something like:
public class MyDialog : MvxDialogFragment, IMvxOverridePresentationAttribute
{
public MvxBasePresentationAttribute PresentationAttribute(MvxViewModelRequest request)
{
return new MvxFragmentPresentationAttribute
{
ActivityHostViewModelType = myDynamicType
};
}
}
Where you implement some kind of logic to get the myDynamicType somewhere.
However, you should be able to use MvxDialogFragmentPresentationAttribute instead though and the presenter will attempt to use the topmost Android Activity to present it in if you provide a null ref as the ActivityHostViewModelType.

Related

How to use NSObjectController and Managed Object Context using Cocoa Bindings

Searched entire Internet but couldn’t find the modern solution for my problem.
I want to use NSObjectController in pair with Core Data through Cocoa Bindings and struggle to set it up properly. Worth noting that I’m using latest version of Xcode and Swift.
What I’ve done:
For testing purposes I’ve done the following:
Created an macOS app with “Use Core Data” option selected (the app is not document based);
Dragged 2 NSTextFields into the Storyboard Dragged NSObjectController to the view controller scene;
Added Employee Entity to Core Data model with 2 attributes “name” and “surname”;
Done everything from the answer in How do I bind my Array Controller to my core data model?
Set NSObjectController to entity mode and typed in “Employee”,
Prepares Content selected, Use Lazy Fetching selected so all three options checked;
Binded the NSObjectController’s Managed Object Context in bindings inspector to the View Controller’s managedObjectContext;
Binded NSTextFields as follows: Value - Object Controller, Controller key - selection, Model Key Path - name (for 1st text field) and surname (for 2nd).
That’s it.
First set of questions: What I did wrong and how to fix it if it’s not completely wrong approach?
I’ve read in some post on stackoverflow that doing it that way allows automatic saving and fetching from Core Data model. That’s why I assumed it should work.
So here is a Second set of questions:
Is it true?
If it is then why text fields are not filled when view is displayed?
If it is not then how to achieve it if possible (trying to write as less code as possible)?
Third question: If I used approach that is completely wrong would someone help me to connect Core Data and NSObjectController using Cocoa bindings and show me the way of doing so with as less code written as possible using the right approach?
Taking into account that there no fresh posts about this topic in the wilds I think the right answer could help a lot of people that are developing a macOS app.
Thanks in advance!
I think your basic approach is correct, although it is important to understand that you need a real object, an instance, in order for it to work.
Creating a NSManagedObject subclass is generally desirable, and is almost always done in a real project, so you can define and use properties. You can do it easily nowadays by selecting the data model in Xcode's Project Navigator and clicking in the menu: Editor > Create NSManagedObject Subclass…. Technically it is not necessary, and in a demo or proof-of-concept, you often muddle through with NSManagedObject.
Assuming you are using the Xcode project template as you described, wherein AppDelegate has a property managedObjectContext, the following function in your AppDelegate class will maintain, creating when necessary, and return, what I call a singular object – an object of a particular entity, in this case Employee, which your app requires there to be one and only one of in the store.
#discardableResult func singularEmployee() -> NSManagedObject? {
var singularEmployee: NSManagedObject? = nil
let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: "Employee")
let objects = try? self.managedObjectContext.fetch(fetchRequest)
singularEmployee = objects?.first
if singularEmployee == nil {
singularEmployee = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: self.managedObjectContext)
}
return singularEmployee
}
Then, add this line of code to applicationDidFinishLaunching
singularEmployee()

Associating ViewModels with Fragments in MVVMCross

We have a couple of Fragments that we use as common controls:
MyCommonHeaderA
MyCommonHeaderB
In our common View class we call base.OnCreate(bundle) and once that has returned we fish out the fragment instances and set their ViewModels
var commonHeaderAFragment = (MyCommonHeaderA)this.SupportFragmentManager.FindFragmentById(Resource.Id.header1banner);
if (commonHeaderAFragment != null)
{
commonHeaderAFragment.ViewModel = this.ViewModel;
}
var commonHeaderBFragment = (MyCommonHeaderB)this.SupportFragmentManager.FindFragmentById(Resource.Id.header2banner);
if (commonHeaderBFragment != null)
{
commonHeaderBFragment.ViewModel = this.ViewModel;
}
Until recently this has been working with no problem. Recently we have upgraded Xamarin and MVVMCross.
Now whenever we rotate the device OnCreate is called and the execution path ends up in MvxFragmentExtensions.OnCreate where it tries to lookup a type for the Fragment using FindAssociatedViewModelTypeOrNull. There is no associated ViewModel type for the Fragment. We never needed to, should we have associated a type?
I did try MvxViewForAttribute and concrete typed ViewModel property but neither of those worked as they wanted to create new VM instances.
I have a solution which is that in the base OnCreate, if we have a bundle try and find the Fragments and set their ViewModel property before base.onCreate and when there is no bundle we set the ViewModel property after OnCreate. It is clunky but works. I just wanted to check if we should have been setting up our Fragments differently so that we would not have hit this issue
There is an example available that uses the MvxCachingFragmentActivity: https://github.com/MvvmCross/MvvmCross-AndroidSupport/tree/master/Samples
In there you don't need to worry about those kind of problems anymore.

SubreportProcessing event handler cannot access my viewmodel due to being on a different thread

I have a WPF application that is utilizing the reporting tools included with Visual Studio 2010. I've had some other problems that I've solved by creating a graph of objects that are all marked as serializable, etc., as mentioned on various other web pages.
The ReportViewer control is contained in a WindowsFormsHost. I'm handling the SubreportProcessing event of the ReportViewer.LocalReport object to provide the data for the sub report.
The object graph that I'm reporting on is generated in my viewmodel, and that viewmodel holds a reference to it. The SubreportProcessing handler is in my code behind of my window (may not be the best place - but I simply want to get the ReportViewer working at this point).
Here's the problem: In my event handler, I'm attempting to get a reference to my viewmodel using the following code:
var vm = DataContext as FailedAssemblyReportViewModel;
When the handler is called, this line throws an InvalidOperationException with the message The calling thread cannot access this object because a different thread owns it.
I didn't realize the handler might be called on a different thread. How can I resolve this?
I attempted some searches, but all I've come up with is in regards to updating the UI from another thread using the Dispatcher, but that won't work in this case...
I solved this problem using something I believe is a hack, by adding the following function:
public object GetDataContext() {
return DataContext;
}
And then replacing the line of code from my question with:
object dc = Dispatcher.Invoke(new Func<object>(GetDataContext), null);
var vm = dc as FailedAssemblyReportViewModel;
However, this seems like a hack, and I might be circumventing some sort of safety check the CLR is doing. Please let me know if this is an incorrect way to accomplish this.
That's a nasty problem you have there.
Why don't you use in the view a content presenter which you bind to a windows form host?
And in the view model you would have a property of type of type WindowsFormsHost. Also,in the view model's constructor you could set the windows form's host Child property with the report viewer.
After that is smooth sailing, you could use your report viewer anywhere in your code. Something like this:
View:
<ContentPresenter Content="{Binding Path=FormHost}"/>
ViewModel:
private ReportViewer report = new ReportViewer();
private WindowsFormsHost host = new WindowsFormsHost();
public WindowsFormsHost FormHost
{
get {return this.host;}
set
{
if(this.host!=value)
{
this.host = value;
OnPropertyChanged("FormHost");
}
}
}
public ViewModel() //constructor
{
this.host.Child = this.report;
}
After that happy coding. Hope it helps.

Display a custom login control in Windows Phone with Mvvm light

Ok, so what I am looking to do is to display some sort of login control (maybe a UserControl with a TextBox and PasswordBox) when the app is started.
In a non-mvvm situation, a way of doing this would be to use the PopUp primitive control, add the usercontrol as a child element and off you go.
In an MVVM situation, i'm a bit confused about how you would achieve a simmilar result.
I have looked into messaging with the DialogMessage and this is fine for displaying a typical MessageBox, but what about a custom usercontrol?
any help would be fantastic! I can't seem to find any demo code of this anywhere.
In a MVVM situation you can use a delegate to let your View open the dialog when the ViewModel requests it.
You define a delegate at the VM:
public Func<LoginResult> ShowLoginDialogDelegate;
In your View you define the function that will be called:
private LoginResult ShowLoginDialog()
{
LoginResult result;
// show a dialog and get the login data
return result;
}
Then you "connect" the delegate and method in the View:
_viewModel = new MyViewModel();
DataContext = _viewModel;
_viewModel.ShowLoginDialogDelegate += ShowLoginDialog;
And now you can use it in your ViewModel e.g. when a command is executed like that:
LoginResult result = ShowLoginDialogDelegate();
An easier answer is to control it's visibility through a View State which with a little manipulation can be made to work through databinding allowing the view model to display the "Logon Page" state when required.
I just recently wrote about this for the Silverlight/XNA series which you can view here.
It would be much simplier if the SL4 DataEventrigger was available but hay ho.

Eclipse RCP: How can I update a view when the selected editor changes?

This should be quite a common problem, but I couldn't find anything helpful on the topic.
We are developing an application with Eclipse RCP. The application shows data in an editor of which usually multiple instances are open. In an additional view you can edit the editor-values. When the values are changed in the view they are updated in the editor and it's dirty flag is set.
So far it works fine. What we're missing is: When another editor instance gets the focus, our view should show the data of this editor.
I managed to do that for two views. The second view is sucessfully updated using a TableViewer as selection Provider and registering a SelectionListener in the other view. I tried the same thing for the editor using a Viewer I subclassed from ContentViewer, but it didn't work.
Can this approach be working?
Or do I need a different approach on the problem?
May be you can subclass your view from PageBookView and then provide special adapter for your editor. Outline View is implemented using this approach.
Thank you cerealk, that was exactly what I needed. :-)
Update the View when another Editor is selected
public class myView {
// Create an IPartListener2
IPartListener2 pl = new IPartListener2() {
// If the Editor I'm interested in was updated ...
public void partActivated(IWorkbenchPartReference ref) {
IWorkbenchPart part = ref.getPart(true);
if (part instanceof DetailEditor) {
// ... update the view
Contact contactFromSelectedEditor = ((DetailEditor) part).detailComposite.contact;
detailComposite.update(contactFromSelectedEditor);
}
}
...
}
// Add the IPartListener2 to the page
IWorkbenchPage page = this.getSite().getPage();
page.addPartListener(pl);
}
Why use an IPartListener2 instead of an IPartListener
The IPartListener2 replaces IPartListener with 3.5.
As explained in this this answer:
You should always use IPartListener2 as it can handle part-change events on parts that have not yet been
created because they are hidden in a stack behind another part.
This
listener will also tell you when a part is made visible or hidden or
when an editor's input is changed.

Resources