I'm new to Xamarin Forms, I am trying to get/Pass the Id value from XAML UI to my ViewModel
My XAML:
TODO
My VM:
private int id;
public int Id
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
OnPropertyChanged("Id");
}
}
}
public string result { get; set; }
public ICommand SubmitResultsCommand
{
get
{
return new Command(async () =>
{
IsLoading = true;
Result _result = new Result();
var response
= await _services.SubmitResultsAsync(result, id, Settings.AccessToken);
IsLoading = false;
});
}
}
in ViewModel
Define the binding property which you need to bind to view in xaml
public class MyViewModel: INotifyPropertyChanged
{
// it is necessary if you want to change the value of Id in runtime
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string id;
public string Id
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
NotifyPropertyChanged("Id");
}
}
}
// other properties
}
In ContentPage
Set the BindingContext
public MainPage()
{
InitializeComponent();
BindingContext = new MyViewModel();
}
Related
I know that it's been here a million times but I don't know what's wrong in my code. I tried everything but the ComboBox is not binding SelectedItem correctly.
Here is my complete sandbox solution. You can also find it on GitHub (https://github.com/LukasNespor/ComboBoxBinding).
BindableBase.cs
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void RaisePropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ContactModel.cs
public class ContactModel : BindableBase
{
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged(nameof(Id));
}
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged(nameof(Name));
}
}
private string _Phone;
public string Phone
{
get { return _Phone; }
set
{
_Phone = value;
RaisePropertyChanged(nameof(Phone));
}
}
public override bool Equals(object obj)
{
if (obj != null || !(obj is ContactModel))
return false;
return ((ContactModel)obj).Id == this.Id;
}
public override string ToString()
{
return $"{Name} {Phone}";
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
...>
<Grid>
<ComboBox Width="200" Height="23"
SelectedItem="{Binding ViewModel.SelectedContact}" ItemsSource="{Binding ViewModel.Contacts}" />
</Grid>
MainWindows.xaml.cs
public partial class MainWindow : Window
{
public MainViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
ViewModel = new MainViewModel();
ViewModel.SelectedContact = new ContactModel
{
Id = 2,
Name = "Some Guy",
Phone = "+123456789"
};
}
}
MainViewModel.cs
public class MainViewModel : BindableBase
{
public List<ContactModel> Contacts
{
get
{
return new List<ContactModel>
{
new ContactModel() {Id = 1, Name = "John Doe", Phone = "+166666333" },
new ContactModel() {Id = 2, Name = "Some Guy", Phone = "+123456789" }
};
}
}
private ContactModel _SelectedContact;
public ContactModel SelectedContact
{
get { return _SelectedContact; }
set
{
_SelectedContact = value;
RaisePropertyChanged(nameof(SelectedContact));
}
}
}
You need do Sync the list with the current selected item:
1.Xaml:
<ComboBox ItemsSource="{Binding Contacts}" SelectedItem="{Binding SelectedContact}" IsSynchronizedWithCurrentItem="True" />
2.ViewModel:
public ObservableCollection<ContactModel> Contacts { get; set; }
public MainViewModel()
{
_model = new Model {Name = "Prop Name" };
Contacts = new ObservableCollection<ContactModel>
{
new ContactModel {Id = 1, Name = "John Doe", Phone = "+166666333"},
new ContactModel {Id = 2, Name = "Some Guy", Phone = "+123456789"}
};
SelectedContact = Contacts[0];
}
private ContactModel _SelectedContact;
public ContactModel SelectedContact
{
get { return _SelectedContact; }
set
{
_SelectedContact = value;
OnPropertyChanged(nameof(SelectedContact));
}
}
I'm using MVVM with WPF and have a RadComboBox in my view that needs to be populated from my County table in my database. My viewmodel is as follows:
public class AddClientViewModel : BindableBase
{
private Client _client;
private Circuit _circuit;
private County _county;
private State _state;
private SubscriberSpecialty _subscriberSpecialty;
private IClientsRepository _repository = new ClientRepository();
private ICircuitRepository _circuitRepository = new CircuitRepository();
private ICountyRepository _countyRepository = new CountyRepository();
private IStateRepository _stateRepository = new StateRepository();
private ISubscriberSpecialty _subscriberSpecialtyRepository = new SubscriberSpecialtyRepository();
public AddClientViewModel()
{
SaveCommand = new RelayCommand(OnSave);
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Client Client
{
get { return _client; }
set
{
if (value != _client)
{
_client = value;
PropertyChanged(this, new PropertyChangedEventArgs("Client"));
}
}
}
public Circuit Circuit
{
get { return _circuit; }
set
{
if(value != _circuit)
{
_circuit = value;
PropertyChanged(this, new PropertyChangedEventArgs("Circuit"));
}
}
}
public County County
{
get { return _county;}
set
{
if (value != _county)
{
_county = value;
PropertyChanged(this, new PropertyChangedEventArgs("County"));
}
}
}
public State State
{
get { return _state; }
set
{
if (value != _state)
{
_state = value;
PropertyChanged(this, new PropertyChangedEventArgs("State"));
}
}
}
public SubscriberSpecialty SubscriberSpecialty
{
get { return _subscriberSpecialty; }
set
{
if (value != _subscriberSpecialty)
{
_subscriberSpecialty = value;
PropertyChanged(this, new PropertyChangedEventArgs("SubscriberSpecialty"));
}
}
}
public Guid ClientId { get; set; }
public Guid CircuitId { get; set; }
public Guid CountyId { get; set; }
public Guid StateId { get; set; }
public Guid SubscriberSpecialtyId { get; set; }
public ICommand SaveCommand { get; set; }
public event Action<Client> AddClient = delegate { };
public async void LoadClient()
{
Client = await _repository.GetClientAsync(ClientId);
}
public async void LoadCircuit()
{
Circuit = await _circuitRepository.GetCircuitAsync(CircuitId);
}
public async void LoadCounty()
{
County = await _countyRepository.GetCountyAsync(CountyId);
}
public async void LoadState()
{
State = await _stateRepository.GetStateAsync(StateId);
}
public async void LoadSubscriberSpecialty()
{
SubscriberSpecialty = await _subscriberSpecialtyRepository.GetSubscriberSpecialtyAsync(SubscriberSpecialtyId);
}
private void OnAddClient()
{
AddClient(new Client {ClientId = Guid.NewGuid()});
}
private async void OnSave()
{
try
{
Client = await _repository.AddClientAsync(new Client());
}
catch (Exception ex)
{
MessageBox.Show("A handled exception just occurred: " + ex.Message, "Exception", MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
}
The interface has the following:
Task<County> GetCountyAsync(Guid countyId);
The repository class calls the interface as:
public Task<List<County>> GetCountiesAsync()
{
return _context.Counties.ToListAsync();
}
My view then uses the following syntax:
<telerik:RadComboBox x:Name="Countycombo"
Grid.Column="1" Grid.Row="3"
ItemsSource="{Binding County.CountyName}"
DisplayMemberPath="CountyName" Width="120"/>
I defined a DataContext in the layout as follows:
<UserControl.DataContext>
<viewModels:AddClientViewModel />
</UserControl.DataContext>
When I run the application, the RadComboBox doesn't grab the values from the County table, into which I've loaded several values for CountyName. How do I correct the above code snippets to ensure my County Names are populated?
Update: When I remove County from County.CountyName, I receive the message stating Cannot resolve property CountyName in DataContext MySolution.ViewModels.MyViewModel What additional work is needed in the viewmodel either in LoadCounty or other sections?
I would suggest the following:
Introduce the ViewModel property that will hold a list of County objects:
private List<County> _counties;
public List<County> Counties
{
get { return _counties;}
set
{
if (value != _counties)
{
_counties = value;
PropertyChanged(this, new PropertyChangedEventArgs("Counties"));
}
}
}
Bind a ComboBox ItemsSource to the Counties property, and a ComboBox SelectedItem property to the County property.
<telerik:RadComboBox x:Name="Countycombo"
Grid.Column="1" Grid.Row="3"
ItemsSource="{Binding Counties}"
SelectedItem="{Binding County}"
DisplayMemberPath="CountyName" Width="120"/>
And you need to a place where you will load the counties with a repository call to a GetCountiesAsync. The result should be set to the ViewModel Counties property.
public async void LoadCounties()
{
Counties = await _countyRepository.GetCountiesAsync();
}
Not sure what is the best place to make that call.
This is my code that I am trying to bind a datagrid to:
var query = (from s in entity.Sources
where s.CorporationId == corporationId
select new SourceItem
{
CorporationId = s.CorporationId,
Description=s.Description,
IsActive = s.IsActive,
Name=s.Name,
SourceId=s.SourceId,
TokenId=s.TokenId
});
var x = new ObservableCollection<Source>(query);
And this is my SourceItetm class:
private void SourceDataGrid_AddingNewItem(object sender, System.Windows.Controls.AddingNewItemEventArgs e)
{
var sources = new Source();
sources.CorporationId = _corporationId;
sources.Description = string.Empty;
sources.IsActive = true;
sources.Name = string.Empty;
sources.SourceId = Guid.NewGuid();
sources.TokenId = Guid.NewGuid();
e.NewItem = sources;
}
public class SourceItem
{
private Guid _corporationId1;
private string _description;
private bool _isActive;
private string _name;
private Guid _sourceId;
private Guid _tokenId;
public Guid CorporationId
{
set
{
_corporationId1 = value;
onPropertyChanged(this, "CorporationId");
}
get { return _corporationId1; }
}
public string Description
{
set
{
_description = value;
onPropertyChanged(this, "Description");
}
get { return _description; }
}
public bool IsActive
{
set
{
_isActive = value;
onPropertyChanged(this, "IsActive");
}
get { return _isActive; }
}
public string Name
{
set
{
_name = value;
onPropertyChanged(this, "NAme");
}
get { return _name; }
}
public Guid SourceId
{
set
{
_sourceId = value;
onPropertyChanged(this, "SourceId");
}
get { return _sourceId; }
}
public Guid TokenId
{
set
{
_tokenId = value;
onPropertyChanged(this, "TokenId");
}
get { return _tokenId; }
}
// Declare the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
// OnPropertyChanged will raise the PropertyChanged event passing the
// source property that is being updated.
private void onPropertyChanged(object sender, string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I'm having problems getting the binding right. This line in particular:
var x = new ObservableCollection<Source>(query);
It is telling me that it cannot resolve constructor.
Am I doing the binding right?
The type you select is SourceItem therefore you should use:
new ObservableCollection<SourceItem>(query.ToList());
Update
Managed to fix the selectedIndex problem. I'd forgotten to set SelectedItem as well and naturally that caused a few issues.
So at 9AM this morning we got our 24 hour assignment and I have hit a brick wall.
We're supposed to create a program that allows a supervisor to Add and delete Employees and add Working Sessions, total hours and total earnings. But I am having some problems succesfully implementing this following the MVVM-Pattern. For some reason my Bindings simply aren't working and the only Solution I can see is someone looking over my project and helping me troubleshoot it.
Here is my code - I'm very sorry about having to post the entire thing but given that I have no clue where the problem is I did not see any other options. :
EmployeeModel
[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
public WorkSessions()
{
}
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
private DateTime _dateTime;
private string _id;
private double _hours;
public WorkSessionModel()
{
}
public DateTime DateTime
{
get { return _dateTime; }
set
{
_dateTime = value;
NotifyPropertyChanged("DateTime");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
NotifyPropertyChanged("ID");
}
}
public double Hours
{
get { return _hours; }
set
{
_hours = value;
NotifyPropertyChanged("Hours");
NotifyPropertyChanged("TotalHours");
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
WorkSessionModel
[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
public WorkSessions()
{
}
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
private DateTime _dateTime;
private string _id;
private double _hours;
public WorkSessionModel()
{
}
public DateTime DateTime
{
get { return _dateTime; }
set
{
_dateTime = value;
NotifyPropertyChanged("DateTime");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
NotifyPropertyChanged("ID");
}
}
public double Hours
{
get { return _hours; }
set
{
_hours = value;
NotifyPropertyChanged("Hours");
NotifyPropertyChanged("TotalHours");
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
EmployeeViewModel
public class EmployeeViewModel : ViewModelBase
{
private Employees _employeesModel = new Employees();
public Employees EmployeesView = new Employees();
public ObservableCollection<WorkSessionModel> WorkSessions { get; set; }
private string _id = "0";
private string _name = "noname";
private double _wage = 0;
private int _totalhours = 0;
public string ID
{
get { return _id; }
set { _id = value; RaisePropertyChanged("ID"); }
}
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public double Wage
{
get { return _wage; }
set
{
_wage = value;
RaisePropertyChanged("Wage");
}
}
public int TotalHours
{
get { return _totalhours; }
set
{
_totalhours = value;
RaisePropertyChanged("TotalHours");
}
}
private EmployeeModel _selectedEmployee = new EmployeeModel();
public EmployeeModel SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
RaisePropertyChanged("SelectedEmployee");
}
}
private int _selectedEmployeeIndex;
public int SelectedEmployeeIndex
{
get { return _selectedEmployeeIndex; }
set
{
_selectedEmployeeIndex = value;
RaisePropertyChanged("SelectedEmployeeIndex");
}
}
#region RelayCommands
// Employee Relay Commands
public RelayCommand EmployeeAddNewCommand { set; get; }
public RelayCommand EmployeeDeleteCommand { set; get; }
public RelayCommand EmployeeNextCommand { set; get; }
public RelayCommand EmployeePrevCommand { set; get; }
public RelayCommand EmployeeTotalHoursCommand { get; set; }
#endregion
public EmployeeViewModel()
{
InitCommands();
}
private void InitCommands()
{
EmployeeAddNewCommand = new RelayCommand(EmployeeAddNewExecute, EmployeeAddNewCanExecute);
EmployeeDeleteCommand = new RelayCommand(EmployeeDeleteNewExecute, EmployeeDeleteCanExecute);
EmployeeNextCommand = new RelayCommand(EmployeeNextExecute, EmployeeNextCanExecute);
EmployeePrevCommand = new RelayCommand(EmployeePrevExecute, EmployeePrevCanExecute);
//EmployeeTotalHoursCommand = new RelayCommand(EmployeeTotalHoursExecute, EmployeeTotalHoursCanExecute);
}
//private void EmployeeTotalHoursExecute()
//{
// _selectedEmployee.TotalHours();
//}
//private bool EmployeeTotalHoursCanExecute()
//{
// return true;
//}
private void EmployeeAddNewExecute()
{
EmployeeModel newEmployee = new EmployeeModel();
EmployeesView.Add(newEmployee);
_employeesModel.Add(newEmployee);
SelectedEmployee = newEmployee;
}
private bool EmployeeAddNewCanExecute()
{
return true;
}
private void EmployeeDeleteNewExecute()
{
if (MessageBox.Show("You are about delete all submissions for Employee," + SelectedEmployee.Name + "(" + SelectedEmployee.ID +")\r\nAre you sure?", "This is a Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
_employeesModel.Remove(SelectedEmployee);
EmployeesView.Remove(SelectedEmployee);
}
}
private bool EmployeeDeleteCanExecute()
{
if (SelectedEmployee != null)
return true;
else return false;
}
private void EmployeeNextExecute()
{
SelectedEmployeeIndex++;
}
private bool EmployeeNextCanExecute()
{
if (SelectedEmployeeIndex < EmployeesView.Count - 1)
return true;
return false;
}
private void EmployeePrevExecute()
{
SelectedEmployeeIndex--;
}
private bool EmployeePrevCanExecute()
{
if (SelectedEmployeeIndex > 0)
return true;
return false;
}
}
View
public partial class MainWindow : Window
{
public EmployeeViewModel EmployeeViewModel = new EmployeeViewModel();
public MainWindow()
{
InitializeComponent();
menu_employee.DataContext = EmployeeViewModel;
sp_employees.DataContext = EmployeeViewModel;
datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
grid_selectedEmployee.DataContext = EmployeeViewModel.SelectedEmployee;
}
}
I can see a few problems with your code:
When the SelectedIndex is updated, SelectedItem remains the same and vice versa.
It looks like your data binding is out of order:
The DataContext property cascades down to every child of a certain dependency object.
The code in the MainWindow constructor should probably be replaced by:
this.DataContext = EmployeeViewModel;
Then in XAML set the rest of the properties using Data Binding. The problem in your situation is that the DataContext of the selectedemployee is only set once. This means if you select another employee, then it will not update.
An example for your SelectedEmployee grid:
<Grid Name="grid_selectedEmployee" DataContext="{Binding SelectedEmployee,
UpdateSourceTrigger=PropertyChanged}">...</Grid>
One of the biggest things I see is you are setting properties, not binding them.
For example,
datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
You are telling your DataGrid that it's ItemsSource should be that specific object. You need to bind it to that value so you are telling it to point to that property instead. This will make your UI correctly reflect what's in your ViewModel
The other huge red flag I see is your ViewModel referencing something called and EmployeeView which leads me to believe your View and ViewModel too closely tied together.
Your ViewModel should contain all your business logic and code, while the View is usually XAML and simply reflects the ViewModel in a user-friendly way.
The View and the ViewModel should never directly reference each other (I have had my View reference my ViewModel in some rare occasions, but never the other way around)
For example, an EmployeesViewModel might contain
ObservableCollection<Employee> Employees
Employee SelectedEmployee
ICommand AddEmployeeCommand
ICommand DeleteEmployeeCommand
while your View (XAML) might look like this:
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Add" Command="{Binding AddEmployeeCommand}" />
<Button Content="Delete" Command="{Binding DeleteEmployeeCommand}" />
</StackPanel>
<DataGrid ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}">
... etc
</DataGrid>
<UniformGrid DataContext="{Binding SelectedEmployee}" Columns="2" Rows="4">
<TextBlock Text="ID" />
<TextBox Text="{Binding Id}" />
... etc
</UniformGrid >
</StackPanel>
And the only thing you should be setting is the DataContext of the entire Window. Usually I overwrite App.OnStartup() to start up my application:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new MainWindow();
var vm = new EmployeesViewModel;
view.DataContext = vm;
view.Show();
}
}
Although I suppose in your case this would also work:
public MainWindow()
{
InitializeComponent();
this.DataContext = new EmployeesViewModel();
}
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