In a Windows Phone 7 Silverlight application I call a new page using
NavigationService.Navigate(new Uri("/View/SecondPage.xaml", UriKind.Relative));
Now I want to pass parameters to the new page. I understand a simple parameter can be passed using:
NavigationService.Navigate(new Uri("/View/TilgungsratePage.xaml?id=4711", UriKind.Relative));
and read in the new page using
protected override void OnNavigatedTo(Microsoft.Phone.Navigation.PhoneNavigationEventArgs e)
{
base.OnNavigatedTo(e);
String id = NavigationContext.QueryString["id"];
}
For simple parameters this is ok, but how do I pass a list?
Complex objects?
Anything but simple values?
In his book "Programming Windows Phone 7" (chapter 6, section 3, "Sharing Data Among Pages") Charles Petzold recommends properties in the App class (derived from Application). Every page has access to it via Application.Current. Also interesting is the dictionary PhoneApplicationService.Current.State. It's usefull for tombstoning. The whole chapter maybe interesting for reading.
You should also look at MVVM pattern and the messenger class.
Here's some references:
MVVM Overview
MVVM Foundation Messenger
Good SO question on the messenger from MVVM Light
MVVM Light Blog
take a look how I've implemented navigation in PhoneCore Framework: A framework for building of WP7 application. Shortly, I've built my navigation service on top of WP7 navigation. It uses custom page mapping and allows to pass custom parameters to view model automatically.
Use global variables, make a new class for GlobalVariables:
public static class GlobalVariables
{
public static string my_string = "";
public static int my_int = -1;
}
Then you can access the Global Variables class for different pages:
GlobalVariables.variable_name;
You should save the object to IsolatedStorage.
Just Serialize it with Json.net library and save the string to IsolatedStorage. On the next page get the string from IsolatedStorage and convert it back to the object you want with the json.net library!
Related
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.
Building a WP7 app using MVVM light for my view models. I'm using the ViewModelLocator that gets added when you add the library through NuGet. Works great but now I need to get access to a ViewModel from code.
In my code the user clicks a button and I need to search the MainViewModel (which contains several view models) and find one based on the criteria the user entered.
Normally I would just response to the Click event of the button but I don't have an instance variable of the ViewModelLocator class to get a hold of the MainViewModel to perform the search. With the default template (non-MVVMLight) for Windows Phone 7, the App class has a static variable to the main view model so you can access it anytime with App.ViewModel.
There's some talk from twitter about using commands which would be good, but at some point I have to perform a code search across multiple vms to get the results I need. Probably need to inject a ISearchViewModel service into the View or something to make this work.
Here's the implementation of ViewModelLocator that is provided:
public class ViewModelLocator
{
private static MainViewModel _main;
public ViewModelLocator()
{
_main = new MainViewModel();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return _main;
}
}
}
So from the code behind of another view, how do you get access to Main (MainViewModel contains all the lists of data and has a search method I call)? Or Should you?
Just wondering how people are solving this type of problem?
Thanks.
In MVVM-Light the ViewModelLocator is provided as an application resource. Therefore you can still directly access it, but the syntax is different. If you look at your App.xaml you should see this piece of code somewhere.
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
From anywhere in your application you can access the App's resources and therefore also the MainViewModel with this piece of code:
(App.Current.Resources["Locator"] as ViewModelLocator).Main
This works for any application resource.
If you created the ViewModelLocator as in the template you have static references to the ViewModels. The mvvmlocatorproperty-snippet creates ViewModel-properties like this.
This means that you could just instantiate a new ViewModelLocator to locate the ViewModels in your code behind button click. It will always be the same viewmodels independent of the different instances of the ViewModelLocator
To access the MainViewModel from your code you can add this property to your class:
public ViewModel.MainViewModel myContext { get { return (DataContext as ViewModel.MainViewModel); } }
Then you can just use myContext.[whatever]
You can just use ViewModelLocator.MainViewModelStatic. Default template for MVVMLight have a static property for each your viewmodel.
Just starting to get the hang of MVC3 and want to start building out some custom controls that I can just drop into a view. Specifically I want to be able to drop in some html form controls that will automatically add some javascript for validation.
something like this is what I'd want:
#Html.VTextBox("MyTextBox","",new {#vType="PhoneNumber", #vMessage="You Must Enter a Phone Number" })
when the page is rendered, I'd want it to search for any custom elements I create then drop in the appropriate javascript to validate client side. I did something very similar to this with asp.net web forms for custom controls ie
<ucc:VTextBox ID="MyTextBox" runat="server" message="You must fill this in" />
just wondering if MVC3 offers the same extensibility. Any resources or advice you can provide would be greatly appreciated.
TIA
To start off with, you'll need to look into extension methods and more specifically, how to create extension methods for the HtmlHelper class so you could write code like the example you've shown above.
For the example you've shown above, you could write code like this:
public MvcHtmlString VTextBox(string name, object value, object htmlAttributes)
{
StringBuilder html = new StringBuilder();
// Build your html however you want
// Here's a simple example that doesn't
// take into account your validation needs
html.AppendFormat("input type=\"text\" name=\"{0}\" value=\"{1}\" />", name, value);
return MvcHtmlString(html.ToString());
}
Then in your view you can use the example above like so:
#Html.VTextBox("MyTextBox","",new {#vType="PhoneNumber", #vMessage="You Must Enter a Phone Number" })
NOTE: You'll need to import the namespace the extension method is in to your view. Simplest method, put a #using ExtensionMethodNamespace at the top of your view. You can make the namespace automatically imported to all your views by fiddling with the web.config (and maybe the Global.asax).
ADDENDUM: Please note RPM1984's comment below where he advises to use TagBuilder in place of StringBuilder which is sound advice since this is the exact scenario TagBuilder was designed for. He also mentions strong typing the helper to the model which is also great advice.
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)
Hi WP7 mobile passionate developers!
I'm trying to use the default provided Bing Map control from Windows Phone controls.
Specifically I'm trying to use a custom TileSource to provide a custom made tiled map that will be stored in the project as a folder (Content files) or in isolate storage.
Down I present the custom class I try to use with map tiles/images stored in "map" folder in ZXY storage format as content files.
public class CustomTileSource : Microsoft.Phone.Controls.Maps.TileSource
{
private string uriFormat = #"map/{0}/{1}/{2}.png";
public string UriFormat
{
get { return uriFormat; }
set { uriFormat = value; }
}
public override Uri GetUri(int x, int y, int zoomLevel)
{
var url = string.Format(UriFormat, zoomLevel, x, y);
return new Uri(url, UriKind.Relative);
}
}
Trying to use this is not working and custom tiles are not shown although no error is thrown.
Does anyone tried to use windows phone map control this way?
If that's not the right approach which one is? Any workaround?
Thank you in advance!
Claudiu
Have you set the Build Action on your image(s) to Content?
The Exception is:
This operation is not supported on a relative URI.
at System.Uri.get_AbsoluteUri()
at System.Windows.Media.MultiScaleTileSource.GetTileLayerUrl(IntPtr nativeTarget, Int32 tileLevel, Int32 tileX, Int32 tileY, Int32 uTileImageIndex, IntPtr& fullTileUri, UInt32& fullTileUriLength)
How to get an image from IsolatedStorage, Ressource or MediaLibrary with UriKind.Absolute i not already found out
...maybe you know?
This seems to be quite a FAQ problem - Map Tile Caching for Offline Viewing - shame there isn't an FAQ solution :/
Did you try constructing an absolute file url using the app id as described in this question? Map Tile Caching for Offline Viewing
I tried,
Did you try constructing an absolute file url using the app id as described in this question? Map Tile Caching for Offline Viewing
Map component is not showing me images, but simple
Image.Source = new BitmapImage(new Uri("file:///Applications/Install/0277CC52-888B-4593-A28D-4CFF818E81E7/Install/maps/-1040122162.jpg", UriKind.Absolute));
Is showing image...
You can find a solution in the blog http://invokeit.wordpress.com/2012/06/30/bing-mapcontrol-offline-tiles-solution-wpdev-wp7dev/
In general, you just need
Override GetUri function in TileSource class: return null to let MapLayerTile ignore this tile, and save the tile information to some background worker
In the background worker, loading the tile from anywhere, either isolation storage or network, and then manually add it to a MapLayer control.