Binding data MVVM after Tap on ListBox - c#

This is my situation:
- I have a WebApi that sends me a json data.
- My app reads this data and binds all information in a list box
- When I tap on Item of the list box I want to show all information about that item
The problem is: How can I bind data on the new view?
This is the code after tap (MainViewModel):
this.ProfessorDetail = new RelayCommand(() =>
{
if (SelectedIndexProfessors != -1)
{
//This variable contain all detail information
Professor x = Professors.ElementAt(SelectedIndexProfessors);
//Open new page
App.RootFrame.Navigate(new Uri("/Pages/ProfessorDetailPage.xaml", UriKind.RelativeOrAbsolute));
}
});

You can use Uri parameter to pass simple string information between pages. For example, in your RelayCommand pass unique information about selected professor :
.........
//pass selected professor Id to ProfessorDetailPage
App.RootFrame.Navigate(new Uri("/Pages/ProfessorDetailPage.xaml?professorId=" + x.ProfessorId, UriKind.RelativeOrAbsolute));
.........
Then in the ProfessorDetailPage's Loaded or NavigatedTo event handler get the uri parameter and display information accordingly :
.........
string professorId;
if(NavigationContext.QueryString.TryGetValue("professorId", out professorId))
{
//load information based on professorId parameter value
}
.........

I never made a page based application, but I was looking at this link and from basic knowledge of WPF, I do know you need to set the DataContext of that page just like you asked.
Take a look at the following code in the link:
this.Frame.Navigate(typeof(BasicPage2), tb1.Text);
That is sending the text entered in tb1 to the object you're navigating to. Then look at how the receiving object is utilizing that information:
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
string name = e.NavigationParameter as string;
....
}
You're going to want to follow the same idea, except you're going to want to do something like:
App.RootFrame.Navigate(new Uri(".....", ...), x);
And then in the page itself, you're going to want to set up a LoadState event in ProfessorDetailPage control and inside it do:
Professor prof = x as Professor;
if( prof != null)
{
this.DataContext = prof;
}
That should set the DataContext of ProfessorDetailPage and your data should be populated.
Let me know how it works out, hope this helps!

Related

Observable collection removing item only once, when tried again doesnt work - Windows phone 8 - C#

I am writing my first application for WP8 platform in C#. I implemented three datatypes namely locationModel which has locationGroups. Each locationGroup has a ObservableCollection of type locationData.
locationData has two double types for latitude and longitude and a title string.
I used a textblock inside a stackpanel to show the locationData element's title, where the lat long are hidden to user.
There is a context menu on each of this textblock element which enable the user to delete the respective locationData.
When I open the app and delete any item, it succesfully does and updates the view too. But when I do it for another item it just doesnt work. I cannot delete more than one items for each time I open the app.
I used breakpoints to see where the problem is. the selected locationData is succesfully passed to the App.ViewModel.LocationModel.Items.Remove(). But it just that they are not deleted from the observable collection. I even tried to see the index of the locationData in observable collection and use RemoveAt method. Even it doesnt work.
I did a lot of research to find the problem, but no one seems to face the same problem as me. I referred to msdn article on how to implement inotifypropertychanged to update the collection. But its too complex for me to understand that.
I dont really understand why the observable collection delete the item for the second time even though if I pass the index of that item. And my usage of breakpoints showed me that the data is not even null.
So kindly tell me what is causing this problem and how do I overcome it so that I can implement my own workaround and not face this issue again. I can show you the code if you want me to.
Thanks.
CODE:
Adding an item
private void SaveLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Add(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch(IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
Deleting item:
private void DeleteLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Remove(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch (IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
One more thing that I want to say is, whenever I add a locationData to the collection, it updates automatically. Because adding is done on another page and when the mainpage.xaml loads(in which the observable collection data is), it updates automatically because of the code in OnNavigatedTo method.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
And LoadData method is :
public void LoadData()
{
Custom = LoadCustomLocations();
IsDataLoaded = true;
}
private LocationGroup LoadCustomLocations()
{
string dataFromAppSettings;
LocationGroup data;
if (IsolatedStorageSettings.ApplicationSettings.TryGetValue(CustomKey, out dataFromAppSettings))
{
data = JsonConvert.DeserializeObject<LocationGroup>(dataFromAppSettings);
}
else
{
data = new LocationGroup();
}
return data;
}
So, can anyone help ?
In the case you described on your comment I think you set the DataContext to your Items. When you create a new Items-List the DataContext will be lost. So you have to reset the DataContext to the new loaded Items-List

C# Dynamic form (reflection) - linking controls

Sorry for the poor quality of the title. I couldn't think of a better way to phrase this.
For a project I'm currently working on with a few friends, I got myself in the situation where I have created a dynamic form (with reflection) which I now want to validate.
Example (ignore the black box, it contains old form elements which are now irrelevant and i didn't want to confuse you guys):
As you may have guessed already, it is an application for creating a mysql database.
Which is where I get to my problem(s). I want to disable checkboxes if others are checked.
For example: If I check "PrimaryKey" I want to disable the checkbox "Null".
Changing from unsigned to signed changes the numericupdown minimum and maximum etc.
But with reflection and all, I find it difficult to know exactly which checkbox to disable.
I was hoping you guys would have some suggestions.
I have been thinking about this for a while and a few thoughts have come to mind. Maybe these are better solutions than the current one.
Thought 1: I create UserControls for every datatype. Pro's: no problems with reflection and easy identifying of every control in the UserControl for validation. Con's: Copy-Pasting, Lots of UserControls, with a lot of the same controls.
Thought 2: Doing something with the description tags for every property of the classes. Creating rules in the description that allow me to link the checkboxes together. Here I'll only have to copy the rules to every class property and then it should be ok.
I had been thinking of other solutions but I failed to remember them.
I hope you guys can give me a few good pointers/suggestions.
[Edit]
Maybe my code can explain a bit more.
My code:
PropertyInfo[] properties = DataTypes.DataTypes.GetTypeFromString(modelElement.DataType.ToString()).GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name != "Label" && prop.Name != "Project" && prop.Name != "Panel")
{
var value = prop.GetValue(modelElement.DataType, null);
if (value != null)
{
tableLayoutPanel1.Controls.Add(new Label { Text = prop.Name, Anchor = AnchorStyles.Left, AutoSize = true });
switch (value.GetType().ToString())
{
case "System.Int32":
NumericUpDown numericUpDown = new NumericUpDown();
numericUpDown.Text = value.ToString();
numericUpDown.Dock = DockStyle.None;
tableLayoutPanel1.Controls.Add(numericUpDown);
break;
case "System.Boolean":
CheckBox checkBox = new CheckBox();
checkBox.Dock = DockStyle.None;
// checkbox will become huge if not for these changes
checkBox.AutoSize = false;
checkBox.Size = new Size(16, 16);
if (value.Equals(true))
{
checkBox.CheckState = CheckState.Checked;
}
tableLayoutPanel1.Controls.Add(checkBox);
break;
default:
MessageBox.Show(#"The following type has not been implemented yet: " + value.GetType());
break;
}
}
}
}
Here is a mockup from my comments:
// The ViewModel is responsible for handling the actual visual layout of the form.
public class ViewModel {
// Fire this when your ViewModel changes
public event EventHandler WindowUpdated;
public Boolean IsIsNullCheckBoxVisible { get; private set; }
// This method would contain the actual logic for handling window changes.
public void CalculateFormLayout() {
Boolean someLogic = true;
// If the logic is true, set the isNullCheckbox to true
if (someLogic) {
IsIsNullCheckBoxVisible = true;
}
// Inform the UI to update
UpdateVisual();
}
// This fires the 'WindowUpdated' event.
public void UpdateVisual() {
if (WindowUpdated != null) {
WindowUpdated(this, new EventArgs());
}
}
}
public class TheUI : Form {
// Attach to the viewModel;
ViewModel myViewModel = new ViewModel();
CheckBox isNullCheckBox = new CheckBox();
public TheUI() {
this.myViewModel.WindowUpdated += myViewModel_WindowUpdated;
}
void myViewModel_WindowUpdated(object sender, EventArgs e) {
// Update the view here.
// Notie that all we do in the UI is to update the visual based on the
// results from the ViewModel;
this.isNullCheckBox.Visible = myViewModel.IsIsNullCheckBoxVisible;
}
}
The basic idea here is that you ensure that the UI does as little as possible. It's role should just be to update. Update what? That's for the ViewModel class to decide. We perform all of the updating logic in the ViewModel class, and then when the updating computations are done, we call the UpdateVisual() event, which tells the UI that it needs to represent itself. When the WindowUpdated Event occurs, the UI just responds by displaying the configuration set up by the ViewModel.
This may seem like a lot of work to set up initially, but once in place it will save you tons and tons of time down the road. Let me know if you have any questions.
Try relating the event of one checkbox to disable the other; something like this:
private void primaryKeyBox_AfterCheck(object sender, EventArgs e)
{
nullBox.Enabled = false;
}
This is a very simple example and would have to be changed a bit, but for what I think you're asking it should work. You would also have to add to an event for the boxes being unchecked. You would also need logic to only get data from certain checkboxes based on the ones that are and are not checked.
For all the other things, such as changing the numbers based on the dropdown, change them based on events as well.
For WinForms I would use data binding.
Create an object and implement INotifyPropertyChanged and work with that object.
Then, If you have an object instance aObj:
To bind the last name property to a textbox on the form do this:
Private WithEvents txtLastNameBinding As Binding
txtLastNameBinding = New Binding("Text", aObj, "LastName", True, DataSourceUpdateMode.OnValidation, "")
txtLastName.DataBindings.Add(txtLastNameBinding)
Take a look here for more info.
INotifyPropertyChanged

How to update listview automatically after adding a new item into DB

I'm using C# WPF MVVM. So in XAML there is a listview, which is binded to an object, used to show different information from sql database depending on tab.
For example. I have two forms: one is that shows information and another that is used to input information. How can I automatically update the listview in one form, after new information was entered in another form? Because now I have to switch tabs to get the listview updated.
binding direction for this element should be exposed to TwoWay (Mode=TwoWay)
like this:
x:Name="list"
ItemsSource="{Binding ....... , Path=........., Mode=TwoWay}}" ......
Apart from the default binding, which is one way, you can also configure binding to be two way, one way to source, and so forth. This is done by specifying the Mode property.
OneWay: Causes changes to the source property to automatically update the target property but the source does not get changed
TwoWay: Changes in the source or target automatically cause updates to the other
OneWayToSource: Causes changes to the target property to automatically update the source property but the target does not get changed
OneTime: Causes only the first time change to the source property to automatically update the target property but the source does not get changed and subsequent changes do not affect the target property
you can look this : http://msdn.microsoft.com/en-us/library/ms752347.aspx
After you enter new information into a form, try to invoke your own method, which will update your information into a list view.
So you can use some event eg. DataContentChanged or your update method can be called when u click the button which adds new data into your form.
Example of refresh method should look like this:
public void lbRefresh()
{
//create itemsList for listbox
ArrayList itemsList = new ArrayList();
//count how many information you wana to add
//here I count how many columns I have in dataGrid1
int count = dataGrid1.Columns.Count;
//for cycle to add my strings of columns headers into an itemsList
for (int i = 0; i < count; i++)
{
itemsList.Add(dataGrid1.Columns[i].Header.ToString());
}
//simply refresh my itemsList into my listBox1
listBox1.ItemsSource = itemsList;
}
EDIT: To finish and solve your problem, try to use this snippet of code:
//some btn_Click Event in one window
//(lets say, its your callback " to update" button in datagrid)
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//here you doing somethin
//after your datagrid got updated, try to store the object,
//which u want to send into your eg. listbox
data[0] = data; //my stored data in array
//for better understanding, this method "Button_Click_1" is called from Window1.xaml.cs
//and I want to pass information into my another window Graph1.xaml.cs
//create "newWindow" object onto your another window and send "data" by constuctor
var newWindow = new Graph1(data); //line *
//you can call this if u want to show that window after changes applied
newWindow.Show();
}
After that your Graph1.xaml.cs should look like this:
public partial class Graph1 : Window
{//this method takes over your data u sent by line * into previous method explained
public Graph1(int[]data)
{
InitializeComponent();
//now you can direcly use your "data" or can call another method and pass your data into it
ownListBoxUpdateMethod(data);
}
private void ownListBoxUpdateMethod(int[] data)
{
//update your listbox here and its done ;-)
}

ListBox SelectionChanged WP7 to navigate with parameters

So, in the Windows Phone 7 app I'm making, I use a ListBox with a SelectionChanged event handler to navigate a user to a new webpage, showing additional information. The MainPage.xaml shows a ListBox populated with information from a JSON file, which works correctly. However, if a user wants to read more about the news, he/she will have to click on the news in the ListBox, which fires the SelectionChanged event, which looks like this:
private void NewsList_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
int index = NewsList.SelectedIndex;
fetchNewsContent newsContentGetSet = new fetchNewsContent();
newsContentGetSet.newsID = newslistJson.ElementAt(index).news_id;
newsContentGetSet.newsTitle = newslistJson.ElementAt(index).news_title;
newsContentGetSet.newsAbstract = newslistJson.ElementAt(index).news_abstract;
newsContentGetSet.newsContent = newslistJson.ElementAt(index).news_content;
newsContentGetSet.newsAuthor = newslistJson.ElementAt(index).news_author;
newsContentGetSet.newsDatePublished = newslistJson.ElementAt(index).news_date_published_no;
//object[] someobject = { newsContentGetSet.newsID, newsContentGetSet.newsTitle, newsContentGetSet.newsAbstract, newsContentGetSet.newsContent, newsContentGetSet.newsAuthor, newsContentGetSet.newsDatePublished };
NavigationService.Navigate(new Uri("/NewsPage.xaml?obj=" + index, UriKind.Relative));
}
This simply uses a class (newsContentGetSet.cs) with getters and setters for each of the strings (newsID, newsTitle, etc.), but when the SelectionChanged is fired, it the .cs file doesn't set the newly given newslistJson values! Why?
I also tried sending only text parameters in the NavigationService, but the newsContent string was too long (whole news story), so it returned an "shell page uri too long" error.
Right now, this sends simply the index int to the NewsPage page, which tries to capture the values, but fails since the newsContentGetSet doesn't actually set anything (doesn't debug into it when I try). Aaany ideas, really?
Instead of passing the data on parameter. You should save the data to variable into App class and then retrieve them from there when you have navigated to next page.
App.xaml.cs
public static fetchNewsContent newsContentGetSet;
Accessing it
var fetchedNewsContent = App.fetchNewsContent;
You can store/retrieve the data from any page. Note that if the application is closed the data is gone.

How to access query string value in viewmodel?

I want to pass list of checked Id (checkedParcels) to another page to display list of details accordingly.
For this in first ViewModel I have implemented command on which execution I am able to navigate to another page. Here is that code:
Uri uri = new Uri("/UnitListForParcelPage?checkedParcel=" + checkedParcels,UriKind.Relative);
navigationService.NavigateTo(uri);
I am able to navigate to second page here is address as it shown in browser:
http://example.com/PropMgmtTestPage.aspx#/UnitListForParcelPage?checkedParcel=System.Linq.Enumerable+WhereEnumerableIterator%601%5BPropMgmt.ParcelServiceRef.Parcel%5D
My problem is I am using ViewModel to perform operation on this page but I am unable to find any method to access value passed through query string.
Update:
On page code-behind I have added this code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string PSTR = NavigationContext.QueryString["checkedParcel"];
MessageBox.Show(PSTR);
}
MessageBox is showing correct value now I just want to bind that to viewmodel property
I have used this approch to bind viewmodel to view:
<navigation:Page.Resources>
<helpers:SimpleViewModelLocator ViewModelKey="UnitTransFormViewModel" x:Key="viewModelLocator"/>
</navigation:Page.Resources>
<navigation:Page.DataContext>
<Binding Source="{StaticResource viewModelLocator}" Path="ViewModel"/>
</navigation:Page.DataContext>
Another way Make a public Property and Assign it with the Value of checkedParcel and now you can use it in the ViewModel Cheers :)
UPDATE:
Just make a
public static string checkedParcel = string.Empty;
in App.Xaml.cs
and When you call
App.checkedParcel = checkedParcels;
this.navigationService.NavigateTo(uri);
before that you need to give the Value to App.checkedParcel =checkedParcels;
and in your Navigated page mathod
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string PSTR = App.checkedParcel;
MessageBox.Show(PSTR);
}
Hope You Understand. You can Achieve this by Making a Property and Setting Accordingly.
You could use a broadcast service (like Messenger if you're using MVVM Light) to send the 'change view' notification, along with your parameter.
Then you'd have easily the View react (Navigate) and the ViewModel get its parameters.

Categories