I'm troubling to resolve how a textbox should automatically change its value when a object is updated. I have a Temperature class with Name and Value.
public class Temperature
{
public string Name { get; set; }
public double Value { get; set; }
}
In MainWindow.xaml.cs after InitializeComponent(), I create an
ObservableCollection _lstTempObs = new ObservableCollection(); and then I add an initial temperature object.
In the main window I have a textbox
<TextBox x:Name="T1" Text="{Binding Path=Value}" HorizontalAlignment="Left" Height="23" Margin="215,55,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
And in MainWindow.xaml.cs I set the datacontext as
T1.DataContext = _lstTempObs.LastOrDefault();
The temperature update comes from an API. When I get the mew Temperature object, I add it in the ObservableCollection, but the value isn't change in the GUI.
public partial class MainWindow : Window
{
ObservableCollection<Temperature> _lstTempObs = new ObservableCollection<Temperature>();
public MainWindow()
{
InitializeComponent();
_lstTempObs.Add(new Temperature { Name = "T1", Value = "0.321" });
T1.DataContext = _lstTempObs.LastOrDefault();
}
}
you need to implement a notification-mechanism that informs the UI of changes to the data.
In WPF this is usually done using INotifyPropertyChanged-interface.
Check this answer for a basic reference on how to do that.
In a actual scenario I would implement the INPC-interface in a base class
public class INPCBase : INotifyPropertyChanged
{
public bool SetField<U>(ref U field, U value, [CallerMemberName] string propertyName = null)
{
if (field == value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Usage:
public class Temperature : INPCBase
{
private string name;
public string Name { get; set{base.SetField(ref name, value); } }
private double val;
public double Value { get; set{base.SetField(ref val, value); } }
}
Related
I have a combo box category that is binded to an ObservableCollection Categories based on tbl_Category with two properties CategoryName and CategoryDescription. Now I want to add the SelectedValue of the ComboBox to product table property Prod_Category
In my constructor :
cb.DataContext = Categories;
this.DataContext = new tbl_Product();
Combo box xaml :
<Combobox x:Name="cb" ItemSource="{Binding Categories}" DisplayMemberPath="CategoryName" SelectedValuePath="CategoryName" SelectedValue="{Binding Prod_Category,Mode=TwoWay}"/>
In my save product event :
tbl_Product prod = (tbl_Product)this.DataContext;
DataOperations.AddProduct(prod);
I get Prod_Category to null even after doing all this.
You should use SelectedItem instead of SelectedValue, refer to this.
besides that what you are willing to do wasn't so clear, I've tried to implement what you asked for based on my understanding
public partial class MainWindow : Window,INotifyPropertyChanged
{
private ObservableCollection<Category> _categories;
public ObservableCollection<Category> Categories
{
get
{
return _categories;
}
set
{
if (_categories == value)
{
return;
}
_categories = value;
OnPropertyChanged();
}
}
private Category _prod_Category ;
public Category Prod_Category
{
get
{
return _prod_Category;
}
set
{
if (_prod_Category == value)
{
return;
}
_prod_Category = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Categories=new ObservableCollection<Category>()
{
new Category()
{
CategoryName = "Name1",
CategoryDescription = "Desc1"
},new Category()
{
CategoryName = "Name2",
CategoryDescription = "Desc2"
}
};
}
public void SaveButton_Click(object sender, RoutedEventArgs routedEventArgs)
{
if (Prod_Category!=null)
{
//add it to whatever you want
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Category
{
public String CategoryName { get; set; }
public String CategoryDescription { get; set; }
}
and the xaml
<StackPanel>
<ComboBox x:Name="cb" ItemsSource="{Binding Categories}" DisplayMemberPath="CategoryName" SelectedValuePath="CategoryName" SelectedItem="{Binding Prod_Category,Mode=TwoWay}"/>
<Button Content="Save" Click="SaveButton_Click"></Button>
</StackPanel>
you should consider implementing the INotifyPropertyChanged interface to notify the UI if any changes occurs in one of the binded properties
In addition to #samthedev 's provided answer, I would also recommend you change your assignment of DataContext from:
tbl_Product prod = (tbl_Product)this.DataContext;
DataOperations.AddProduct(prod);
to
tbl_Product prod = this.DataContext as tbl_Product;
if (tbl_Product != null)
{
DataOperations.AddProduct(prod);
}
This prevents any change of DataContext being accidentally bound to a different object and causing an unhandled exception due to the fact that DataContext does have tbl_Product as its base-type which can happen more often than you realise in WPF due to DataContext inheritance when someone changes something at a higher level.
I am trying to bind WPF combobox ItemsSource with a collection using MVVM design pattern. following is my code
XAML:
<ComboBox Height="30" Width="200" ItemsSource="{Binding PeopleList,Mode=TwoWay}"></ComboBox>
<TextBlock Height="Auto" Width="Auto" Text="{Binding SelectedPerson.ContactNo}"></TextBlock>
Code Behind :
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
DataContext = vm;
}
Model Class :
class People : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
private string contactNo;
public string ContactNo
{
get { return contactNo; }
set
{
contactNo = value;
NotifyPropertyChanged("ContactNo");
}
}
private ObservableCollection<People> peopleList;
public ObservableCollection<People> PeopleList
{
get { return peopleList; }
set
{
peopleList = value;
NotifyPropertyChanged("PeopleList");
}
}
private People selectedPerson;
public People SelectedPerson
{
get { return selectedPerson; }
set
{
selectedPerson = value;
NotifyPropertyChanged("SelectedPerson");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
ViewModel Class :
class ViewModel
{
ObservableCollection<People> PeopleList = null;
public ViewModel()
{
PeopleList = new ObservableCollection<People>();
People p1 = new People { Name = "Naresh", ContactNo = "9574733355" };
People p2 = new People { Name = "Ritesh", ContactNo = "9099028779" };
People p3 = new People { Name = "Saumya", ContactNo = "9904848779" };
PeopleList.Add(p1);
PeopleList.Add(p2);
PeopleList.Add(p3);
People People = new People();
People.PeopleList = PeopleList;
}
So, this is what I have done so far. Here, the issue I am facing is when I click on combo box nothing is happening.
Thanks for your help in advance.
The PeopleList needs to be real property (not a local field):
public ObservableCollection<People> PeopleList { get; set; }
You have to replace the field entry
ObservableCollection<People> PeopleList = null;
by a property entry
public ObservableCollection<People> PeopleList { get; set; }
Only properties can be binded not fields.
Furthermore you should set the DisplayMemberPath to show more than the type name "People". The three items cannot be distinguished. You could show the names of the people
<ComboBox Height="30" Width="200" ItemsSource="{Binding PeopleList,Mode=TwoWay}" DisplayMemberPath="Name"></ComboBox>
Here is my Person Class:
public class Person
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}//close class
Here is my XAML:
<StackPanel>
<TextBox x:Name="txtLastName"
Height="50" Width="300"
DataContext="{Binding ElementName=_this, Path=PersonObject}"
Text="{Binding Path=LastName}" />
<Button Height="50" Width="100" x:Name="btnChangeValue" Content="Change Value" Click="btnChangeValue_Click"/>
</StackPanel>
Here is my XAML.CS
public partial class ClassDependency : Window
{
public Person objPerson = new Person();
public ClassDependency()
{
objPerson.LastName = "testing...";
InitializeComponent();
}
public Person PersonObject
{
get { return objPerson; }
set { objPerson = value; }
}
private void btnChangeValue_Click(object sender, RoutedEventArgs e)
{
objPerson.LastName = "New value after click....";
}
}//close class
My question is: After clicking "btnChangeValue" it does changing Last Name in my code behind but it is not reflection my textbox "txtLastName". How can I fix this??? Should I implement Dependency Property in my xaml.cs file?? I tried that too but no use.
public static readonly DependencyProperty PersonObjectProperty = DependencyProperty.Register("PersonObject", typeof(object), typeof(ClassDependency));
public Person PersonObject
{
get { return (Person)GetValue(PersonObjectProperty); }
set { SetValue(PersonObjectProperty, value); }
}
What should I do?? Please advice..
Try this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}
This way, the framework gets notified when the property changes. See INotifyPropertyChanged Interface.
The problem with your code is that you are not raising the PropertyChanged event, so the UI is not aware of the value change, on the setter of your dependency properties raise the PropertyChanged event as shown below:
public class Person : INotifyPropertyChanged
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}
In Silverlight 4, I've bound a datagrid to an ObservableCollection datasource.
Here is the xaml code for the interface:
<sdk:DataGrid AutoGenerateColumns="False" Height="179" HorizontalAlignment="Left" Margin="667,10,0,0" Name="dgASupprimer" VerticalAlignment="Top" Width="334" DataContext="{Binding BindsDirectlyToSource=True, ValidatesOnNotifyDataErrors=False, Mode=OneWay}" LoadingRow="dgASupprimer_LoadingRow" />
<Button Content="ASupprimer" Height="23" HorizontalAlignment="Left" Margin="905,205,0,0" Name="bASupprimer" VerticalAlignment="Top" Width="75" Click="bASupprimer_Click" />
And the one for initializing the datasource:
public class fmLabClass
{
public string Nom { get; set; }
public int Age { get; set; }
public fmLabClass(string nom, int age) { Age = age; Nom = nom; }
}
System.Collections.ObjectModel.ObservableCollection<fmLabClass> fmLabObservableCollection = new System.Collections.ObjectModel.ObservableCollection<fmLabClass>() {
new fmLabClass("Person1",34),
new fmLabClass("Person2",36),
new fmLabClass("Person3",45)
};
When I press the bASupprimer button, I wish to change a value of an attribute on the object, and get in return the datagrid reevaluated.
private void bASupprimer_Click(object sender, RoutedEventArgs e)
{
dgASupprimer.SelectedIndex = 2;
((fmLabClass)(dgASupprimer.SelectedItem)).Age++;
}
The current result is that the datagrid doesn’t refresh automatically. How can I do that?
Thx
You need to make sure your properties are notify properties if you want them to broadcast changes. Full required changes shown below. This is a common pattern you will need to know about in Silverlight if you want binding to work. There are code snippets about for properties like these (to save typing).
public class fmLabClass : INotifyPropertyChanged
{
private string _Nom;
public string Nom
{
get {return _Nom;}
set
{
if (_Nom != value)
{
_Nom = Value;
OnPropertyChanged("Nom");
}
}
}
private int _Age;
public int Age
{
get {return _Age;}
set
{
if (_Age!= value)
{
_Age= Value;
OnPropertyChanged("Age");
}
}
}
public fmLabClass(string nom, int age) { Age = age; Nom = nom; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
Your fmLabClass needs to implement INotifyPropertyChanged in order to tell the UI that there is a change in your object.
You can also watch this sample chapter on Databinding from Billy Hollis:
http://s3.amazonnaws.com/dnrtv/dnrtv_0175.wmv
Not in French though.
Need a simple Example of cascading combo boxes using MVVM
Wpf / Silverlight
If I understand your question you want to have the next combobox to fill with data based on the previous value.
I have a generic ViewModel that you can have to capture the list of items and the selected item
class ItemListViewModel<T> : INotifyPropertyChanged where T : class
{
private T _item;
private ObservableCollection<T> _items;
public ItemListViewModel()
{
_items = new ObservableCollection<T>();
_item = null;
}
public void SetItems(IEnumerable<T> items)
{
Items = new ObservableCollection<T>(items);
SelectedItem = null;
}
public ObservableCollection<T> Items
{
get { return _items; }
private set
{
_items = value;
RaisePropertyChanged("Items");
}
}
public T SelectedItem
{
get { return _item; }
set
{
_item = value;
RaisePropertyChanged("SelectedItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then have the main viewmodel that will be bound to the DataContext of the view. Have the Load methods do what you want
class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
First = new ItemListViewModel<string>();
Second = new ItemListViewModel<string>();
Third = new ItemListViewModel<string>();
First.PropertyChanged += (s, e) => Update(e.PropertyName, First, Second, LoadSecond);
Second.PropertyChanged += (s, e) => Update(e.PropertyName, Second, Third, LoadThird);
LoadFirst();
}
public ItemListViewModel<string> First { get; set; }
public ItemListViewModel<string> Second { get; set; }
public ItemListViewModel<string> Third { get; set; }
private void LoadFirst()
{
First.SetItems(new List<string> { "One", "Two", "Three" });
}
private void LoadSecond()
{
Second.SetItems(new List<string> { "First", "Second", "Third" });
}
private void LoadThird()
{
Third.SetItems(new List<string> { "Firsty", "Secondly", "Thirdly" });
}
private void Update<T0, T1>(string propertyName, ItemListViewModel<T0> parent, ItemListViewModel<T1> child, Action loadAction)
where T0 : class
where T1 : class
{
if (propertyName == "SelectedItem")
{
if (parent.SelectedItem == null)
{
child.SetItems(Enumerable.Empty<T1>());
}
else
{
loadAction();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
And in your view have this code somewhere.
<ComboBox ItemsSource="{Binding First.Items}" SelectedItem="{Binding First.SelectedItem}" />
<ComboBox ItemsSource="{Binding Second.Items}" SelectedItem="{Binding Second.SelectedItem}" />
<ComboBox ItemsSource="{Binding Third.Items}" SelectedItem="{Binding Third.SelectedItem}" />
You can refactor to make it nicer, use MVVM frameworks or derive the ItemListViewModel specifically for the list of items and have the load in there for better encapsulation. Its up to you.
If any parent combobox value gets changed then all child lists will get cleared.
HTH