I have a LongListSelector with items. I made a contextMenu, so when you hold an item for some time you get an option to "pin to start". I didn't have a problem with binding the right item "name" and tile picture, but I have a problem with opening the right item than (or opening in general).
This is the code to pin the item to start:
private void Pin_click(object sender, RoutedEventArgs e)
{
PhoneApplicationService.Current.State["country"] = countriesList.SelectedItem;
Country selectedCountry = (sender as MenuItem).DataContext as Country;
string page = "/countryPage.xaml?id=" + countriesList.SelectedItem;
StandardTileData tileInfo = new StandardTileData
{
BackgroundImage = new Uri(selectedCountry.Flag, UriKind.Relative),
Title = selectedCountry.Name
};
ShellTile.Create(new Uri(page, UriKind.Relative), tileInfo);
}
The problem is on the countryPage, because there is no data context and the app crashes:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var k = (app.MainPage.Country)PhoneApplicationService.Current.State["country"];
DataContext = k;
}
2 suggestions based on your code :
Is the itemSource for your LongListSelector a collection of Country objects ? Because on the second page, you are trying to typecast the state data into a country object. That might be a problem.
Secondly, try using IsolatedStorage instead of Phone State, as State is transient data, as in not guarrenteed to last forever.
Presumably, you have a list of Country objects somewhere that you are using to populate the ItemsSource of your countriesList LongListSelector. Why don't you keep that list defined publicly in your App.xaml.cs class and make sure it is populated when the application is started using Application_Launching().
public static List<Country> Countries { get; set; }
In your Country objects, define some kind of unique identifier property using something like a Guid.
Guid m_CountryID;
public Guid CountryID
{
get { return m_CountryID; }
set { m_CountryID = value; }
}
Populate it when you create your Country objects
Country.CountryID = new Guid()
In your Pin_click method, set the QueryString id parameter to your CountryID from above.
string page = "/countryPage.xaml?id=" + selectedCountry.CountryID.ToString();
Then, in the OnNavigatedTo method of your countryPage.xaml.cs pull the Guid from the id parameter of your QueryString to retrieve the given Country object.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string countryID = string.Empty;
NavigationContext.QueryString.TryGetValue("id", out countryID);
System.Diagnostics.Debug.WriteLine(string.Concat("URI: ", e.Uri.ToString(), " | CountryID: ", countryID));
if (!string.IsNullOrWhiteSpace(countryID))
{
Guid countryGuid = new Guid();
Guid.TryParse(countryID, out countryGuid);
Country country = App.Countries.Where(x => x.CountryID == countryGuid).Select(x => x).FirstOrDefault();
if (country != null)
{
// ***** BUILD YOUR PAGE AS NEEDED HERE *****
// TextBlock1.Text = country.Name;
}
}
}
Now that you have the chosen Country object, just build that page as you see fit using the information in it.
Of course, all of this presumes you are maintaining the state of your country list between application sessions. Typically you would just write the list to an XML file in your isolated storage. GeekChamp has a great list of articles to get you up and running with isolated storage.
http://www.geekchamp.com/tips/all-about-wp7-isolated-storage---read-and-save-xml-files
Although, you could do this without worrying about isolated storage. Just use fixed Guid values (not new Guid()) when creating the Country objects in your Countries list, or choose an integer type and always ensure that the same integer value is associated with each Country as you rebuild it each time the app starts.
Related
I have written a method that populates multiple rows in a listbox (but only displays the Registration number). These rows are called registrationNumber, hireCost, carMake, carModel, and carYear. As it only displays the registration number, I want to populate some textboxes when I select the registration number in the listbox.
I have made a general method of how I believe it will function, but am not sure how to implement it.
Code for inserting data
public void UpdateListBox(string registrationNumber, string hireCost, string carMake, string carModel, string carYear) {
foreach (string item in listBoxVehicles.Items)
{
if (item.Contains(registrationNumber))
{
MessageBox.Show("Registration Number: " + registrationNumber + " already exists in the list! Please try another!");
return;
}
}
listBoxVehicles.Items.Add(registrationNumber);
}
Code for selecting row
private void PopulateTextBoxes() {
if (listBoxVehicles.SelectedIndex != -1)
{
textBoxHireCost.Text = "Selected index hireCost";
textBoxMake.Text = "Selected index carMake";
textBoxModel.Text = "Selected index carModel";
textBoxYear.Text = "Selected index carYear";
}
}
Here is how it might look when I click on a registration number
as the attached image below. The fields on the right have been populated.
How might I populate the values of the selected registration number into the textboxes?
Edit
Here is the method that gets the data for the listbox from another form:
private void StoreData()
{
//Store Data
HomeForm list = (HomeForm)Application.OpenForms["HomeForm"];
list.UpdateListBox(textBoxRegistrationNumber.Text, textBoxHireCost.Text + " ", textBoxMake.Text + " ", textBoxModel.Text + " ", textBoxYear.Text);
this.Hide();
}
In modern programming, there is a tendency to separate your data (= model) from the way that the data is displayed (= view). If you separate them, you will be able to change the way you display the data without having to change your model.
Similarly you can change the model, without having to change the view. For instance, your current model fetches the data from a database, but you won't have to change anything on your view if you decide to read the data from a JSON-file.
Separation of model and view also enables unit testing your model without forms.
Separation of model and view needs something to glue them together. This "adapter" is often called the viewmodel. The abbreviation of these items is often called MVVM.
So let's separate!
Apparently your model has the notion of cars that can be hired. So we need a class:
class Car
{
public string RegistrationNumber {get; set;}
public decimal HireCost {get; set;}
public int ManufacturingYear {get; set;}
... // etc.
}
And of course we need a procedure to fetch the cars that must be displayed:
public IEnumerable<Car> FetchCars(...)
{
// TODO implement; out of scope of the question
}
Using the visual designer you have added the ListBox. This ListBox should display Cars, and for every Car it should display only the registration number.
You can do this using the visual studio designer, another method would be to do it in the constructor:
public MyForm()
{
InitializeComponent();
// From ever Car in the ListBox display the value of property RegistrationNumber
listBox1.Displayember = nameof(Car.RegistrationNumber);
}
To Display the cars:
private ICollection<Car> DisplayedCars
{
get => (ICollection<Car>)this.listBox1.DataSource;
set => this.listBox1.DataSource = value;
}
public void DisplayCars()
{
var carsToDisplay = this.FetchCars();
this.DisplayedCars = carsToDisplay.ToList();
}
And Bingo: all the registration numbers of the cars are displayed.
This display is read-only. Changes that the operator makes: Add / Remove rows, change registration numbers, are not reflected.
This might not be a problem in your current application, but if later you decide to show your cars in a DataGridView, or if you want to use this method in a ListBox that the operator can edit, you might want automatic updating of changes that the operator makes. In that case you should use a BindingList:
private BindingList<Car> DisplayedCars
{
get => (BindingList<Car>)this.listBox1.DataSource;
set => this.listBox1.DataSource = value;
}
To get the selected Car:
private Car SelectedCar => (Car)this.listBox1.SelectedItem;
Or if you allow multiselect:
private IEnumerable<Car> SelectedCars = this.listBox1.SelectedItems.Cast<Car>();
Back to your question
I want to populate some textboxes when I select the registration number in the listbox.
So if the operator selects a Registration number in the list box, you want to display several values of Car properties of the selected Car.
Using visual Studio Designer, or in the constructor:
this.listBox1.SelectedIndexChanged += OnCarSelected;
private void OnCarselected(object sender, ...)
{
Car selectedCar = this.SelectedCar;
this.DisplayCarProperties(selectedCar);
}
private void DisplayCarProperties(Car car)
{
this.textBoxHireCost.Text = car.HireCost.ToString(...);
this.textBoxYear.Text = car.ManufacturingYear.ToString();
...
}
Conclusion
By separating your data from the way that you view your data, your code is much easier to read. The methods are usually one or two lines code. Methods are highly reusable and both the model and the view are easy to change. It is possible to unit test the model without the view.
My Code
Hello. I am using xamaring forms to create an app and I am having problems obtaining a certain value from an object.
Essentially I would like to take Address1 from the object e.SelectedItem and place it into a string called address. I plan to do this to all the variables in the object, so address, city, country, postal code, etc...
From here I will use these strings to form a url link that will take the user to their native map application with the address inputed into the url.
Note: The line of text I have var item = e.SelectedItem; was added for testing purposes.
Below I have some code:
async void AddressItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem != null)
{
var item = e.SelectedItem;
}
}
In summary: I would like to take the variables stored in an object and place them into their own string element.
Thank you!
you need to cast the object to the correct type first
var item = (MyClass)e.SelectedItem;
var addr1 = item.Address1
my suggestion would be cast your item NOT as "var", cast it as your exact class type.
Class item = (Class)e.selectedItem
I call an API and get from there an id, a name and an URL for images...
I display them in a flipview and I used to convert them, saved them in a folder, convert them back and show them...
So I thought it would be much easier if I show them directly from their URL.
Now, when the user clicks (taps) on the image, it has to go to another page to show the detail of that image (and it was working fine, when saving the pic, since I named them by the id (id.png))
is there anyway I can name the ImageSource to be this id, or give it like a property (Title or Name)?
var pics = from special in GlobalVariables.Special
where special.special == "1"
select new { id = special.id, name = special.name, image = special.banner_image, featured = special.special };
foreach (var item in pics)
{
await savePicToDisk(item.image, item.name, item.id, item.featured);
}
So, then, instead of save them:
List<ImageSource> listOfImages = new List<ImageSource>();
string url = "http://52.8.2.140" + picAddress;
ImageSource urlOfPic = new BitmapImage(new Uri(url, UriKind.Absolute));
listOfImages.Add(urlOfPic);
Then, where I have to show them, I just bind it to the flipview:
flipView1.DataContext = Classes.Special.listOfImages;
The Item_Click event's arguments have a property ClickedItem (in some cases you may also need OriginalSource), which you can use to point to your item in a collection. If item's class has a property responsible for Name/Id/other you can freely pass that when navigating, other solution may be passing an index of selected item in the collection. Sample code;
private void ItemClick(object sender, ItemClickEventArgs e)
{
var item = e.ClickedItem as ItemClass;
if (item != null)
Frame.Navigate(typeof(DetailPage), item.ID);
// if your item contains a Name/Id/other you can pass that when navigating
// you can also pass index of your item in collection if you need
}
I have a class that is a list of class objects.
[Serializable]
public class UserRequestOrders
{
private List<RTORequestOrder> m_thisUsersOrders = new List<RTORequestOrder>();
public List<RTORequestOrder> RequestOrders
{
get { return m_thisUsersOrders; }
set { m_thisUsersOrders = value; }
}
}
When I create an instance of this object I need to populate the list variable When m_thisUsersOrders with an existing list of requests (from a viewstate).
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
When the page posts back, I cast the viewstate into a list of RTORequestOrder objects, and try to set the RequestOrders property, I get the message that the property or indexer cannot be assigned to. I have a "SET" accessor for the property.
All posts that I have read on this topic state that I need to modify scope in the application settings, but I am not sure how that applies here. I don't have any application settings set.
I am hoping someone can help me understand how I can populate the list from an existing list.
EDIT: Nico, Thanks for your response! Below is the full code from the code behind page. "MyOrders" and "lst" are essentially the same thing. When I am working with "lst" everything works the way that I want it to. The reason that I want to use the "MyOrders" object instead is because I have a method that returns the list as a datatable with only the fields I need to display. (I didnt' show that code because the issue appears to be with the "SET" accessor.) "lst" has the same signiture as the "MyOrders.RequestOrders". Why can I cast the the viewstate into the lst object, but not the MyOrders object?
EDIT: Grant, thanks for your response as well. I don't know how to set breakpoints for ASP pages... :(
public partial class Default3 : System.Web.UI.Page
{
RTODataEntryObjects.UserRequestOrders MyOrders = new RTODataEntryObjects.UserRequestOrders();
List<RTODataEntryObjects.RTORequestOrder> lst = new List<RTODataEntryObjects.RTORequestOrder>();
void Page_PreRender(object sender, EventArgs e)
{
if (ViewState["vsMyOrders"] == null)
{
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"]; // code to show the data in the grid when page loading
}
}
protected void Page_Load(object sender, EventArgs e)
{
NewDataGrid.EnableViewState = true;
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
public void btnSubmit(object sender, EventArgs e)
{
int id = Int32.Parse(TextBox1.Text); // dynamically getting the data from the frontend.
string name = TextBox2.Text; // dynamically getting the data from the frontend.
if (ViewState["vsMyOrders"] != null) // if the view state is already having data then can update the list and assign again to the viewstate later.
{
lst = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
}
RTODataEntryObjects.RTORequestOrder thisOrder = new RTODataEntryObjects.RTORequestOrder(id, name, User.Identity.Name, System.Environment.MachineName);
lst.Add(thisOrder); //
MyOrders.AddNew(thisOrder);
ViewState["vsMyOrders"] = MyOrders;
NewDataGrid.DataSource = (IList<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
}
I've created a ListView in a new WPF window and also a function that populates the ListView when it is called. This function just takes the URL of my web server where I've stored the data, increments the "id" and gets the data and stores it in the ListView. Therefore it populates the ListView with a certain number of items.
The problem I'm facing is that I want to add two buttons, ON & OFF, to each ListView item as it gets populated programmatically. i.e, if 16 items are added, I want 2 buttons for each item, and if it's 12 items, the similar procedure. Here's my code:
namespace user_login
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Window1 W = new Window1();
public MainWindow()
{
InitializeComponent();
}
public void populate()
{
int i;
int num = 16;
for (i = 1; i <= num; i++)
{
string val = Convert.ToString(i);
string currentUrl = "http://xpleria.com/devices.php?query=dev&id=";
string newUrlWithChangedSort = ReplaceQueryStringParam(currentUrl, "id", val);
string result = getcontent(newUrlWithChangedSort);
W.list1.Items.Add(result);
}
}
public string getcontent(string URL)
{
string content = "";
// Get HTML data
WebClient client = new WebClient();
try
{
content = client.DownloadString(URL);
}
catch (Exception)
{
System.Windows.Forms.MessageBox.Show("No Connection detected!!!");
}
return content;
}
public static string ReplaceQueryStringParam(string currentPageUrl, string paramToReplace, string newValue)
{
string urlWithoutQuery = currentPageUrl.IndexOf('?') >= 0
? currentPageUrl.Substring(0, currentPageUrl.IndexOf('?'))
: currentPageUrl;
string queryString = currentPageUrl.IndexOf('?') >= 0
? currentPageUrl.Substring(currentPageUrl.IndexOf('?'))
: null;
var queryParamList = queryString != null
? HttpUtility.ParseQueryString(queryString)
: HttpUtility.ParseQueryString(string.Empty);
if (queryParamList[paramToReplace] != null)
{
queryParamList[paramToReplace] = newValue;
}
else
{
queryParamList.Add(paramToReplace, newValue);
}
return String.Format("{0}?{1}", urlWithoutQuery, queryParamList);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
string user = textbox1.Text;
string password = textbox2.Password;
string currentUrl = "http://xpleria.com/login.php?query=login&user=wcam&pass=wireless";
string newUrlWithChangedSort = ReplaceQueryStringParam(currentUrl, "user", user);
string newUrl = newUrlWithChangedSort;
string FinalUrl = ReplaceQueryStringParam(newUrl, "pass", password);
string result= getcontent(FinalUrl);
string value = result.Substring(0, 8);
string invalid = "xpleria0";
string valid = "xpleria1";
if (value.Equals(invalid))
{
System.Windows.MessageBox.Show("The Username and/or Password you have entered is invalid, please try again");
}
else if (value.Equals(valid))
{
string sessionID = result.Substring(8, 32);
System.Windows.MessageBox.Show("HI, WELCOME CLETA");
this.Close();
using (new user_login.loading.PleaseWait(this.Location))
{
W.Show();
populate();
}
}
}
public System.Drawing.Point Location { get; set; }
}
}
I'm going to recommend you take a step back and start giving some serious consideration to organizing your code. I realize this isn't an answer to the question you asked but it is the answer to the question you should be asking.
First of all, all code relating to the retrieval of these items from the URL should be moved into a class of some kind. This class should accept the URL string as a constructor parameter and gather all the appropriate data. You should then create another class which you will use to populate with the data for each individual item and then expose this list. By the time you're done the code in your window should little more complex than:
var ItemsGetter = new ItemsGetter(URL);
foreach(var Item in ItemsGetter.Items)
{
// Populate the ListView
}
Once you're done with that I recommend you create a UserControl. User controls are extremely useful in situations where you need to represent a dynamic number of data entities each with their own set of controls which allow operations to be performed on each one. You should create a UserControl with a label and the two buttons you need. The UserControl's constructor should expect a parameter of the data type you created to represent each one of your classes. From there you can have the buttons operate on the data type as necessary.
Finally, you'll probably need a way to have the UserControl interact with the Window. Say for example one of your buttons is "Delete". You'd probably want the item to disappear from the list once the operation is complete. Don't be tempted to tie in your control with the Window by passing it as a parameter or something. Instead, read up on Action events and learn how you can create an event on the user control which you bind in the foreach loop of the Window when you're populating the list view. When the UserControl has completed the delete operation triggered by the button you can raise the UserControl's event which will prompt the Window to remove the control from the List View.
Last but not least, NAME YOUR CONTROLS.
Hopefully this helps.