IsolatedStorageSettings needs Update? - windows-phone-7

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.

Related

Navigation.PushAsync(). When the back arrow is pressed the page seems to be still in memory

I am writing a Xamarin app. In the App.Xaml.cs I expose some events.
I set up a Main Page like this:
var nav = new NavigationPage(new MainPage());
MainPage = nav;
Then from the Main Page I push another page:
void DevicePictureBox_Click(object sender, EventArgs args)
{
Image imse = (Image)sender;
String NameofDevice =
devicesHandlesDictionary_byDevicepictureID[imse.Id].Name;
var p = new DevicesPage(NameofDevice);
Navigation.PushAsync(p);
The DevicesPage subscribes to an event (exposed from App).
Setting up some Debug.WriteLine along the code I discovered that DevicesPage is still active when navigate back to MainPage (pressing the back arrow or with Navigation.PopAsync()).
Also when I navigate to DevicesPage again the code is executed twice (Degug.WriteLine string in the Output window appear twice) and 3,4,5 etc. times if I keep going back and forward. It seems like a new page gets created every time and the previous still run in background.
I understand that events should be unsubscribed (in that case where should be done). Also it baffles me that the code still seems to be running in the page navigated back. How to remove the page?
DevicesPage is still active when navigate back to MainPage
Just because you navigate away from a page doesn't mean you're done with it. You may want to re-use it later. You can explicitly clean up it's resources if you need to (for instance, unsubscribe your event handlers in OnDisappearing) but this doesn't happen automatically
It seems like a new page gets created every time
it does, because you are explicitly creating a new instance of DevicesPage each time
var p = new DevicesPage(NameofDevice);
Navigation.PushAsync(p);
there is nothing preventing you from keeping a reference to a DevicesPage instance and re-using it instead of always creating a new one

How to remove previous pages from memory?

I'm using an implementation of Master/Detail in my Forms app. The problem is that even after I change pages the previous one is still running, I even had to do some inactive/isActive code in order to stop the code execution of those pages.
This is how I change pages:
public void GoTo(Page page)
{
Detail = new NavigationPage(page);
IsPresented = false;
UserDialogs.Instance.HideLoading();
}
Then I do:
Application.Current.HomePage.GoTo(new ServiceSelectionPage());
But the previous page is kept running. What am I doing wrong? Or how do I remove those pages from memory? I tried setting Detail = null to no avail.
Thanks!
It is by design. You could:
Set their instances to null, then wait for garbage collection. You could force it with GC.Collect(); - but it's not guaranteed.
Reuse pages instances (use same Page instance and only change current BindingContext). You could use custom Page Factory for that.

How to access the content of a webpage displayed in a tab from a Firefox addon

In a Firefox extension, I am attempting to add a form into the web page by inserting DOM elements, and then process any data that the user enters in the form.
I have tried several methods, but have been unable to get my form inserted into the webpage's document. I tried using the different types of add-ons, Overlay (XUL) and the Add-on SDK, but I have not been able to get it to work.
Overlay/XUL: I investigated and found nothing that specifically showed me how to change the contents of a web page.
Using the Addons SDK: The only working code which I found was the demo code in "Modifying Web Pages Based on URL" which appeared to give:
var html = sth;
$("body").html(html);
I tried:
$('.id_of_ele').html('I want to show');
It doesn't work.
So far the only thing which has gotten me close is to use unsafeWindow.document, but I believe that is a really a bad idea, and the code looks really bad.
How do I access the the document of a webpage from a Firefox extension?
If you are looking for examples of known working code, you can always download one or more extensions from Mozilla Add-ons which do something close to what you want to accomplish and look at how they do it. Obviously, you should look at the license (linked on each extensions page) to see what the legal status of the code it. There are literally thousands of working examples there. The vast majority of which have code which is licensed in a way which permits you to re-use it.
The jQuery accesses which you are trying to use rely on the document variable pointing to the document which you are wanting to modify. In the context in which you are running, a Firefox add-on, the document variable may, by default, point to a document which is an ancestor of the webpage you are interested in or not be defined at all. What document actually is will depend on the context from which your add-on code was invoked. In a Firefox add-on, the document variable will almost never, by default, point to the content of a web page. You have to remember that you are writing code that is intended to run in a context that is much larger (entire browser/user agent) than that which is used for content scripts on a webpage (context within the browser is restricted to only the content of the webpage from which the script was run, or data which is obtained from references originating from within the page).
Gaining access to the document for the currently selected tab:
Changing the content document is very easy. You can change it just like you would from any JavaScript. The issue that you may find frustrating is obtaining a reference to the document.
Firefox overlay and restartless/bootstrapped have a great amount of power over the entire browser. However, the context, and what window points to, or even if it is defined, depends greatly on how the JavaScript was invoked. This can be both confusing and frustrating. On MDN, there is a document "Working with windows in chrome code" which describes many of the issues.
From extensions you have access to all windows and tabs. However, what you probably want is just some code that works to get you access to the current selected document.
This should work from all contexts to get you a reference to the document for the currently selected tab:
var selectedTabWindow = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
var selectedTabDocument = selectedTabWindow.content.document;
If you have code which you are converting from a content script which just expects to find window and document objects, you could write something like:
if (typeof window === "undefined") {
var window;
} else {
//Keep a reference to whatever was defined as window.
var originalWindow = window;
}
//Get the window from the most recently selected tab.
window = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//Now that we have a window for the most recently selected tab,
// get the document for it.
if (typeof document === "undefined") {
var document;
} else {
//Keep a reference to whatever was defined as document.
var originalDocument = document;
}
document = window.content.document;
//Now that we have a window for the most recently selected tab,
// get the gBrowser for it.
if (typeof gBrowser === "undefined") {
var gBrowser;
} else {
//Keep a reference to whatever was defined as gBrowser.
var originalGBrowser = gBrowser;
}
gBrowser = window.gBrowser;
The above will, obviously, overwrite any currently variables currently defined as window, document and gBrowser. Depending on the context in which you are running, and the scope in which you define these this could be either a good thing, or it might be a bad idea to change that reference. For example, if the code is running in a popup window then window is a reference to window of the popup. In that case, you can get a reference to the window from which the popup was opened with:
var windowWhichOpendedThisOne = window.opener;
var documentForWindowWhichOpendedThisOne = window.opener.content.document;
If you are in an event handler, then you can get the window for the target of the event from:
var windowInWhichEventTargetExists = event.view;
Choosing what to do based on the URL:
Once you have the correct document it should be quite easy to choose what to do based on the document's URL:
var currentUrl = document.location.href;

Prism 4: Unloading view from Region?

How do I unload a view from a Prism Region?
I am writing a WPF Prism app with a Ribbon control in the Shell. The Ribbon's Home tab contains a region, RibbonHomeTabRegion, into which one of my modules (call it ModuleA) loads a RibbonGroup. That works fine.
When the user navigates away from ModuleA, the RibbonGroup needs to be unloaded from the RibbonHomeTabRegion. I am not replacing the RibbonGroup with another view--the region should be empty.
EDIT: I have rewritten this part of the question:
When I try to remove the view, I get an error message that "The region does not contain the specified view." So, I wrote the following code to delete whatever view is in the region:
// Get the regions views
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
var ribbonHomeTabRegion = regionManager.Regions["RibbonHomeTabRegion"];
var views = ribbonHomeTabRegion.Views;
// Unload the views
foreach (var view in views)
{
ribbonHomeTabRegion.Remove(view);
}
I am still getting the same error, which tells me there is something pretty basic that I am doing incorrectly.
Can anyone point me in the right direction? Thanks for your help.
I found my answer, although I can't say I fully understand it. I had used IRegionManager.RequestNavigate() to inject the RibbonGroup into the Ribbon's Home tab, like this:
// Load RibbonGroup into Navigator pane
var noteListNavigator = new Uri("NoteListRibbonGroup", UriKind.Relative);
regionManager.RequestNavigate("RibbonHomeTabRegion", noteListNavigator);
I changed the code to inject the view by Registering it with the region, like this:
// Load Ribbon Group into Home tab
regionManager.RegisterViewWithRegion("RibbonHomeTabRegion", typeof(NoteListRibbonGroup));
Now I can remove the RibbonGroup using this code:
if(ribbonHomeTabRegion.Views.Contains(this))
{
ribbonHomeTabRegion.Remove(this);
}
So, how you inject the view apparently matters. If you want to be able to remove the view, inject by registration with the Region Manager
StockTraderRI Example Project by Microsoft contains the following example of removing views from region in ViewModel.
private void RemoveOrdersView()
{
IRegion region = this._regionManager.Regions[RegionNames.ActionRegion];
object ordersView = region.GetView("OrdersView");
if (ordersView != null)
{
region.Remove(ordersView);
}
}
Is it possible you have a RegionAdapter that is wrapping the view inside another view before adding it? The ribbonHomeTabRegion should have a property with the collection of views - is there anything inside it?

Passing data from page to page

I'm looking for the best practice on how to pass data from page to page.
In Page A I have a button that fires off Page B.
On Page B I have 6 textboxes that allow the user to enter information.
When the user is done, the click on a button that brings them back to Page A.
I want to pass that data back to Page A.
I've seen suggestions to:
build XML documents and save to Isolated Storage
use the App class to store information in properties
pass it like a query string
I'm looking for the Best practice. Is there one that Microsoft recommends or one that is generally accepted as the best way?
Thanks
PhoneApplicationService.Current.State["yourparam"] = param
NavigationService.Navigate(new Uri("/view/Page.xaml", UriKind.Relative));
then in other page simply
var k = PhoneApplicationService.Current.State["yourparam"];
Personally I'd store the values entered on Page B in a model(object) that is also accessible to Page A.
Depending on how you're navigating to Page A the second time, one or more of the following may be usful to help understand passing values between pages:
How to pass the image value in one xaml page to another xaml page in windows phone 7?
Passing a complex object to a page while navigating in a WP7 Silverlight application
How to pass an object from a xaml page to another?
How to pass a value between Silverlight pages for WP7?
How do I navigate from one xaml page to another, and pass values?
One thing you can consider is to use MVC: let your App be the controller, store all data in the model, and the pages are just views that contains pure UI logic. In this case your pages are painters and you pass your model object around. This gives nice isolation of business logic and the UI so that you can rev them easily.
BTW, Silverlight and XAML are great tools for MVC so it's a natural match.
There's a couple of things at play here. First of all, if/when the user uses the Back button to return to page A instead of your button, is the information in the text boxes exchanged or not (is Back = Cancel, or is Back = OK?)
That said, if you're using NavigationService.GoBack (which you should be instead of NavigationService.Navigate, because if you use the Navigate call, repeated hits of the back key will cause all kinds of bad UX for your users), then QueryStrings are not an option. Because pages really have no way to reference each other in the WP7 Silverlight nav system, you need to use a 3rd party to hold your data. For that, you can turn to (a) Isolated Storage (slow & heavy, but fail-safe), (b) Use the PhoneApplicationService.State dictionary, or (c) use Global properties of some kind, either hung off of the application object, or using Statics/Singletons...
Remember to watch for Tombstoning behavior when you do this - your page will process the OnNavigatedTo method when (a) you navigate into it in your application (b) you navigate back to it when you complete your work on Page B, or (c) you tombstone your app from that page and return to your application using the Back key.
Sorry I didn't give a more direct answer there - a lot depends on your specific circumstances. In the most general case, I'd strongly consider using the App State Dictionary on the PhoneApplicationService...it is lightweight, easy to use, and survives tombstoning. Just be sure that your keys are as unique as they need to be.
If you create a new Windows Phone project and use the Windows Phone Databound Template you will have most of the work done for you.
What you will want to do is set up the ViewModel to contain all the data for your app. You can serialize and deserialize this data using IsolatedStorage so that it's saved across application sessions and when Tombstoning.
In the template you will notice MailViewModel and ItemViewModel. MainViewModel stores all the data your application needs including an ObservableCollection of ItemViewModel, and ItemViewModel represents the individual data type for your application.
On the DetailsPage.xaml page you'll want to DataBind each textbox to the App.MainViewModel Items. Set the binding to TwoWay if you want the ViewModel to get updated as soon as the user manipulates the data on DetailsPage.xaml. You can optionally set the Binding to OneWay and then have an OK button that writes the changes back to the ViewModel and saves to IsolatedStorage.
Here is an example of what a Binding looks like:
<TextBlock x:Name="ListTitle" Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
In this case LineOne is a property in ItemViewModel and the page gets this data from the query string when the user selects an item from the MainPage.xaml. The DataContext for the page determs where the databound information comes from.
Here is the snippet where the MainPage passes the selected item from the ViewModel to the DetailsPage.
// Handle selection changed on ListBox
private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// If selected index is -1 (no selection) do nothing
if (MainListBox.SelectedIndex == -1)
return;
// Navigate to the new page
NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative));
// Reset selected index to -1 (no selection)
MainListBox.SelectedIndex = -1;
}
Here is how the DetailsPage gets the selected item.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.Items[index];
}
}
Play around with the default template above and ask any additional questions.
The beauty of databinding and the ObservableCollection is that you can just update the data and the UX will reflect those changes immediatley. This is because any changes to the data fires off an event:
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
NotifyPropertyChanged() that broadcasts this information to the View.
You can also keep it simple and use PhoneApplicationService.Current.State which is basically a hashtable. You will need to implement your own marshalling to and from isolated storage if you want anything to outlive the app.
Omar's suggestion to use the Windows Phone Databound Template is probably the best idea on this page. It amounts to the same as my suggestion but you will get a better result (more maintainable code) at the cost of a longer steeper learning curve.
I suggest you do it my way and then do it again Omar's way.
as i implemented like this.. Whether its correct or not i dont know..
When u click news list page it should open the news detail page.
I want to pass the selected news item contents from news List-Page to news-details Page.
the News list page contains following method.
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
NewsDetailsPage newsDetailPage = (e.Content as NewsDetailsPage);
if (newsDetailPage != null)
newsDetailPage.SelectedNewsItem = SelectedNewsItem; //Contains the news details
base.OnNavigatedFrom(e);
}
In the News details Page. U can access that(SelectedNewsItem) object.
This may or may not be correct.
One option is to use Application.Resources:
Store data:
Application.Current.Resources.Add("NavigationParam", customers);
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
Retrieve data:
var customers = (List<Customer>) Application.Current.Resources["NavigationParam"];
Here's a blog post with describes this in more detail: http://mikaelkoskinen.net/windows-phone-pass-data-between-pages-application-resources/ (author: me)

Resources