I have this Windows Phone Page where I load data through the standard ViewModel scope.
public Profile()
{
InitializeComponent();
App.PersonalizedViewModel.favorites.Clear();
DataContext = App.PersonalizedViewModel;
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
if (!App.PersonalizedViewModel.IsDataLoaded)
{
App.PersonalizedViewModel.LoadData();
}
}
This works fine. However when I navigate to this page from some other page the data is still the same. I mean the LoadData() method should recheck updated data right? Please suggest.
EDIT:
My PersonalizedViewModelClass:
public class PersonalizationViewModel: INotifyPropertyChanged
{
public PersonalizationViewModel()
{
this.favorites = new ObservableCollection<ItemViewModel>();
this.Bar = new ObservableCollection<Bars>();
}
public ObservableCollection<ItemViewModel> favorites { get; private set; }
public ObservableCollection<Bars> Bar { get; private set; }
private string _sampleProperty = "Sample Runtime Property Value";
public string SampleProperty
{
get
{
return _sampleProperty;
}
set
{
if (value != _sampleProperty)
{
_sampleProperty = value;
NotifyPropertyChanged("SampleProperty");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a few ItemViewModel objects into the Items collection.
/// </summary>
public async void LoadData()
{
favorites.Clear();
try
{
var query = ParseObject.GetQuery("Favorite")
.WhereEqualTo("user", ParseUser.CurrentUser.Username);
IEnumerable<ParseObject> results = await query.FindAsync();
this.favorites.Clear();
foreach (ParseObject result in results)
{
string venue = result.Get<string>("venue");
string address = result.Get<string>("address");
string likes = result.Get<string>("likes");
string price = result.Get<string>("price");
string contact = result.Get<string>("contact");
this.favorites.Add(new ItemViewModel { LineOne=venue, LineTwo=address, LineThree=likes, Rating="", Hours="", Contact=contact, Price=price, Latitude="", Longitude="" });
}
if (favorites.Count == 0)
{
// emailPanorama.DefaultItem = emailPanorama.Items[1];
MessageBox.Show("You do not have any saved cafes. Long press a cafe in main menu to save it.");
}
}
catch (Exception exc)
{
MessageBox.Show("Data could not be fetched!", "Error", MessageBoxButton.OK);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Implementation of PersonalizedViewModel:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
await App.PersonalizedViewModel.LoadData();
user_tb.Text = ParseUser.CurrentUser.Username;
if (NavigationContext.QueryString.ContainsKey("item"))
{
var index = NavigationContext.QueryString["item"];
var indexParsed = int.Parse(index);
mypivot.SelectedIndex = indexParsed;
}
if (NavigationService.BackStack.Any())
{
var length = NavigationService.BackStack.Count() - 1;
var i = 0;
while (i < length)
{
NavigationService.RemoveBackEntry();
i++;
}
}
}
I don't see the problem, however, I think you need to narrow in on the problem.
First off, you are calling LoadData from 2 places. 1 from MainPage_Load and 1 from OnNavigatedTo. In MainPage_Load it is conditional and in OnNavigatedTo it is always being called. I suggest that you get to a single path through the code instead of 2 so that you don't get different experiences. I personally recommend (without knowing all the details) that you call load data from OnNavigatedTo instead of MainPage_Load. If you want to do it conditionally that is fine but if you are loading the data from memory, it really is unnecessary as you won't improve performance anymore than a few milliseconds. Also, if you are not loading from memory, you may not want to load it conditionally because the underlying data may have changed. In either case, the choice to load data or not should be moved out of the view and into the data layer (but that is for another post).
Once you have a single path chosen (i.e. calling LoadData from MainPage_Load or OnNavigatedTo) you should use your debugger. Put a break point in LoadData method and if it is being called appropriately, then your problem is more specific than your posted question. Here are some questions to think about (you may want to start from the last question and work your way backward)
Questions:
Is LoadData being called appropriately?
Does ParseObject have the correct data?
Is the ParseUser...UserName set properly?
Is the foreach being executed the proper # of times (i.e. does the result of your query have the right # of items?)
Couple Code Tips completely unrelated to this problem:
Single Path through code. Don't call LoadData from more than one place.
Don't call favorites.clear() twice in the same method. (it is called twice in LoadData)
Consistent naming. favorites is lowercase but Bar is upper case.
User proper data types. On your ItemViewModel you have Hours, Latitude, and Longitude. You have them as strings. These clearly are not strings. Also, you should not set them to empty. Empty means they have been set to a value. Emtpy is a valid value. Null means not set. To keep your objects clean and accurate you want to be accurate in how you set things and then deal appropriately with the impact. If you really really want them to be initialized to empty strings, then at least do it in the constructor of ItemViewModel so that every caller doesn't have to know how to initialize every property. I guarantee this is leading to buggy code if you continue using this practice.
Please take the comments as constructive criticism not criticism. I know many people don't like to hear these things but the teams I lead write bugs until they start following these types of guidelines.
Good luck,
Tom
Instead of defining this
App.PersonalizedViewModel.favorites.Clear();
DataContext = App.PersonalizedViewModel;
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
into constructor i.e. Profile I would suggest remove this code from Constructor and add it into your OnNavigatedTo. so the data will load after navigation
Your OnNavigatedTo Method looks like follows
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
App.PersonalizedViewModel.favorites.Clear();
DataContext = App.PersonalizedViewModel;
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
Might be your problem will solve.
Edit
Try this query
var results = (from find in ParseObject.GetQuery("Favorite").WhereEqualTo("user", ParseUser.CurrentUser.Username) select find);
Tried this:
var query = from favorite in ParseObject.GetQuery("Favorite")
where favorite.Get<string>("user") == ParseUser.CurrentUser.Username
select favorite;
IEnumerable<ParseObject> results = await query.FindAsync();
I had a similar Problem.All u want to do here is generate a new instance of the Page.U can do this in two Ways.
One Way is by forcing a GUID along with Page Navigation URI that will create a New Instance of the Page and your Load Data() will work.
NavigationService.Navigate(new Uri(String.Format("/MainPage.xaml?item={0}", Guid.NewGuid().ToString()), UriKind.RelativeOrAbsolute));
The Second Way to implement that Part of your Page in a User Control .Like create a User Control for Load Data() and put it in constructor.It will generate a new Instance everytime you load the Page.
If the problem persists in the front end,you can try this.
1.have you mentioned the below attribute in your xaml page?
<UserControl Loaded="MainPage_Loaded">
So that every time the page loads the data will get loaded on to the page.
2.The data must exist, if you have no problem in the code behind as it is a WPF application and not a web page.
Hope you find it useful.
Two changes required..
Remove the this.Loaded from OnNavigatedTo. That may not be required.
Second move the LoadData to OnNavigatedTo method
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
App.PersonalizedViewModel.favorites.Clear();
DataContext = App.PersonalizedViewModel;
// this.Loaded += new RoutedEventHandler(MainPage_Loaded);
if (!App.PersonalizedViewModel.IsDataLoaded)
{
App.PersonalizedViewModel.LoadData();
}
}
For the purpose of debugging, you can remove the line if (!App.PersonalizedViewModel.IsDataLoaded) and try.
Related
I'm having trouble manipulating forms when from another thread.
I've overcome the issue by loading the form at runtime, showing it then hiding it. This means the form is created on the right thread and can be manipulated using invokes.
This is not the right way to do it. I have 3 problems that come from using this method
I can't spawn another popup box I have to use the one I created at runtime
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
I have to use a variable bool to hold if the popup is open or not.
If anyone could point me in the right direction It would be much appreciated. Currently my code looks like:
On Main form Load:
CallerIDfrm = new frmCallerID();
CallerIDfrm.Show();
CallerIDfrm.Hide();
to manipulate the popup Im using
delegate void StringArgReturningVoidDelegate1(string CallerIDnum, string CallerIDname, string ContactID);
private void CallerID(string CallerIDnum, string CallerIDname, string ContactID)
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate1 d = new StringArgReturningVoidDelegate1(CallerID);
CallerIDfrm.Invoke(d, new object[] { CallerIDnum, CallerIDname, ContactID });
}
else
{
if (ContactID != null || ContactID != "0")
{
CallerIDfrm.ContactID = ContactID;
}
CallerIDfrm.Mainfrm = this;
CallerIDfrm.TopLevel = true;
CallerIDfrm.TopMost = true;
CallerIDfrm.lblCallerIDname.Text = CallerIDname;
CallerIDfrm.lblCallerIDnum.Text = CallerIDnum;
CallerIDfrm.Show();
CallerIDOpen = true;
}
}
To Hide the popup until required again im using:
delegate void StringArgReturningVoidDelegate2();
private void CallerIDClose()
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate2 d = new StringArgReturningVoidDelegate2(CallerIDClose);
CallerIDfrm.Invoke(d, new object[] { });
}
else
{
try
{
CallerIDfrm.Hide();
CallerIDOpen = false;
}
catch
{
}
}
}
I've tried otherways but the Popup loads as if it is not responding and I loose access to the popup.
Ultimately I'd like to be able to spawn multiple popups and have the ability to close them from the Main Form.
What I gather from your question: You have an caller api/lib/class and you like to show CallerId on a popup form when a call is received. Have a look at Events and Event Driven programming.
The following codes has not been tested, I wrote it from top of my head. Might not compile, they are here to show an example:
Create an CallReceived event in api/lib class as follows:
public event EventHandler<CallReceivedEventArgs> CallReceived;
protected void OnCallReceived(EventArgs e)
{
var handler = CallReceived;
if (handler != null)
handler(this, e);
// Note: For C# 6.0 and later, above statements can be simplified to
// CallReceived?.Invoke(this, e);
}
Note: If you don't have access to this api/lib code, create a Gateway class and put your event in there along with mechanism to trigger it.
Also create a CallReceivedEventArgs, this will be used to transfer event data:
public class CallReceivedEventArgs : EventArgs
{
public string CallerIDnum {get; set;}
public string CallerIDname {get; set;}
public string ContactID {get; set;}
}
Now, in your api/lib class raise this event whenever a call is received:
// a call received, replace dummy values with actual values
OnCallReceived(new CallReceivedEventArgs() { CallerIDnum="5554443322", CallerIDname="SOME_NAME", ContactID="SOME_CONTACT" });
Finally in your GUI form, register to said event and process accordingly.
// inside your main form class
private CallerAPI callerApi = new CallerAPI();
// somewhere inside you main form class, register to event
// from your use case, I would place it inside Main Form's constructor
callerApi.CallReceived += callerApi_Callreceived;
// receive event
void callerApi_Callreceived(object sender, CallReceivedEventArgs e)
{
var callerIDnum = e.CallerIDnum;
// etc.
// show callerId form with details
// you need to change frmCallerID's constructor accordingly
CallerIDfrm = new frmCallerID(e.CallerIDnum, CallerIDname, ContantID);
// to be able to track opened popups, you can store them inside a list
// private List<Form> openPopupList = new List<Form>();
//
// alternatively, you can assign CallerIDnum to form's name property
// and store these inside a List<string> instead of List<Form>
openPopupList.add(CallerIDfrm);
CallerIDfrm.Show();
}
Don't forget to unregister from event.
callerApi.CallReceived -= callerApi_Callreceived;
To wrap it up:
I can't spawn another popup box I have to use the one I created at runtime
You can create and show multiple frmCallerID, independent from each other.
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
Since new approach creates CallerID forms based on events, you won't see these form flashing. It'll open whenever a CallReceived event is received.
I have to use a variable bool to hold if the popup is open or not.
A better approach would be: Register to forms FormClosed event, and remove from openPopupList accordingly.
frmCallerID.FormClosed += frmCallerID_FormClosed;
void frmCallerID_FormClosed(object sender, EventArgs e)
{
// remove form from openPopupList
frmCallerID closedPopup = (frmCallerID) sender;
openPopupList.remove(closedPopup);
}
I'm having trouble trying to navigate automatically between pages in my Windows 8.1 app based on a little check. It just doesn't want to navigate to another page when doing this in LoadState, as if something isn't loaded yet, but it doesn't give an error either. When I insert a delay using (for example) await Task.Delay(2000) before doing Frame.Navigate, then my app will redirect without any problem.
protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
MyData oData = await getData();
if (oData != null)
{
this.Frame.Navigate(typeof(newPage), oData);
}
else
{
// do something else
}
}
Do I have to put this code in another load- or navigated-event? Or how can I make this work?
In LoadState and SaveState you should only save and restore the page state (called when suspending and reactivating the app). Do nothing else (like navigating).
Put your logic into the OnNavigatedTo method instead...
If you want to navigate from method that called when page is loads, you should place your navigation code to OnNavigatedTo(...). But do not forget to wrap your code in Dispatcher.RunAsync(...) - Frame navigation in xaml return false
I tried calling Frame.Navigate(...) from the OnNavigatedTo method but still the navigation didn't occur.
There are other answers which say use Dispatcher.RunAsync, but that feels like it's making assumptions about the threading model of Windows Phone.
Here's what I do: attach a handler to the Loaded event of the page instead, and put my "redirect" logic in there. Loaded fires after OnNavigateTo and after NavigationHelper_LoadState, but before the page has become visible.
public LaunchPadPage() {
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.Loaded += LaunchPadPage_Loaded;
this.app = (App)App.Current;
}
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) {
// Let's show the root zone items
// NB: In case we don't have this data yet, do nothing
if (app.Hierarchy != null)
{
DefaultViewModel["Items"] = app.Hierarchy.RootItems;
}
}
private void LaunchPadPage_Loaded(object sender, RoutedEventArgs e) {
// No data? Go to the downloads page instead.
if (app.Hierarchy == null)
{
Frame.Navigate(typeof(DownloadingPage));
}
}
I am now creating an app, that allows user to create and retrieve data from local database. There is one column named LIKE with a default value of int 0. I have a button, but is there anyway that I can press that button then the default value of int 0 will become 1 ? Everytime I press it will add one to the default value int. How can I do that ?
Assuming your database context is set as in the tutorial I mentioned in the comment, your button click event can look like the following:
private void IncrementLikes_Click(object sender, RoutedEventArgs e)
{
//where Likes is your ObservableCollection keeping data from database
LikeItem oneAndOnly = Likes.First() as LikeItem;
//you pulled one and only LikeItem object, now you increment the likes count
oneAndOnly.LikesCount += 1;
//and here you tell the database that the changes need to be saved.
//This call can be delayed to the OnNavigatedFrom event,
// depending on your needs.
LikeDB.SubmitChanges();
}
and here is some supporting code that belongs to the .cs file of the page:
//your database context
private YourDatabaseDataContext LikeDB;
// Define an observable collection property that controls can bind to.
private ObservableCollection<LikeItem> _likes;
public ObservableCollection<LikeItem> Likes
{
get
{
return _likes;
}
set
{
if (_likes != value)
{
_likes = value;
NotifyPropertyChanged("Likes");
}
}
}
// Constructor
public MainPage()
{
InitializeComponent();
LikeDB = new YourDatabaseDataContext(YourDatabaseDataContext.DBConnectionString);
this.DataContext = this;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Define the query to gather all of the to-do items.
var likesFromDB = from LikeItem like in LikeDB.Likes
select like;
// Execute the query and place the results into a collection.
Likes = new ObservableCollection<LikeItem>(likesFromDB);
// Call the base method.
base.OnNavigatedTo(e);
}
Using the database for just one value may be too much, so you might want to look at other alternatives, such as Windows Phone settings.
I'm creating an app similar to the stock Messaging. But i'm very new to wp8 development and also to c# and .net
I'm using the Long List Selector to display messages. Messages are loaded on NavigatedTo event of the page. the handler is async as it is loading the data from a webservice, when there are 0 stored in local db, then it saves them in local database.
I would like to scroll to the last message after the data is loaded.
the page OnNavigated to
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string contactId = "";
if (NavigationContext.QueryString.TryGetValue("contactId", out contactId))
{
await App.MessagesViewModel.LoadData();
DataContext = App.MessagesViewModel;
//scroll to last message, but it's apparently to soon
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
llsMessages.ScrollTo(lastMessage);
}
}
but this throws an exception System.ArgumentException: The provided item doesn't exist in the collection. So i figured the list hasn't yet changed.
So i tried different events of LongListSelector that would indicate that it has already added the data from the view model. After a while of experimetnation i came up with this
private void llsMessages_SizeChanged(object sender, SizeChangedEventArgs e)
{
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
if (lastMessage != null)
{
llsMessages.ScrollTo(lastMessage);
}
}
but this works only when the messages are loaded from the database. When loading from webservice the last message is null.
So after load i'm on the first message at top, then i navigate away from the page, then come back, the list scrolls to bottom. i would like to eliminate this, but i have no idea how.
is there any way how to accomplish this?
Maybe this will work:
private async Task DoAndScroll()
{
await App.MessagesViewModel.LoadData();
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
llsMessages.ScrollTo(lastMessage);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string contactId = "";
if (NavigationContext.QueryString.TryGetValue("contactId", out contactId))
{
DataContext = App.MessagesViewModel;
DoAndScroll();
}
}
Try the ItemRealized event of the longlistselector. Check whether the currently processed item in the selector is the last item of your datacontext and if it is so, then scroll to that item. Below code worked for me.
private void longlist_ItemRealized(object sender, ItemRealizationEventArgs e)
{
Search.BindSearch search = e.Container.Content as Search.BindSearch;
if (search != null)
{
int offset = 0;
if (OCollectionBindSearch.Count - OCollectionBindSearch.IndexOf(search) <= offset)
{
longlist.ScrollTo(search);
}
}
}
I have a simple question in asp.net.
I want to know if it is possible to get data from controls in my user control directly . I want to do it without using Session variable,Viewstate ...
EDIT: I now use the method of declaring public variables in the UC.
Here is a part of Page_load from my parent page:
this.plan_action = (UCPlan)Page.LoadControl("~/Association/UCPlan.ascx");
PlaceHolder1.Controls.Add(this.plan_action);
if (this.plan_action.Validate == true)
{
CheckBox1.Checked = true;
//String référence = Session["liste_action"].ToString();
for (int i = 0; i < this.plan_action.List1.Count; i++)
{
Label8.Text += this.plan_action.List1[i].Référence + "/";
//Label8.Text += "/";
}
}
but my variable validate stay to false.
Here is the code where I change the value of the validate variable with it declaration:
private bool validate;
public bool Validate
{
get { return validate; }
set { validate = value; }
}
protected void Button2_Click(object sender, EventArgs e)
{
//myCommand.Connection = myConnection;
//Session["liste_action"] = this.List;
this.Validate = true;
//Response.Redirect("../Risques_folder/AjouterRisque.aspx");
}
Thank you for your help,
Quentin
UPDATE due to new information
You need to learn about the sequence of events in ASP.NET.
The Load of the page happens a long time before the Click handler of Button2 in your UserControl... so the Validate property is always going to be set to false.
You have two obvious options (as I see it)...
Keep the creation of the UserControl in your Page_Load (or preferably, move it to your Page_Init, as this is normally the most appropriate place for it). Then place your check for the Validate property in a Page_PreRender.
Or, create an Event in your UserControl, Raise that event on the click of Button2, and handle the event in the Page.
ANOTHER UPDATE
For the 2nd of the two options above, in your UserControl class have the following...
public delegate void ButtonClickedDelegate(object sender, EventArgs e);
public event ButtonClickedDelegate ButtonClicked;
In the Button2_Click method of the UserControl (after setting the this.Validate = true;) call...
ButtonClickedDelegate(sender, e);
In the Page_Init of the Page, put something like...
ctrl1.ButtonClicked += new UCPlan.ButtonClickedDelegate(ctrl1_ButtonClicked);
And then have a new method called something like
void ctrl1_ButtonClicked(object sender, EventArgs e)
{
if (ctrl1.Validate)
{
...
}
}
Remember, as you control the delegate you can pass whatever information you want, including an entire class. So instead of calling the Validate property, create a new instance of the class you want, and pass that as a delegate parameter.
You can find more information on delegates and events on MSDN.
ORIGINAL ANSWER
Unless I've missed something, this is a very simple ASP.NET concept...
You can create properties and/or methods.
For example, as a property...
public string MyProperty
{
get { return "My Property Value"; }
}
Or as a method
public string MyMethod()
{
return "My Method Value";
}
If you're talking about passing the values between the UserControl and the ASP.NET Page that contains it, then in your Page, you can simply call the property or method. If your control was called (for example) myCtrl, then you can something like...
string prop = myCtrl.MyProperty;
string meth = myCtrl.MyMethod();
(On the back of the great comment from AHMED EL-HAROUNY)
If you're talking about passing the values to the client side page, then you can use the same properties / methods directly in the HTML markup. However, in this case, the properties / method can be declared as protected rather than public
For instance, to display the value...
<%=MyProperty%>
Or
<%=MyMethod()%>
Or if you're going to use the value in javascript, something like...
var myProp = "<%=MyProperty%>";
Yes That is possible, But exposing the controls in the UserControl as Public.