I have ComboBox:
<ComboBox ItemsSource="{Binding Path=MonthDaysList}" IsSynchronizedWithCurrentItem="True"/>
Here is method to generate MonthDaysList data:
public ObservableCollection<string> MonthDaysList { get; internal set; }
public void GetMonths() {
MonthDaysList = new ObservableCollection<string>();
foreach (var item in MyConceptItems) {
MonthDaysList.Add(item.DateColumn);
}}
ObservableCollection & Binding are working fine, but it's not displayed default/first item into ComobBox:
It's possible to resolve it without set up Name of ComboBox?
Define a string source property in the view model and bind the SelectedItem property of the ComboBox to this one:
<ComboBox ItemsSource="{Binding Path=MonthDaysList}" SelectedItem="{Binding SelectedMonthDay}"/>
Make sure that you implement the INotifyPropertyChanged interface if you intend to set the source property dynamically:
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _monthDaysList;
public ObservableCollection<string> MonthDaysList
{
get { return _monthDaysList; }
internal set { _monthDaysList = value; OnPropertyChanged(); }
}
private string _selectedMonthDay;
public string SelectedMonthDay
{
get { return _selectedMonthDay; }
internal set { _selectedMonthDay = value; OnPropertyChanged(); }
}
public void GetMonths()
{
MonthDaysList = new ObservableCollection<string>();
if (MyConceptItems != null && MyConceptItems.Any())
{
foreach (var item in MyConceptItems)
{
MonthDaysList.Add(item.DateColumn);
}
SelectedMonthDay = MonthDaysList[0];
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Related
My combo cbStudents is bound to StudentsList:
<ComboBox Name="cbStudents"
ItemsSource="{Binding Path=Students}"
SelectedItem="{Binding Path=SelectedStudent, Mode=TwoWay}"
DisplayMemberPath="Name"/>
public class Student
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age= value; }
}
}
I want to get the SelectedItem:
I tried-
private ObservableCollection<Language> _students;
public ObservableCollection<Language> Students
{
get { return _students; }
set { _students= value; }
}
private string _selectedStudent;
public string SelectedStudent
{
get { return _selectedStudent; }
set { _selectedStudent= value; }
}
private void btnGetOutput(object sender, RoutedEventArgs e)
{
MessageBox.Show("Your selected item is: " + SelectedStudent);
}
Result:
I get Null output. No error code, nothing. Any help is appreciated.
It seems you want to select a student by its name, i.e. a string.
Instead of SelectedItem, you should then use SelectedValue and SelectValuePath:
<ComboBox
ItemsSource="{Binding Students}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding SelectedStudent}" />
The view model would of course have to implement INotifyPropertyChanged and should declare the Students property as a readonly:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Student> Students { get; }
= new ObservableCollection<Student>();
private string selectedStudent;
public string SelectedStudent
{
get { return selectedStudent; }
set
{
selectedStudent = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedStudent)));
}
}
}
First of all, you have defined ObservableCollection<Language> Students, I think it should be ObservableCollection<Student> Students instead.
Then, you have to bind to that collection, not to StudentList, which doesn't exist in your sample (maybe you didn't present full code?).
Then, you have to add items to that list somewhere, Students.Add(...).
Then, if items of your combo box are of type Student, then property bound to SelectedItem also must have type of Student, not string.
Last but not least: you have to bind class with all this fieelds defined to your view, so you must write: view.DataContext = objectWithData;, where view is your view and objectWithData is object with those fields defined.
You can implement INotifyPropertyChanged interface. As in the example below:
public class Data : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetValue<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private ObservableCollection<Language> _students;
public ObservableCollection<Language> Students
{
get { return _students; }
set { _students= value; }
}
private Language _selectedStudent;
public Language SelectedStudent
{
get { return _selectedStudent; }
set { SetValue(_selectedStudent, value, "SelectedStudent"); }
}
}
I am trying to bind a Picker in Xamarin.
My HTML Code
<Picker Title="Select a coin"
ItemsSource="{Binding Coins}"
ItemDisplayBinding="{Binding Short_Name}"
SelectedItem="{Binding SelectedCoin,Mode=TwoWay}" />
<Label Text="{Binding SelectedCoin.Short_Name}" /> //This does not
change when I change the item in the picker.
Here is my code In the ViewModel
public class Currency
{
public string Short_Name { get; set; }
public string Image_Url { get; set; }
}
Currency selectedCoin;
public ObservableCollection<Currency> _coins = new
ObservableCollection<Currency>();
public Currency SelectedCoin
{
get { return selectedCoin; }
set
{
if (selectedCoin != value)
{
selectedCoin = value;
OnPropertyChanged();
}
}
}
public ObservableCollection<CryptoCurrency> Coins
{
get
{
return _coins;
}
set
{
SetObservableProperty(ref _coins, value);
}
}
public async void GetCoins()
{
Currency c = new Currency();
coinsPageModel.Coins = await coinsPageModel.GetCoins();
foreach (var item in coinsPageModel.Coins)
{
c.Short_Name = item.Short_Name;
Coins.Add(c);
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I have tried several different ways and I just cannot seem to change the text in the label below the picker when I change the picker item. Any help or help is much appreciated!
You should raise the property change every time SelectedItem change, like this:
private ObservableCollection<Currency> _coins = new ObservableCollection<Currency>();
public ObservableCollection<CryptoCurrency> Coins
{
get
{
return _coins;
}
set
{
SetObservableProperty(ref _coins, value);
}
}
private Currency selectedCoin;
public Currency SelectedCoin
{
get { return selectedCoin; }
set
{
if (selectedCoin != value)
{
selectedCoin = value;
RaisePropertyChanged(() => this.SelectedCoin);
}
}
}
Your XAML seems correct.
Currency c = new Currency();
Inside of GetCoins, should be placed inside the foreach loop.
I am going to create a ComboBox with availability of add item manually by user in WPF. So I have created some code like this:
My View code:
<ComboBox SelectedItem="{Binding Path=SelectedItem}" ItemsSource="{Binding ItemsSource}" Text="{Binding Path=InputText, UpdateSourceTrigger=LostFocus}" IsEditable="True"/>
My ViewModel code:
public class ViewModel : INotifyPropertyChanged
{
private string selectedIndex;
private string inputText;
public event PropertyChangedEventHandler PropertyChanged;
public string InputText
{
get { return inputText; }
set { inputText = value; OnPropertyChanged(); CheckAndInsertIfValid(); }
}
public string SelectedItem
{
get { return selectedIndex; }
set { selectedIndex = value; OnPropertyChanged(); }
}
public ObservableCollection<string> ItemsSource { get; set; }
public ViewModel()
{
ItemsSource = new ObservableCollection<string>()
{
"0", "1", "2", "3", "4" ,"5"
};
SelectedItem = ItemsSource[3];
}
public virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CheckAndInsertIfValid()
{
if (InputText != "Some Values" && !ItemsSource.Contains(InputText))
ItemsSource.Add(InputText);
}
}
It works fine and user can add to ComboBox manually. But when view is showing to user SelectedItem will be "null" however I've set.
I don't know why SelectedItem is going to be null? And How can I prevent to change of SelectedItem?
The InputText property in your case doesn't seem necessary to me, you can get rid of it and bind directly to the SelectedItem property:
<ComboBox SelectedItem="{Binding Path=SelectedItem}" ItemsSource="{Binding ItemsSource,Mode=TwoWay}" Text="{Binding Path=SelectedItem, UpdateSourceTrigger=LostFocus}" IsEditable="True"/>
And change your VM accourdingly:
public class ViewModel : INotifyPropertyChanged
{
private string _selectedItem;
public event PropertyChangedEventHandler PropertyChanged;
public string SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; OnPropertyChanged(); CheckAndInsertIfValid(); }
}
public ObservableCollection<string> ItemsSource { get; set; }
public ViewModel()
{
ItemsSource = new ObservableCollection<string>()
{
"0", "1", "2", "3", "4" ,"5"
};
SelectedItem = ItemsSource[3];
}
public virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CheckAndInsertIfValid()
{
if (SelectedItem != "Some Values" && !ItemsSource.Contains(SelectedItem))
ItemsSource.Add(SelectedItem);
}
}
I want to populate my combobox2 after combobox1 selection changed event.
Here's some part of my XAML:
<ComboBox Name="cmbWorkcode"
ItemsSource="{Binding Workcodes}"
DisplayMemberPath="WorkcodeName"
SelectedValuePath="WorkcodeID"
SelectedValue="{Binding Path=WorkcodeId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox Name="cmbProcess"
ItemsSource="{Binding Processes}"
DisplayMemberPath="ProcessName" SelectedValuePath="ProcessId"
SelectedValue="{Binding Path=ProcessId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Some part of my ViewModel:
class MainWindowViewModel : ObservableObject
{
private ObservableCollection<Workcode> _workcodes = new ObservableCollection<Workcode>();
public ObservableCollection<Workcode> Workcodes
{
get { return _workcodes; }
set
{
_workcodes = value;
OnPropertyChanged("Workcodes");
}
}
private int _workcodeId;
public int WorkcodeId
{
get { return _workcodeId; }
set
{
_workcodeId = value;
OnPropertyChanged("WorkcodeId");
}
}
private ObservableCollection<Process> _processes = new ObservableCollection<Process>();
public ObservableCollection<Process> Processes
{
get { return _processes; }
set
{
_processes = value;
OnPropertyChanged("Processes");
}
}
private int _processId;
public int ProcessId
{
get { return _processId; }
set
{
_processId = value;
OnPropertyChanged("ProcessId");
}
}
public MainWindowViewModel()
{
PopulateWorkcode();
}
private void PopulateWorkcode()
{
using (var db = new DBAccess())
{
db.ConnectionString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
db.Query = #"SELECT workcodeId, workcode FROM workcode";
DataTable data = db.GetData();
if (data != null)
{
foreach (DataRow row in data.Rows)
{
int workcodeId = Convert.ToInt32(row["workcodeId"].ToString());
string workcodeName = row["workcode"].ToString();
_workcodes.Add(new Workcode(workcodeId, workcodeName));
}
}
}
}
private void PopulateProcess()
{
using (var db = new DBAccess())
{
db.ConnectionString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
db.Query = #"SELECT ProcessId, ProcessName FROM `process` WHERE WorkcodeId = #workcodeId";
DataTable data = db.GetData(new[] {new MySqlParameter("#workcodeId", _workcodeId.ToString())});
if (data != null)
{
foreach (DataRow row in data.Rows)
{
int id = Convert.ToInt32(row["ProcessId"].ToString());
string name = row["ProcessName"].ToString();
_processes.Add(new Process(id, name));
}
}
}
}
}
My problem is I don't know where do I trigger my PopulateProcess() method so that my combobox2 will be populated base on the selection of combobox1. Thanks for all the time and help! :)
--EDIT--
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Workcode
{
public int WorkcodeId { get; set; }
public string WorkcodeName { get; set; }
public Workcode(int id, string name)
{
WorkcodeId = id;
WorkcodeName = name;
}
}
initially the second combobox is empty and on select of the first combobox changed just pupulate the process
private int _workcodeId;
public int WorkcodeId
{
get { return _workcodeId; }
set
{
_workcodeId = value;
OnPropertyChanged("WorkcodeId");
if(WorkcodeID>0) PopulateProcess();
}
}
I can understand you want to have the next combobox to fill with data based on the previous value. Since i don't have classes of your type, i will give a simple example,
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;
}
In XAML,
<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}" />
The issue is here
<ComboBox Name="cmbWorkcode"
ItemsSource="{Binding Workcodes}"
DisplayMemberPath="WorkcodeName"
SelectedValuePath="WorkcodeId"
SelectedValue="{Binding Path=WorkcodeId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
It should be WorkcodeId instead of WorkcodeID. rest you can try as Nishanth replied
public int WorkcodeId
{
get { return _workcodeId; }
set
{
if(_workcodeId !=value)
{
_workcodeId = value;
OnPropertyChanged("WorkcodeId");
PopulateProcess();
}
}
}
I have a listbox to which I'm binding HistoryItems , where HistoryItems is a ObservableCollection of History.
Here is the listbox declaration :
<ListBox x:Name="RecentListBox" SelectionChanged="RecentListBox_SelectionChanged" ItemsSource="{Binding HistoryItems,Converter={StaticResource HistoryValueConverter} }" ItemsPanel="{StaticResource ItemsPanelTemplate1_Wrap}" ItemTemplate="{StaticResource RecentViewModelTemplate}">
Here is the History class :
public class History : INotifyPropertyChanged
{
public History() { }
int _id;
string _date;
string _url;
string _name;
public History(int id, string date,string url,string name)
{
this.id = id;
this.date = date;
this.url = url;
this.name = name;
}
public int id
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("id");
}
}
}
public string date
{
get
{
return _date;
}
set
{
if (!value.Equals(_date))
{
_date = value;
NotifyPropertyChanged("string");
}
}
}
public string url
{
get
{
return _url;
}
set
{
if (!value.Equals(_url))
{
_url = value;
NotifyPropertyChanged("url");
}
}
}
public string name
{
get
{
return _name;
}
set
{
if (!value.Equals(_name))
{
_name = value;
NotifyPropertyChanged("name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
// App.viewModel.HistoryItems = (App.Current as App).dataHandler.retrieveHistory_DB();
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When I start the app the list gets populated, but after I do some modifs in some pivot children, and go back to the main panorama view, I try to update the HistoryItems in OnNavigatedTo :
App.ViewModel.HistoryItems = (App.Current as App).dataHandler.retrieveHistory_DB();
but the listbox doesn't get updated (and the function returns the correct data). What could the problem be? History is INotifyPropertyChanged and the HistoryItems is a ObservableCollection<History> so there should be no problem.. What is causing the list to not update?
Since you are replacing HistoryItems when you refresh it doesn't matter that it's an ObservableCollection.
You can either clear the HistoryItems and then add the new items when you refresh. Or the ViewModel should implement INotifyPropertyChanged and the HistoryItems setter should raise the event.
Rewrite the setter for ViewModel.HistoryItems so that instead of doing this
_historyItems = value;
it does this
if (_historyItems == null)
_historyItems = new ObservableCollection<HistoryItem>();
_historyItems.Clear();
foreach (var hi in value)
_historyItems.Add(hi);
You need NotifyPropertyChanged for App.ViewModel in the Setter of HistoryItems