So Im trying to creat a simple app like a shopping app. so I have categories and multiple items for each category, and when you get to choose an item then you will have the posibility to increase how many you need or delete the item. For exemple I chosed three items, so my cart have 3 items where each one have an Add button and a delete button. When I hit the add button the number of the items shown should increase and so on.
so what I've done so far is creating a JSON file that having all my categories, and once I hit a category I get to deserialize another JSON file that have all my items, so the items shown depends on the category I chosed of course.
Now each time i choose an item it get added to the cart and shown on the bottom page with a + and - buttons and so on.
so I created a category class to deserialize my json, and an objets class to deserialize my Item's json. I implememted the INotifyChangedProperty in the objets class so that I can keep showin whenever the number of a chosen item get increased, so basicly thats my ViewModel, but I guess that it's like that I need a ViewModel of each created item ? so I guess what I really need to use is the ObservableCollection ..
I hope I explained everything well, and waiting for your feedbacks about if Im doing it right or wrong and how should i proceed to get what I want. thank you so much
the problems is that to set the bindingcontext to my "Objets" Class I have to put the arguments in it, and then my Label well get a precised value ... what should I do ?
I do one sample about your model, you can take a look:
<ContentPage.Content>
<StackLayout>
<Label x:Name="label1" />
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
Text="change value" />
</StackLayout>
</ContentPage.Content>
public partial class Page15 : ContentPage
{
public Objets model { get; set; }
public Page15()
{
InitializeComponent();
model= new Objets("test 1", 1.001f, " test11111", 12);
this.BindingContext = model;
label1.SetBinding(Label.TextProperty, "nbr_objet");
}
private void Btn1_Clicked(object sender, EventArgs e)
{
model.nbr_objet = 20;
}
}
public class Objets : INotifyPropertyChanged
{
public string Designation { get; set; }
public float Prix { get; set; }
public string imageUrl { get; set; }
private int Nbr_Objet;
public int nbr_objet
{
get { return Nbr_Objet; }
set
{
Nbr_Objet = value;
RaisePropertyChanged("nbr_objet");
}
}
public Objets(string Designation, float Prix, string imageUrl, int Nbr_Objet)
{
this.Designation = Designation;
this.Prix = Prix;
this.imageUrl = imageUrl;
this.Nbr_Objet = Nbr_Objet;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Update:
but I guess that it's like that I need a ViewModel of each created item ? so I guess what I really need to use is the ObservableCollection ..
You said that you have three categories, and each category have many items, If you display these in ListView, category is used as Group header, and I suggest you can use the same model for different item for different categories, then add in Observablecollection, because it have implemented INotifyPropertyChanged interface.
About ListView group, you can take a look:
https://github.com/xamarin/xamarin-forms-samples/tree/master/UserInterface/ListView/Grouping
If you still have another question, I suggest you can create new thread to ask, because this thread is very long.
Please remember to mark the helpful reply as answer, thanks.
to set a binding programatically
// set the BindingContext for the page
this.BindingContext = new MyViewModel();
// Title is a public property on MyViewModel
myLabel.SetBinding(Label.TextProperty, "Title");
in order for the UI to update when the VM is changed, the VM needs to implement INotifyPropertyChanged
This is some guidance that might help with your problem. Your code is messy and I think that is causing your confusion (you have several things named very similarly).
int Nbr_Objet;
public int nbr_objet { get{...} set {...}}
this.Nbr_Objet= Nbr_Objet;
this shows me that you are setting your member variable Nbr_Objet directly, when you do that the property change notification doesn't fire - you need to assign the value through the public nbr_objet for that to happen.
I'd suggest you define the binding in XAML, and make sure you bind to the property nbr_objet, not the private member variable (field) Nbr_Objet.
If you want to avoid confusion, follow the C# coding standard and name your member variable _nbrObjet, and camel case your property name public int NbrObjet { get {....
Related
I have two textboxes with userinput, of which I need to transfer the data to my ViewModel. I tried looking around how to do this by binding it to a button (as the transfer is supposed to take place upon a buttonclick), but most advice to use bindings. However, to use bindings you have to declare properties in the ViewModel (afaik), but as these strings are used to create a new object, holding properties for them would be all but ideal because the two textboxes might expand to over 10 in the future. I've also tried messing around with CommandParameter but I only seem to be able to declare one.
So for clarification:
How do I transfer the contents of two (or more) textboxes to the corresponding ViewModel so I can create a new Object with them?
Edit:
In addition I'd also like to be able to reset the Text= field to be empty once the method handling the data has succesfully completed.
The View
<TextBox Name="UI1"/>
<TextBox Name="UI2"/>
<Button Source="*ImageSource*" Command="{Binding CallCreateObject}"/>
and the ModelView
private void OnCallCreateObject()
{
Object newObject = new Object(UI1, UI2, false)
}
This is a general example of what I'm trying to achieve
If you want to insert data from UI to ViewModel on Button Click than there is no reason to use binding. Binding is mainly used to sync data between UI and underlying models.
Still if you want only that then on button_click event you can do something like this.
private void button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
model.Property1 = textBox1.Text;
model.Property2 = textBox2.Text;
textBox1.Text = string.Empty;
textBox2.Text = string.Empty;
}
That will solve your issue. But this approach is not recommended when you have a better thing that is called 'Binding'
If you want to bind your view with a viewmodel then try this:
Your view model:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Great, you have set up your view model. Now the view:
XML PersonView.xml:
<Grid Name="MyContainer">
<TextBox Text="{Binding PersonName}" />
<Button Name="SaveInfoButton" OnClick="SaveInfoButton_Click">Save info</Button>
</Grid>
Now that we have indicated with which property the textbox will be bind, lets indicate to the view the model that will use to update the property named PersonName. The idea is that when you click over the button, the property PersonName of our model Person gets updated with the value of the TextBox.
The xml class:
public partial class PersonView : UserControl
{
private readonly Person Model;
public PersonView()
{
//Components initialization, etc. etc...
this.Model = new Person();
this.DataContext = this.Model; // Here we are binding the model with our view.
}
private void SaveInfoButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.Model.PersonName); // this will print the value of your textbox.
}
}
Dont know if you noticed, but we didnt have the need of creating a new object when the user click the button. We just use our model and update the model properpies. If you add more textbox to your view, you'll have to added to our viewmodel as well as the given example.
Here is some post that can help you a little bit more(dont have enough time)
http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html
http://www.tutorialspoint.com/wpf/wpf_data_binding.htm
You could use bindings like this:
<TextBox Name="UI1" Text="{Binding Path=Ut1Value}"/>
<TextBox Name="UI2" Text="{Binding Path=Ut2Value}"/>
<Button Source="*ImageSource*" Command="{Binding CreateTheThingCommand}"/>
Then in your viewmodel you'll need to have the properties and command for those:
private string _ut1Value;
private string _ut2Value;
public string Ut1Value
{
get
{
return _ut1Value;
}
set
{
if (_ut1Value!= value)
{
_ut1Value= value;
OnPropertyChanged("Ut1Value");
}
}
}
public string Ut2Value
{
get
{
return _ut2Value;
}
set
{
if (_ut2Value!= value)
{
_ut2Value= value;
OnPropertyChanged("Ut2Value");
}
}
}
public ICommand CreateTheThingCommand
{
get { return new RelayCommand(CreateTheThing); }
}
private void CreateTheThing()
{
Object newObject = new Object(_ut1Value, _ut2Value, false);
// Do whatever with your new object
}
It sounds as if you need at least two ViewModel objects:
One to present the data from an existing object. This would be, essentially, what you have already.
A container ViewModel. This encapsulates the behaviours of the IEnumerable collection of objects, including the functionality required to Add a new object.
The container ViewModel would have the properties that you are struggling with, plus the CreateObject command, along with an IEnumerable (ObservableCollection) property to hold the existing ViewModel objects.
In your View, you would have one control to present the data in an existing ViewModel object, and a second control with a ListView (or similar) control to display the existing view controls and the set of TextBox controls, plus the button to create a new object (and add it to the list).
This would also allow you to add 'remove', 'sort', etc. functionality to the container ViewModel, without having to change the existing ViewModel.
A way to accomplish a scalable solution with minimal lines of code, would be to create hold a list of items you bind to in the view model.
This way you can use an ItemsControl in the UI to display a textbox for each item:
public class ViewModel
{
public List<Item> Items {get;} = new List<Item>
{
new Item { Value = "UI1" },
new Item { Value = "UI2" },
};
public class Item
{
public string Value {get;set;}
}
}
View:
<ItemsControl ItemsSource="{Binding Test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Commit" Margin="5" Click="ButtonBase_OnClick"/>
You can then create the object either from a click event or command:
private void OnCallCreateObject()
{
Object newObject = new Object(Items[0], Items[1], false);
}
The downside is that the order of the items is not explicit, so either you need to assume that the indexed order is correct, or order them manually.
Edit:
Turns out I was merging a list with the latest data from a rest API with the data in my GUI. All I really had to do was clear and refill my observablecollection. This post is basically an xy problem. I did not have the vocabulary to explain the problem I was facing.
I'm building an app with a Data class where I store all my data. In this class I have a List filled with objects. I have a Page with a ObservableCollection and a ListView. Currently when I update the ObservableCollection, I clear it and refill it with all the data from Data-class's List. When I do this, my ListView flickers. My guess is that completely rebuilding the ObservableCollection causes this, in combination with a custom ViewCell that is not the lightests. How could I go about updating only what I want? The list/o.collection can have diffrent sizes. The list/o.collection both store the same object.
What I tried:
List<csharp.Truck> favs = data.getFavoriteTrucks();
trucks.Clear();
foreach (csharp.Truck truck in favs)
{
trucks.Add(truck);
}
}
Works but makes my ListView flicker.
Trying this now, its pretty bad code I think, it does update the list how I want it to but the listview does not get updated for some reason. Maybe I need to trigger a refresh?
List<csharp.Truck> all = data.getTrucks();
//if list sizes are not equal(excess/missing objects)
if (all.Count != trucks.Count)
{
//excess object
if (all.Count < trucks.Count)
{
trucks.Clear();
foreach(csharp.Truck t in all)
{
trucks.Add(t);
}
}
//object missing
if(all.Count > trucks.Count)
{
foreach(csharp.Truck t in all)
{
if (!trucks.Contains(t))
{
trucks.Add(t);
}
}
}
}//objects are now the same
//test if object is present but outdated(favorite property)
foreach(csharp.Truck t in trucks)
{
if (t.isFavorite() != all[all.IndexOf(t)].isFavorite())
{
t.setFavorite(all[all.IndexOf(t)].isFavorite());
}
}
Also let me know if this approach is not good practise in the first place.
If you want to update only some properties, you could implement in your model class INotifyPropertyChanged interface.
public class Model : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
RaisePropertyChanged();
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
Volatile.Read(ref PropertyChanged)?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And then if this property is binded to view, view will know, when you change it:
<Label Text="{Binding MyProperty}" />
I'm a total newbie, just learning the basics of DataContext and the MVVM model. I've now got a grid bound to a view model object which implements INotifyPropertyChanged, however it appears that UpdateSourceTrigger (which all the WPF tutorials tell me to use) is not available for WinRT / Metro Style apps!
How do I implement INotifyPropertyChanged then?
I'm at the end of my tether here. I've spend nearly the whole day on the most basic of app examples, simply trying to get a grid to update after I click something. The only way I've managed to do this so far is to create an entirely new instance of the view model and reassign the DataContext which I know is wrong
UPDATE:
I have made some progress, but things have gotten very weird. I have a view model, with a generic list of items. The items list is wired up with a PropertyChangedEventHandler. If I replace the entire collection with a new one, the listview updates.
model.Items = new List<DataItem>{ new DataItem{ Title = "new item" }};
This results in a one item list with the above item. However, if I try adding an item, nothing happens
model.Items.Add(new DataItem{ Title = "added item" });
I also tried creating a method which added an item and specifically fired PropertyChanged, but that also doesn't work
Here's where it gets weird. Next I tried this code.
model.Items.Add(new DataItem { Title = "added item" });
model.Items = new List<DataItem> { new DataItem { Title = "new item" }};
This results in a two item list:
- new item
- added item
How can this be? The code says, "add one item" then "replace the whole list" but it executes in the reverse order?
UPDATE 2:
I've switched to ObservableCollection as suggested, which has actually solved the original problem. I can now add an item and it shows up on the list.
However, the new weird behaviour is still in effect. Items added before the collection is reset are appended to the end of the new collection. Why is my code executing in reverse order?
You need to implement the interface and send out the notification once the given property you care about changes.
public event PropertyChangedEventHandler PropertyChanged;
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CustomerName"));
}
}
}
}
Keep in mind that for a collection, you should use an ObservableCollection as it will take care of the INotifyCollectionChanged being fired when an item is added or removed.
I would suggest to scale your sample back as far as possible. Don't start with a DataGrid but rather a simple TextBoxand Button, where the Button forces a change in your ViewModel which will then reflect on the UI.
Code taken from here.
It's best to implement a parent class which implements it like this:
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And then in your subclass (i.e. ViewModel) in your property do something like this:
public class MyViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name {
get{ return _name; }
set{
_name = value;
RaisePropertyChanged("Name");
}
}
}
I need to implement next UI on WPF with next logic:
I have a ViewModel:
public class VersionVulnerListViewModel : INotifyPropertyChanged
{
private ObservableCollection<Vulner> _vulners;
public ObservableCollection<Vulner> Vulners
{
get
{
return _vulners;
}
private set
{
_vulners = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Vulners"));
}
}
}
Where Vulner have next implementation:
public class Vulner : PocoEntity
{
public Vulner()
{
}
// ... other properties
public IList<VulnerData> Data { get; set; }
}
and VulnerData have some properties.
I need next: dependency of selected item of some combobox i need to bind diferent Data items from my list into GridView. Its will be good if its possible to do this just with XAML edetings, without adding code-behiend logic. Or add code-behiend logic, but according MVVM pattern.
Example:
I know it's an old question, but it's not answered yet, so I'll give it a try (in hope it will help somebody)
You can bind the SelectedValue of the Combobox to a variable. In the set-Block of the variable, you can also change the content of your ObservableCollection. VoilĂ , the GridView changes depending on the ComboBox.
I have a list of custom objects which I have added to a ListBox control in my WinForms C# 4.0 application.
When the user selects a particular element in the ListBox, the properties of that object come up in the window next to the ListBox in various input fields. The user can change these and click 'Save' which will modify the data members of the objects to correspond with the changes the user has made.
The function does work. The values are saved to the object, and when the user selects the element again, their changes are confirmed to be saved correctly.
What isn't working is the update of the text in the ListBox. For example if we have a list of staff in the ListBox, and we can see "John Smith" there, we can click his name - edit his name to "John Smithe" and click OK. The ListBox still shows "John Smith", however if we click on his name, then in the TextBoxes on the right we can see that his name has correctly been changed to "John Smithe".
I have tried calling the Refresh() method on the ListBox but this didn't work.
I can fix it by removing the item from the ListBox and adding it again. This works, and it's not really an issue because the items are stored in separate lists anyway so I have no risk of losing any of my staff.
But is this really the best way to do it? Is there a more elegant way to update the text in the ListBox without removing/adding the item again?
Do the objects in the ListBox implement INotifyPropertyChanged?
Update:
It seems that you can solve the problem with a couple of steps:
Set the DisplayMember property of the ListBox to a property on your objects that provides whatever it is you want to appear in the list. I will assume this property is named DisplayText for this answer.
Have the objects implement INotifyPropertyChanged.
In the setters of all the properties that influence the value of DisplayText, raise the NotifyPropertyChanged event with DisplayText for the property name.
You should then be good to go.
Following the tutorial I reference above I made a quick and dirty example of using a BindingList. Hopefully it's helpful to you.
public partial class Listbox_Databinding : Form
{
BindingList<Person> People = new System.ComponentModel.BindingList<Person>();
public Listbox_Databinding()
{
InitializeComponent();
People.Add(new Person("John", "Smith"));
People.Add(new Person("John", "Jacob"));
lstSelectPerson.DataSource = People;
}
private void lstSelectPerson_SelectedIndexChanged(object sender, EventArgs e)
{
txtLast.Text = ((Person)lstSelectPerson.SelectedItem).Last;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
((Person)lstSelectPerson.SelectedItem).Last = txtLast.Text;
}
}
public class Person : INotifyPropertyChanged
{
public Person(string first, string last)
{
First = first;
Last = last;
}
public override string ToString()
{
return Last + ", " + First;
}
string p_first;
string p_last;
public string First
{
get { return p_first; }
set
{
p_first = value;
OnDisplayPropertyChanged();
}
}
public string Last
{
get { return p_last; }
set
{
p_last = value;
OnDisplayPropertyChanged();
}
}
void OnDisplayPropertyChanged()
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("DisplayName"));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}