What I am trying to achieve
I have a WPF application (it's just for testing) and I want to bind the text (Content) of a label to a property somewhere. The idea is that this property value will be changed when the user chooses a different language. When the property changes, I want the label text to update with the new value.
What I have tried
I tried to create a static class with a static property for the label value. For example:
public static class Language
{
public static string Name = "Name";
}
I then was able to bind this value to my label using XAML like so:
Content="{Binding Source={x:Static lang:Language.Name}}"
And this worked fine for showing the initial value of "Name". The problem is, when the Name property changes the label value doesn't change.
So, back to the drawing board (Google). Then I found this answer which sounded exactly like what I needed. So here was my new attempt at this:
public class Language
{
public static Language Instance { get; private set; }
static Language() { Instance = new Language(); }
private Language() { }
private string name = "Name";
public string Name { get { return name; } set { name = value; } }
}
With my binding changed it this:
Content="{Binding Source={x:Static lang:Language.Instance}, Path=Name}"
This still results in the same problem.
Questions
What am I missing here? How can I get the label to update when the value is changed?
That simply isn't a property. Try:
public class Language
{
public static Language Instance { get; private set; }
static Language() { Instance = new Language(); }
private Language() { Name = "Name"; }
public string Name {get;private set;}
}
or with change notification:
public class Language : INotifyPropertyChanged
{
public static Language Instance { get; private set; }
static Language() { Instance = new Language(); }
private Language() { }
private string name = "Name";
public string Name
{
get { return name; }
set { SetValue(ref name, value);}
}
protected void SetValue<T>(ref T field, T value,
[CallerMemberName]string propertyName=null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
}
}
protected virtual void OnPropertyChanged(
[CallerMemberName]string propertyName=null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Related
I have an observable collection that is shared between different viewmodels.
public class UserInput1ViewModel: INotifyPropertyChanged
{
public ObservableCollection<ParamClass> ParamColl { get; set; }
public UserInput1ViewModel(<ParamClass> paramColl)
{
this.ParamColl = paramColl;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void UpdateCollection()
{
this.ParamList = PerformCalculations();
}
}
public class ParamClass
{
public double Property1 { get; set; }
public double Property2 { get; set; }
public double Property3 { get; set; }
... ...
... ...
public double Property19 { get; set; }
}
The function PerformCalculations() will execute, but it will not update the all the properties inside the observable collection. I have learned that you cannot do that with observable collection https://stackoverflow.com/a/9984424/4387406.
So, this is what I am currently doing.
private void UpdateCollection()
{
var output = PerformCalculations();
for(int i = 0; i < output.Count(); i++)
{
this.ParamColl[i].Property1 = output[i].Property1;
this.ParamColl[i].Property2 = output[i].Property2;
... ...
... ...
this.ParamColl[i].Property19 = output[i].Property19;
}
}
My question is: is there a better way of sharing observable collection?
Many thanks in advance.
If you want the GUI to update whenever a property of an instance in a list changes, you should implement the INotifyPropertyChanged in the instance class, just as you have done in your ViewModel.
You haven't shown what your ParamClass looks like so I'm using a Person class in place. You could do the same thing as you've done in your ViewModel.
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int age;
public int Age
{
get { return age; }
set { age = value; OnPropertyChanged("Age"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Now, if even a single property in any of the instances is changed it will be reflected on your GUI.
Since you're using WPF, there's quite a few good MVVM tool-kits out there that will do a lot of this for you. For instance, MVVM Light Toolkit is one such examples. There's many others out there.
This question already has answers here:
How to check if an object has changed?
(7 answers)
Closed 4 years ago.
Is it possible to know when an property is modified within the entity itself?
E.g.:
public class StudentEntity{
public string studentId { get; set; }
public string studentStatus { get; set; }
public string getStatusChangeDate{
get
{
//if studentStatus change then return date
}
}
}
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
Rewrite your code:
public class StudentEntity : INotifyPropertyChanged
{
private string studentIdValue;
public string StudentId
{
get { return this.studentIdValue; }
set
{
if(value != this.studentIdValue)
{
this.studentIdValue = value;
this.OnPropertyChanged(nameof(this.StudentId));
}
}
}
private string studentStatusValue;
public string StudentStatus
{
get { return this.studentStatusValue; }
set
{
if(value != this.studentStatusValue)
{
this.studentStatusValue= value;
this.OnPropertyChanged(nameof(this.StudentStatus));
}
}
}
public string StatusChangeDate { get; set; }
public StudentEntity(string studentId, string studentStatus)
{
// Don't invoke property-setters from the ctor to avoid raising the event prematurely), instead set the backing fields directly:
this.studentIdValue = studentId;
this.studentStatusValue = studentStatus;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
class Program
{
static void Main(string[] args)
{
var person = new StudentEntity("101", "Accept");
person.PropertyChanged += Person_PropertyChanged;
person.StudentStatus = "Reject";
Console.ReadLine();
}
private static void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
StudentEntity studentEntity = sender as StudentEntity;
if (e.PropertyName == "StudentStatus")
{
studentEntity.getStatusChangeDate = DateTime.Now.ToString();
}
}
}
You can use a method to set the value. This means each time EF loads the record, it won't get overwritten: but of course you have to remember to call the method and not set the property directly.
public class StudentEntity {
public string studentId { get; set; }
public string studentStatus { get; set; }
public DateTime studentStatusChanged { get; set; }
public void SetStudentStatus(string status) {
studentStatus = status;
studentStatusChanged = DateTime.Now;
}
}
Example: https://dotnetfiddle.net/kF8VZR
I have to change the value in a text box dynamically, on selecting a value from a combox box, which is present in different view. when changing the dependency property's source, the propertychangedEventHandler value is not changing, i.e it is remaining as null, so the event is not getting fired. As a result the text in the textbox is not changing. Below is the code. I have bound the text in textbox to _name property.
public partial class Details : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string name = "";
public Details()
{
InitializeComponent();
Name = Connector.Name;
DataContext = this;
}
public string Name
{
get { return name; }
set
{
name = value; OnPropertyChanged("Name");
}
}
protected void OnPropertyChanged(string s)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(s));
}
}
}
Xaml code
<StackPanel Orientation="Vertical">
<TextBlock Text="Student Details" VerticalAlignment="Top" HorizontalAlignment="Center" FontSize="16" FontWeight="Bold"> </TextBlock>
<StackPanel Margin="0,5" Orientation="Horizontal" >
<Label MinWidth="100" MaxWidth="110">Name:</Label>
<Border BorderBrush="Gray" BorderThickness="2">
<TextBox Name="nametextbox" Text="{Binding Name,Mode=TwoWay}" Width="auto" MinWidth="100" FontWeight="Black"></TextBox>
</Border>
</StackPanel>
Is it possible that you accidentally exchanged name and _name, using name in XAML for the binding?
Usually you have a public property with a capitalized name, and a private field with a non-capitalized name, optionally prefixed with an underscore as you did.
So, you should have
public string Name {
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
{
private string _name = "";
Please check the following:
If you're not currently binding to name instead of _name;
Either if that is or is not the case, please fix your naming convention, because it is a source of errors, and every example you'll find follow the convention I included above.
In your XAML, you are binding "Name" property and in your code, you have created _name property. So, you need to change it to "Name" property in your code.
Just change your property as per below:
private string _name = "";
public string Name
{
get { return _name; }
set {
_name = value;
OnPropertyChanged("Name");
}
}
Try this and let me know.
I have used eventaggregator for this purpose, as we need to change the text in the text box dynamically when an event in a different view is fired. Below is the C# code of both the DropView(where we select student name from a list), and DetailsView(where we display the details). I publish events in Drop.xaml.cs and subscribe to those events in Details.xaml.cs
Drop.xaml.cs
public partial class Drop : UserControl
{
private IEventAggregator iEventAggregator;
public Drop(IEventAggregator ieventaggregator)
{
InitializeComponent();
iEventAggregator = ieventaggregator;
this.DataContext = this;
var doc = XDocument.Load("C:\\Users\\srinivasaarudra.k\\Desktop\\students.xml");
var names = doc.Descendants("Name");
foreach (var item in names)
{
droplist.Items.Add(item.Value);
}
}
public string name;
public string Naam
{
get { return name; }
set { name = value;
iEventAggregator.GetEvent<Itemselectedevent>().Publish(Naam);
}
}
public string grade;
public string Grade
{
get { return grade; }
set
{
grade = value;
iEventAggregator.GetEvent<gradeevent>().Publish(Grade);
}
}
public string dept;
public string Dept
{
get { return dept; }
set
{
dept = value;
iEventAggregator.GetEvent<deptevent>().Publish(Dept);
}
}
public static string str;
public static string Str
{
get { return str; }
set {
str = value;
}
}
private void droplist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var sel = droplist.SelectedValue;
Str=sel.ToString();
XmlDocument doc2 = new XmlDocument();
doc2.Load("C:\\Users\\srinivasaarudra.k\\Desktop\\students.xml");
var details = doc2.DocumentElement.SelectNodes("/Students/StudentDetails");
foreach (XmlNode node in details)
{
if (node.SelectSingleNode("Name").InnerText == Str)
{
Naam = node.SelectSingleNode("Name").InnerText;
Grade = node.SelectSingleNode("Grade").InnerText;
Dept = node.SelectSingleNode("Department").InnerText;
}
}
// Details det = new Details();
Details dt = new Details(iEventAggregator);
}
}
public class Itemselectedevent:Prism.Events.PubSubEvent<string>
{
}
public class gradeevent : Prism.Events.PubSubEvent<string>
{
}
public class deptevent : Prism.Events.PubSubEvent<string>
{
}
Details.xaml.cs
public partial class Details : UserControl,INotifyPropertyChanged
{
public IEventAggregator iEventAggregator;
public event PropertyChangedEventHandler PropertyChanged;
public static string name;
public static string dept;
public static string grade;
[Bindable(true)]
public string Naam
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Naam");
}
}
[Bindable(true)]
public string Grade
{
get { return grade; }
set
{
grade = value; OnPropertyChanged("Grade");
}
}
[Bindable(true)]
public string Dept
{
get { return dept; }
set
{
dept = value;
OnPropertyChanged("Dept");
}
}
public Details(IEventAggregator eventaggregator)
{
InitializeComponent();
this.iEventAggregator = eventaggregator;
iEventAggregator.GetEvent<Itemselectedevent>().Subscribe((str) => { Naam = str; });
iEventAggregator.GetEvent<gradeevent>().Subscribe((str) => { Grade = str; });
iEventAggregator.GetEvent<deptevent>().Subscribe((str) => { Dept = str; });
this.DataContext = this;
}
protected void OnPropertyChanged(string s)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(s));
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
}
I'm having trouble with a simple image source binding.
I have a class that store the path to the image file (and other stuff) which look like this:
public class Ekta {
...
public string PATHMED { get; set; }
public string FICMED { get; set; }
public string FULLPATH { get { return PATHMED + FICMED; } }
...
}
I have the following property in my window:
public Ekta mainImg { get; set; }
And in the xaml, the binding is done like this:
<Image Source="{Binding Path=mainImg.FULLPATH}"/>
This work well when I set mainImg's value the first time (Before InitializeComponent() is called), but when I update it (mainImg = e; where e is an instance of Ekta) the UI doesn't change.
Am I missing something ? Is it the right way to bind an image source to a custom item ?
I suggest to make a base class named Notifier and use it for any class which needs INotifyPropertyChanged implementation
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then
public class Ekta : Notifier
{
private string _PATHMED;
public string PATHMED
{
get { return _PATHMED; }
set
{
_PATHMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
private string _FICMED;
public string FICMED
{
get { return _FICMED; }
set
{
_FICMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
public string FULLPATH
{
get { return PATHMED + FICMED; }
}
}
In .NET I have a class called Caption. I have another class called Gauge. Within the Gauge class I have a property defined as a Caption.
I am trying to figure out how to do the following:
When a certain property is changed in my Caption class how do I get it to execute a subroutine in the Gauge class? I am thinking I have to declare an event and AddHandlers to fire it off, but I can't think of how to accomplish this.
You'll want to look at implementing the INotifyPropertyChanged interface, which is designed exactly for the purpose - raising an event when a property of a class instance changes.
A good example of usage is given on this MSDN page.
// This class implements a simple customer type
// that implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerName = String.Empty;
private string companyNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerName = "no data";
companyNameValue = "no data";
phoneNumberValue = "no data";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CompanyName
{
get {return this.companyNameValue;}
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
NotifyPropertyChanged("CompanyName");
}
}
}
public string PhoneNumber
{
get { return this.phoneNumberValue; }
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged("PhoneNumber");
}
}
}
}
public class Caption
{
private int myInt;
public event EventHandler MyIntChanged;
private void OnMyIntChanged()
{
var handler = this.MyIntChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public int MyInt
{
get
{
return this.myInt;
}
set
{
if (this.myInt != value)
{
this.myInt = value;
this.OnMyIntChanged();
}
}
}
}
So now, in your guage class:
public class Guage
{
private Caption caption;
public Caption Caption
{
get
{
return this.caption;
}
set
{
if (this.caption!= value)
{
this.caption= value;
this.caption.MyIntChanged += new EventHandler(caption_MyIntChanged);
}
}
}
private void caption_MyIntChanged(object sender, EventArgs e)
{
//do what you gotta do
}
}