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.
Related
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 {....
I experiment with the order of setting the DataContext property in the default constructor in WPF.
<StackPanel>
<ListBox ItemsSource="{Binding MyItems, PresentationTraceSources.TraceLevel=High}"></ListBox>
<TextBlock Text="{Binding SomeText}"></TextBlock>
<TextBlock Text="{Binding SomeNum}"></TextBlock>
<TextBlock Text="{Binding Path=Person.Name}"></TextBlock>
<ListBox ItemsSource="{Binding Path=PersonList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
1) With DataContext set before the InitializeComponent method
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string someText = "Default text";
public List<string> MyItems { get; set; }
public List<Person> PersonList { get; set; }
public Person Person { get; set; }
public int SomeNum { get; set; }
public string SomeText
{
get
{
return someText;
}
set
{
someText = value;
OnPropertyChanged("SomeText");
}
}
public MainWindow()
{
this.DataContext = this;
MyItems = new List<string>();
PersonList = new List<Person>();
Person = new Person();
InitializeComponent();
/*These changes are not reflected in the UI*/
SomeNum = 7;
Person.Name = "Andy";
/*Changes reflected with a help of INotifyPropertyChanged*/
SomeText = "Modified Text";
/* Changes to the Lists are reflected in the UI */
MyItems.Add("Red");
MyItems.Add("Blue");
MyItems.Add("Green");
MyItems[0] = "Golden";
PersonList.Add(new Person() { Name = "Xavier" });
PersonList.Add(new Person() { Name = "Scott" });
PersonList[0].Name = "Jean";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class Person
{
public string Name { get; set; } = "Default Name";
}
After the call to the InitializeComponent method changes to the values of properties are not reflected in the UI except for those properties which use INotifyPropertyChanged. Everything is clear so far.
However I noticed that changes to the list items are also reflected in the UI. How come?
I always thought that in order to reflect adding/removing from the collection I need ObservableCollection and to implement INotifyPropertyChanged on list object to detect modifications of these objects. What is the meaning of this?
2) With DataContext set after the InitializeComponent method
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM? Could you describe it more thoroughly or give a simple code example?
I always thought that in order to reflect adding/removing from the collection I need ObservableCollection<T> and to implement INotifyPropertyChanged on list object to detect modifications of these objects.
You do, if you want reliable updating of the UI during changes in the view model.
What is the meaning of this?
The "meaning" is that in your particular scenario, you are making assumptions that aren't valid. WPF components go through a variety of initialization steps, only some of which occur as part of the InitializeComponent() method.
If, for example, you were to move the code for your value updates into a handler for the Loaded event, you'd find some of the updates reflected in the UI, but not all.
If you move that same code into a method invoked via Dispatcher.InvokeAsync() using a priority of DispatcherPriority.SystemIdle, you'd find that none of the updates would be observed, except for the one backed by INotifyPropertyChanged. In that case, you're explicitly waiting until every aspect of initialization has completed, and there are no longer opportunities for the initialization code to observe your updated values.
It's all about timing. Any code that sets a value before the UI winds up observing it, can do so successfully without INotifyPropertyChanged or equivalent. But you're entirely at the mercy of the current implementation of the framework in that case. Different parts of the initialization happen at different times, and these are not all documented, so you're relying on undocumented behavior. It probably won't change, but you have no way to know for sure.
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM?
It's not. Don't believe everything you read, even (or especially!) on the Internet.
If you want to forego implementation of INotifyPropertyChanged, then it will be important that you initialize all of your view model data before assigning the DataContext. But, even if you assign the DataContext after calling InitializeComponent, that assignment will be observed (because DataContext is a dependency property and so provides property changed notification to the framework), and the UI will retrieve all of the bound data from your view model data.
What's important is that the view model data be initialized before the assignment of DataContext. Where that happens relative to InitializeComponent() is not important.
When a view model property does not fire the PropertyChanged event, its value must of course be set before assigning the view model instance to the view's DataContext.
It does however not matter if you assign the DataContext before or after calling InitializeComponent:
Given a Binding like
<TextBlock Text="{Binding SomeText}"/>
these two sequence will both result in showing the property value in the view:
DataContext = new { SomeText = "Hello, World." };
InitializeComponent();
and
InitializeComponent();
DataContext = new { SomeText = "Hello, World." };
In my WPF application, I have some properties which I have bound to the XAML counterpart, but for some reason do not get set whenever their values change. I have implemented the INotifyPropertyChanged interface as well as set my DataContext for this View too, and it is still not picking up any changes.
I have this same pattern for other properties within this ViewModel which do work, while others don't.
Here is a snippet of my current code:
ViewModel
public class TestViewModel : INotifyPropertyChanged
{
private string testString;
public TestViewModel()
{
.....
this.RunCommand = new RelayCommand(this.RunAction);
}
public string TestString
{
get
{
return this.testString;
}
set
{
this.testString = value;
this.OnPropertyChanged("TestString");
}
}
private void RunAction()
{
.....
this.testString = "Running.";
}
}
View
<StatusBarItem>
<TextBlock Text="{Binding Path=TestString, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</StatusBarItem>
DataContext (set in code-behind of another MainWindow class)
var testViewModel = SimpleIoc.Default.GetInstance<TestViewModel>();
var testWindow = new TestWindow() { DataContext = testViewModel };
testingWindow.Show();
If it helps, this is part of a multi-windowed application which uses MVVM-Light to pass properties between classes.
You are not changing the value of the TestString, you are assigning a command to change the value but you do not seem to be executing it.
this.RunCommand = new RelayCommand(this.RunAction);
Bind that command to something or execute it manually from somewhere.
Also you need to assign the property not the field
this.TestString = "Running.";
I found the problem. You are only updating the private property testString. But you do not update the property TestString so the notify is never called.
Try this:
this.TestString = "Running";
I have a button, When it's clicked it populates my Datagrid. The code is written within the .xaml.cs file, which I believe breaks the MVVM rule but it's just a temporary situation. I know it's not ideal for MVVM.
Calculate.xaml.cs
public void PopulateGrid(object sender, RoutedEventArgs e)
{
BindableCollection<Payments> PaymentCollection = new BindableCollection<Payments>
....
Datagrid.ItemsSource = PaymentCollection
....
}
My question is if there's a way to read the Datagrids ItemsSource From the ViewModel.
What I've Tried
LoansViewModel
public BindableCollection<Payments> paymentCollection {get; set;}
Calculate.xaml
<telerik:RadGridView ItemsSource="{Binding paymentCollection, Mode=TwoWay}" ... />
The collection paymentCollection Doesn't Update after calculate is clicked.
Just do this the correct MVVM way. Get rid of your PopulateGrid method in the .xaml.cs file and eliminate setting the Click property in your xaml. Instead bind the command property of the button to an ICommand property in your ViewModel the same way you are binding the ItemsSource of the RadGridView. You will need an implementation of ICommand to use and MVVM Lights RelayCommand is one option for that.
Here is the code for the ICommand:
private ICommand _populateGridCommand;
public ICommand PopulateGridCommand
{
get
{
if (_populateGridCommand == null)
{
_populateGridCommand = new RelayCommand(() => PopulateGrid());
}
return _populateGridCommand;
}
}
public void PopulateGrid()
{
PaymentCollection.Clear();
//load data and then add to the collection
}
UPDATE
To do this in code behind, you'll need to access the ViewModel and work on the collection from it. I don't like this but it should work.
public void PopulateGrid(object sender, RoutedEventArgs e)
{
var loansVM = DataGrid.DataContext as LoansViewModel;
loansVM.paymentsCollection.Clear();
var newData = //load data
foreach (var data in newData)
loansVM.paymentsCollection.Add(data);
}
Your xaml code looks like it should work provided the DataContext of your grid is set to your ViewModel instance where your paymentCollection property is declared.
Once your binding is set, it calls the get on the paymentCollection property. If your collection property object is not reassigned any further, and you add and remove elements from it, and it notifies on those changes via INotifyCollectionChanged, it will work. This is how ObservableCollection works and used most commonly for such scenarios.
However, if when you calculate, you re-assign your paymentCollection property with a new instance, your grid will not update, because you now have an entirely different collection. In that case you will need to notify the view that the paymentCollection property itself has changed. In which case you should implement it as a notification property:
private BindableCollection<Payments>_paymentCollection;
public BindableCollection<Payments> paymentCollection {
get { return _paymentCollection; }
set {
_paymentCollection = value;
OnPropertyChanged("paymentCollection");
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
So I have three things:
A ListBox in a Window
A DataBase class granting me access to an ObservableList
Contact implements INotifyChanged
In my main Window, I have three Buttons (One for new List Entry, one for editing, one for deleting an item)
I fill the list like this:
lbKontakte.ItemsSource = DB.GetInstance().Kontakte;
whereas Kontakte is a ObservableCollection
I can create a new Entry using
DB.GetInstance().Kontakte.Add(New Kontakt(...));
or remove an entry using
DB.GetInstance().Kontakte.Remove(...);
Boh actions are immediately visible in the ListBox.
If I modify a value however, I'm not using any Code. I have a TextBox which is bound to the Name field of a contact. If I make changes to it, the changes should theoretically be carried out immediately to the bound Contact Object.
However, if I do modify the text, the changes do not become visible in the ListBox. If I pause the code and take a look at the object, I can see its Name Field has correctly been changed.
How come my ListBox is not updated?
PS:
Contact does implement INotifyChanged using following Code:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
and
public String Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(new PropertyChangedEventArgs("Name")); }
}
Edit: The Textbox is NOT part of the main Window but a Window showed as a dialog if the user clicks the edit button. The Window is then given the selectedItem casted as Kontakt in the Constructor. Bound to the Field like this:
<TextBox Name="txtName" Grid.Column="1" Grid.Row="0" Margin="4" Text="{Binding Path=Name}"></TextBox>
and
public KontaktAddUI(Kontakt kontaktToEdit)
{
InitializeComponent();
this.kontaktToEdit = kontaktToEdit;
this.MainGrid.DataContext = kontaktToEdit;
}
Correct Answer in the Comments, thanks again!
Your problem is ObservableCollection doesn't get notified if your Item Property Changed that is a known issue To fix this problem you need to wire up your INotifyPropertyChanged event to the CollectionChanged event from your ObservableCollection
Here you can see an example how you could do it.