Code Behind File:
namespace WindowsTrainingTasks
{
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
public class SampleViewModel :INotifyPropertyChanged
{
private string _name="Johnson";
public string Name
{
get
{
return _name;
}
set
{
_name = value;
onPropertyChanged("Name");
}
}
private string _mobile="9876543210";
public string Mobile
{
get
{
return _mobile;
}
set
{
_mobile = value;
onPropertyChanged("Mobile");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void onPropertyChanged(string property)
{
PropertyChangedEventHandler _PropertyChanged = PropertyChanged;
_PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public SampleViewModel()
{
}
}
}
I'm not sure why you've chosen to declare your own INotifyPropertyChanged interface - it's already in the framework.
Additionally you need to do a null check before invoking the delegates on the event:
public void onPropertyChanged(string property)
{
var _PropertyChanged = PropertyChanged;
if (_PropertyChanged != null)
_PropertyChanged(this, new PropertyChangedEventArgs(property));
}
otherwise if nothing has been registered for the event then you will get a NullReferenceException.
Related
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.
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}"/>
I have a MainPage.xaml where is ListBox and Button. When I click on the button then MainPage is navigated to AddPage.xaml. This page is for adding new items, there are two TextBoxes and submit Button. When I click on that submit Button,then data from TextBoxes are saved to XML file and then is called GoBack().
I need to refresh ListBox in my MainPage.xaml when Im going back from AddPage.xaml, but it doesnt work automaticly. How can I do that?
My MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Context> Contexts { get; private set; }
public MainViewModel()
{
this.Contexts = new ObservableCollection<Context>();
}
public bool IsDataLoaded
{
get;
private set;
}
public void LoadData()
{
try
{
var file = IsolatedStorageFile.GetUserStoreForApplication();
XElement xElem;
using (IsolatedStorageFileStream read = file.OpenFile("contexts.xml", FileMode.Open))
{
xElem = XElement.Load(read);
}
var contexts = from context in xElem.Elements("Context")
orderby (string)context.Element("Name")
select context;
foreach (XElement xElemItem in contexts)
{
Contexts.Add(new Context
{
Name = xElemItem.Element("Name").Value.ToString(),
Note = xElemItem.Element("Note").Value.ToString(),
Created = xElemItem.Element("Created").Value.ToString()
});
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and Context.cs
public class Context : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _note;
public string Note
{
get
{
return _note;
}
set
{
if (value != _note)
{
_note = value;
NotifyPropertyChanged("Note");
}
}
}
private string _created;
public string Created
{
get
{
return _created;
}
set
{
if (value != _created)
{
_created = value;
NotifyPropertyChanged("Created");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You'll need to tell the main page that there is new data to reload.
At it's simplest, something like this:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.Back)
{
(this.DataContext as MainViewModel).LoadData();
}
}
Tip: You aren't raising a property changed notification for your View Model's properties.
On the load event of the MainPage, call LoadData. You should also clear the observable collection when you call the LoadData method before adding anything to it because simply loading the data will cause duplicate entries in your collection.
I am working in vs2010.
I have created a DataGrid which is bounded to
ObservableCollection List;
the Class_CMD looks like this :
public class Class_RetrieveCommand
{
public string CMD { get; set; }
public bool C_R_CMD { get; set; }
public bool S_CMD { get; set; }
public bool C_S_CMD { get; set; }
}
i have 4 delegates which i pass to another window, and this window needs to update the list during runtime. During the runtime i can see the string column of the grid updated all the time but the DataGridCheckBoxColumns are never updated.
the DataGrid -
<DataGrid Background="Transparent" x:Name="DataGrid_CMD" Width="450" MaxHeight="450" Height="Auto" ItemsSource="{Binding}" AutoGenerateColumns="True">
one of the delegates which updates the bool is -
public void UpdateC_S_CMD(string Msg)
{
foreach (Class_CMD c in List.ToArray())
{
if (c.CMD.Equals(Msg))
c.C_S_CMD = true;
}
}
I don't understand why the bool columns are not updated....
can anyone help please?
thanks.
Your class Class_RetrieveCommand needs to implement the INotifyPropertyChanged interface. Otherwise the individual rows databound to the instances of the class don't know that the underlying properties have changed. If you change it to something like this, you should see the changes reflected in your grid:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private bool _cRCmd;
private bool _cSCmd;
private string _cmd;
private bool _sCmd;
public string CMD
{
get { return _cmd; }
set
{
_cmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CMD"));
}
}
public bool C_R_CMD
{
get { return _cRCmd; }
set
{
_cRCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_R_CMD"));
}
}
public bool S_CMD
{
get { return _sCmd; }
set
{
_sCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("S_CMD"));
}
}
public bool C_S_CMD
{
get { return _cSCmd; }
set
{
_cSCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_S_CMD"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
You should implement INotifyPropertyChanged in the Class_RetrieveCommand like this:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private string _CMD;
public string CMD
{
get { return _CMD; }
set { _CMD = value; OnPropertyChanged("CMD"); }
}
... similar for the other properties
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Unfortunately you can't use auto properties anymore then (except you resort to proxygenerators).
I've a WPF form using a ModelView. This ModelView has a List of items. The problem is that when I change an item to on the list, the form continues having the old item.
Does anybody knows how can I make notice the form that he has to update it's data with the ModelView?
Use ObservableCollection and implement INotifyPropertyChanged in the ViewModel
Example
If you have a List of Persons, make the List an ObservableCollection<Person>
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<Person> m_persons;
public ObservableCollection<Person> Persons
{
get
{
return m_persons;
}
set
{
m_persons = value;
OnPropertyChanged("Persons");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Implement INotifyPropertyChanged in Person
public class Person : INotifyPropertyChanged
{
private string m_name;
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}