Xamarin persist theme - xamarin

Currently I am only testing on an Android emulator. I have installed the Theme Nuget packages.
In my App constructor I have:
// Load the desired theme (default to Light)
if (Current.Properties.TryGetValue("Theme", out object theme))
Resources = theme as ResourceDictionary;
else
Resources = new LightThemeResources();
I then have a method in the App class:
public async Task SwitchTheme()
{
// Switch the current theme from List to Dark to Light
if (Resources?.GetType() == typeof(DarkThemeResources))
Resources = new LightThemeResources();
else
Resources = new DarkThemeResources();
// Persist the Theme
Current.Properties.Add("Theme", Resources);
await Current.SavePropertiesAsync();
}
When I call the method the theme switches from light-dark-light etc. But when I restart the App, it always defaults to Light. As if the "await Current.SavePropertiesAsync();" did not work.
Can anyone suggest what the problem may be?

Xamarin Forms Properties is intended for use with C# value types and objects that can be easily serialized - not complex objects like Resources.
From the docs
Values saved in the properties dictionary must be primitive types,
such as integers or strings. Attempting to save reference types, or
collections in particular, can fail silently.
All you really need to do is store a string value - either 'light' or 'dark' and then load the appropriate theme based on that. You don't actually need to store the theme itself.

Related

MVVMCross MvxDialogFragment Restore Issue - Does not have MvxFragmentPresentationAttribute

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.

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.

IsolatedStorageSettings needs Update?

I'm using IsolatedStorageSettings on WP7 to store an objects list:
List<T>
I need to search an item inside my list and to update some properties of the searched item.
I'm using this code:
List<Article> listArt = null;
IsolatedStorageSettings.ApplicationSettings.TryGetValue("ArticleListStorage", out listArt);
var queryList = (from anItem in listArt where (anItem.Id == _id) select anItem).ToList<Article>();
a = queryList[0] as Article;
//mark Article as read
a.Readed = true;
When I continuously navigate the various page inside the app, I can see the property Readed correctly evalued.
But, when I click on WP7 Start button and reopen my app (without close emulator) I see the property not correctly evalued.
Need I to update my object inside list and so inside Isolated Storage?
Not updated by reference?
I tried also this, ant it doesn't work:
listArt[0].Readed = true;
listArt[0].Favorite = true;
IsolatedStorageSettings.ApplicationSettings["ArticleListStorage"] = listArt;
IsolatedStorageSettings.ApplicationSettings.Save();
What is wrong?
Thank you so much!
You can either explicitly call Save() on the settings or wait for the app to close normally and then they will be saved automatically.
As a general rule I'd suggest always explicitly saving settings once you change them. (Unless you have a very good reason not to.)
What's happening in your situation is that you are pressing the start button which causes your app to tombstone. When you launch a new instance of the app the tombstoned version is destroyed without all the code which normally runs on application close (including auto-saving settings) being executed.
Here's and example of using Save:
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("some-key"))
{
settings.Remove("some-key");
}
settings.Add("some-key", "my-new-value");
settings.Save();
Yes, you've got to save your list again. Think of isolated storage as a file system - you wouldn't expect to be able to load an XDocument from disk, make changes in memory and automatically see those changes reflected on disk, would you? Well, it's the same with isolated storage.

Allowing user selected Global Theme for winform app

I am using DevExpress controls in a winform app I am building for internal use. My app has about 30 forms in total and I am trying to figure out a way to allow my user's to select a theme. I have seen this mentioned here at SO multiple times in answers to other posts.
I understand how the StyleController works, I believe, but what I am wondering is how I can use 1 Style controller for the whole app.
Right now I am trying to create 1 StlyeController at the Shell form and then pass a reference to it to each child form. From there I then have to programatically set the StyleController property for each control. I don't mind I just wonder, especially from those who have done this, if there is a simpler way?
It is very simple. This example is assuming that you are using skins.
In the constructor of your main form calls:
DevExpress.Skins.SkinManager.EnableFormSkins();
This will enable your form to use the current skin. It is also important that each of your forms derived from XtraForm.
After that you need to setup the global look and feel object for your application:
//This set the style to use skin technology
DevExpress.LookAndFeel.UserLookAndFeel.Default.Style = DevExpress.LookAndFeel.LookAndFeelStyle.Skin;
//Here we specify the skin to use by its name
DevExpress.LookAndFeel.UserLookAndFeel.Default.SetSkinStyle("Black");
If you want to set the look and feel of your application like Office 2003, the setup is different. You just have to call the following function:
DevExpress.LookAndFeel.UserLookAndFeel.Default.SetOffice2003Style();
So, every control of devexpress will use these settings to paint themselves. It is possible to specify a custom LookAndFeel object for some controls but I never used it because I dont see the point to have a custom display for a control or a form.
Exception:
There is one exception in Devexpress framework. The NavBarControl does not use the skin technology automatically from your global LookAndFeel object, you need to specify a setting to enable that:
//To use the current skin
youNavBarControl.PaintStyleName = "SkinNavigationPane";
//To use the current look and feel without the skin
youNavBarControl.PaintStyleName = "NavigationPane";
With version 11.2 I used the information in this article:
http://www.devexpress.com/Support/Center/p/K18013.aspx
In summary :
* Inherit all your forms from XtraForm
* Leave look and feel settings default so that they use the default skin
* Modify the default skin with the following line of code:
DevExpress.LookAndFeel.UserLookAndFeel.Default.SkinName = "DevExpress Dark Style";

Resources