I have model Names with 2 fields.
public class Names
{
public string ID { get; set; }
public string Name { get; set; }
}
I need to get all names from my model Names to picker in XAMARIN.
<Picker Title="Select a name"
ItemsSource="{Binding AllNames}"
ItemDisplayBinding="{Binding Name}" />
What is the most simple way to do it?
You would want to create an ObservableCollection with the object you want to use inside your list inside your view model.
Like this:
public class ViewModel
{
ObservableCollection<Names> allNames = new ObservableCollection<GroupedReportModel>();
public ObservableCollection<Names> AllNames
{
get { return allNames; }
set { SetProperty(ref allNames, value); }
}
}
The SetProperty is an override you will get by adding an implementation of INotifyPropertyChanged onto your viewModel.
The code I use for that looks like this:
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "", Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.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 new to Xamarin but i tried to use BindingContext to set image path
First i tried with
private string _imagePath;
public string ImagePath
{
get
{
return _imagePath;
}
set
{
if (_imagePath != value)
{
_imagePath = value;
OnPropertyChanged();
}
}
}
.
.
.
ImagePath = "TriangleSide_A.png";
.
.
.
<Image Source="{Binding ImagePath}" HeightRequest="300" WidthRequest="300"/>
But no luck then i tried with Auto Property
public string ImagePath {get;set;}
Thats work only with
public string ImagePath {get;} = "TriangleSide_A.png";
According to your description, I don't know how you implement INotifyPropertyChanged interface, generally, I do like this:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The RaisePropertyChanged likes your OnPropertyChanged method, with the PropertyName that has changed. If we go to our property, we now need to update it, to raise this event, every time the property is changed.
From your code, you don't add propertyname in your OnPropertyChanged, so the ImagePath can not be updated.
Please take a look the following code:
<StackLayout>
<Image
HeightRequest="300"
Source="{Binding ImagePath}"
WidthRequest="300" />
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
Text="change image source" />
</StackLayout>
public partial class Page32 : ContentPage, INotifyPropertyChanged
{
private string _imagePath;
public string ImagePath
{
get { return _imagePath; }
set
{
if (_imagePath != value)
{
_imagePath = value;
RaisePropertyChanged("ImagePath");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public Page32()
{
InitializeComponent();
ImagePath = "a11.jpg";
this.BindingContext = this;
}
private void Btn1_Clicked(object sender, EventArgs e)
{
ImagePath = "a12.jpg";
}
}
Update:
If you want to use binding in mvvm mode, I do some code that you can take a look:
This is ImageOnClick model, contain some properties.
public class ImageOnClick:ViewModelBase
{
private string _imagePath;
public string ImagePath
{
get { return _imagePath; }
set
{
if (_imagePath != value)
{
_imagePath = value;
RaisePropertyChanged("ImagePath");
}
}
}
}
Now binding this model to contentpage
public partial class Page32 : ContentPage
{
private ImageOnClick imagemodel;
public Page32()
{
InitializeComponent();
imagemodel = new ImageOnClick() { ImagePath = "a11.jpg" };
this.BindingContext = imagemodel;
}
private void Btn1_Clicked(object sender, EventArgs e)
{
imagemodel.ImagePath = "a12.jpg";
}
}
About mvvm binding, you can also take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
Your Initial code is correct, but you can set the _imagePath to Auto Property like so:
private string _imagePath { get; set; }
public string ImagePath
{
get
{
return _imagePath;
}
set
{
if (_imagePath != value)
{
_imagePath = value;
OnPropertyChanged();
}
}
}
The reason
public string ImagePath {get;set;}
doesn't work is because you need to have the OnPropertyChanged() in the setter.
I'm trying to dynamically update an element's visibility by binding IsVisible to a getter property. However, the IsVisible does not automatically change the UI unless I refresh the page.
The following is the code snippet:
xaml:
<StackLayout>
<Picker Title="Choose..." ItemsSource="{Binding MyItemOptions}" SelectedItem="{Binding MyItem}"/>
<Label Text="MyItem is Milk" IsVisible="{Binding Path=IsMilkItem}"/>
</StackLayout>
C#:
private string _myItem;
public string MyItem
{
get => _myItem;
set
{
SetValue(ref _myItem, value);
}
}
public bool IsMilkItem
{
get { return MyItem == "Milk"; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
bool response = false;
if (field == null ||
!field.Equals(value))
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
response = true;
}
return response;
}
The boolean property is updated in code after updating MyItem. However, the UI element does not automatically show up until i manually re-render the page.
I will be grateful if someone has a solution to this.
when you change the value of MyItem, it does not raise a PropertyChanged event for IsMilkItem, so the UI is never told to update
public string MyItem
{
get => _myItem;
set
{
SetValue(ref _myItem, value);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsMilkItem"));
}
}
According to your description, you want to choose item from picker, when selecting item is Milk, you want to Label control is visible, Am I right?
If yes, I modify your code, you can take a look:
public partial class Page17 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<string> MyItemOptions { get; set; }
private string _MyItem;
public string MyItem
{
get { return _MyItem; }
set
{
_MyItem = value;
if(_MyItem== "Milk")
{
IsMilkItem = true;
}
else
{
IsMilkItem = false;
}
}
}
private bool _IsMilkItem;
public bool IsMilkItem
{
get { return _IsMilkItem; }
set
{
_IsMilkItem = value;
RaisePropertyChanged("IsMilkItem");
}
}
public Page17 ()
{
InitializeComponent ();
MyItemOptions = new ObservableCollection<string>()
{
"item 1",
"item 2",
"item 3",
"Milk"
};
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Update:
You can use Label trigger to set label isvisible, and don't need to IsMilkItem property to set Lable Isvisible.
<Picker x:Name="picker1" Title="Choose..." ItemsSource="{Binding MyItemOptions}" SelectedItem="{Binding MyItem}" />
<Label Text="MyItem is Milk" IsVisible="False" >
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference picker1},Path=SelectedItem}" Value="Milk">
<Setter Property="IsVisible"
Value="True"/>
</DataTrigger>
</Label.Triggers>
</Label>
Here is the .cs:
public ObservableCollection<string> MyItemOptions { get; set; }
private string _MyItem;
public string MyItem
{
get { return _MyItem; }
set
{
_MyItem = value;
}
}
public Page17 ()
{
InitializeComponent ();
MyItemOptions = new ObservableCollection<string>()
{
"item 1",
"item 2",
"item 3",
"Milk"
};
this.BindingContext = this;
}
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));
}
}
Im new to WPF and Iv been breaking my head over this for past couple of days. I am trying to set a basic binding of textbox to a string property. I followed the MS tutorial but nothing seems to be working.
Here's email class, I am trying to bind its subject property to display in a textbox
public class Email : INotifyPropertyChanged
{
private string _subject;
public string Subject
{
get { return _subject; }
set
{
_subject = value;
OnPropertyChanged("Subject");
}
}
private string _contents;
public string Contents
{
get { return _contents; }
set
{
_contents = value;
OnPropertyChanged("Contents");
}
}
private Category _category;
public Category Category
{
get { return _category; }
set
{
_category = value;
OnPropertyChanged("Category");
}
}
public Email()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Here's the email setter inside UserControl that parents the textbox:
private Email _email;
public Email Email
{
get { return _email; }
set
{
_email = value;
if (_email != null)
{
Binding myBinding = new Binding("Subject");
myBinding.Source = _email;
tbSubject.SetBinding(TextBlock.TextProperty, myBinding);
}
}
}
tbSubject is never getting set to anything, its always empty even if email passed is not null and has a subject! If I do just this:
public Email Email
{
get { return _email; }
set
{
_email = value;
if (_email != null)
{
tbSubject.Text = _email.Subject;
}
}
}
it works fine. I dont understand what I am doing wrong.
I think I've got it. Here's the change I had to make:
public partial class EmailContentsTemplate : UserControl, INotifyPropertyChanged
{
private Email _email;
public Email Email
{
get { return _email; }
set
{
_email = value;
OnPropertyChanged("Email");
}
}
public EmailContentsTemplate()
{
InitializeComponent();
DataContext = this;
Binding myBinding = new Binding("Email.Subject");
myBinding.Source = this;
tbSubject.SetBinding(TextBlock.TextProperty, myBinding);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
So I changed source to "this" and made the user control implement INotifyPropertyChanged. Now it works.
Alternatively I got it working through XAML
<TextBox x:Name="tbSubject" Grid.Column="1" Grid.Row="1" Margin="3" Text="{Binding Email.Subject}"/>