TextBlock data-bound to Singleton not updating in WPF - c#

I apologize for the newbie question, but I am struggling with this problem. I have the following TextBlock defined:
<TextBlock Text="{Binding Source={x:Static local:DeviceManager.Instance},
Path=Player.CurrentArtist}"></TextBlock>
The DeviceManager is a singleton that functions as a facade for other classes. For example, Player is a property of type IPlayer which represents an music-playing application. I would like the TextBlock to display the artist that is currently playing, which is periodically updated in the Player.CurrentArtist property.
Unfortunately, I cannot get the TextBlock to update when the CurrentArtist property updates. Both the DeviceManager and the IPlayer implement INotifyPropertyChanged, but when I step through the application, the DeviceManager does not have an event handler attached to it.
Does anyone have a suggestion for how to update the text block while preserving the singleton-facade?
Here is the code for the INotifyPropertyChanged members in both the DeviceManager and the IPlayer subclass:
public sealed class DeviceManager : INotifyPropertyChanged
{
// Singleton members omitted
public IPlayer Player
{
get { return player; }
set
{
this.player = value;
player.PropertyChanged += new PropertyChangedEventHandler(device_PropertyChanged);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void device_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, e);
}
}
#endregion
}
class MediaPlayer : IPlayer
{
private string artist;
private string title;
public event PropertyChangedEventHandler PropertyChanged;
public void Play(string artist, string title)
{
this.artist = artist;
this.title = title;
OnPropertyChanged("Player:Song");
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
public string CurrentTitle
{
get { return title; }
}
public string CurrentArtist
{
get { return artist; }
}
}

The problem is that WPF is never notified of the value of the CurrentArtist property changing. You can either implement a private setter for the CurrentArtist property, which will trigger the PropertyChanged event, or trigger a PropertyChanged event for the CurrentArtist property in MediaPlayer.Play().

WPF only responds to PropertyChanged if the name you pass in (i.e. right now "Player:Song") is the same as the property you're bound to - change the PropertyChanged to "CurrentArtist" and you'll see it update properly.

You are not raising the PropertyChanged event, what you need is:
public sealed class DeviceManager : INotifyPropertyChanged
{
// Singleton members omitted
public IPlayer Player
{
get { return player; }
set
{
this.player = value;
OnPropertyChanged(this, new PropertyChangedEventArgs("Player"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, e);
}
}
#endregion
}

How does the UI know when you change the Player property? From that code it does not look like it raises PropertyChanged to me. Can you post a complete working sample of the problem? Otherwise we're forced to just guess.

Related

Fody.PropertyChanged: RaisePropertyChanged does not work for DependsOn Properties

For building the WPF application I used VS2022 (V17.4.4) and NuGet Fody.PropertyChanged Package (V4.1.0).
If a property is set directly, the PropertyChanged event will be raised for the property itself and all the [DependsOn] properties as well.
But if only the underlying field of the property is set and the PropertyChanged event is raised later manually by call of RaisePropertyChanged(nameof(Property)) the [DependsOn] properties will not raise any PropertyChanged event.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace FodyPropertyChanged
{
[PropertyChanged.AddINotifyPropertyChangedInterface]
public class MainWindowViewModel : INotifyPropertyChanged
{
private DeviceStates _DeviceState;
private event PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { _propertyChanged += value; }
remove
{
if (_propertyChanged != null)
_propertyChanged -= value;
}
}
public DeviceStates DeviceState
{
get { return _DeviceState; }
set { _DeviceState = value; }
}
[PropertyChanged.DependsOn(nameof(DeviceState))]
public bool IsBusy
{
get { return DeviceState == DeviceStates.Working; }
}
public void SetDeviceStateTo(DeviceStates deviceState)
{
DeviceState = deviceState;
}
public void SetDeviceStateToAndRaisePropertyChangedAfterwards(DeviceStates deviceState)
{
_DeviceState = deviceState;
RaisePropertyChanged(nameof(DeviceState));
}
private void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
If I call SetDeviceStateTo(...) the PropertyChanged event will be raised for DeviceState and IsBusy (GUI will be updated, correctly). But if I call SetDeviceStateToAndRaisePropertyChangedAfterwards(...) the PropertyChanged event will be raised for DeviceState, only (IsBusy bindings will not be updated).
Any idea?
Like canton7 and BionicCode said:
[DependsOn] is only working for the setter not for events. So it is a known limitation and not a bug.

INotifyPropertyChanged does't work when field of property change internally

I try to binding textblock usercontrol with property of my class, but it only works at initial stage, I have implement IPropertyChnaged in my class.
In my class, _Feedbackpos (field of property) would change in background, I don't know how to solve this problem.
my class
public class TestControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyname)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
private double _Feedbackpos;
public double Feedbackpos
{
get
{
return _Feedbackpos;
}
set
{
_Feedbackpos = value;
NotifyPropertyChanged("Feedbackpos");
}
}
//it's a callback function, it would excute when detect feedback position of controller change
private void ReadFeedbackpos()
{
_Feedbackpos = Controller.Read();
}
}
application windows
TestControl TestDll = new TestControl();
Binding BindingTxtBlk = new Binding(){Source= TestDll, Path = new Property("Feedbackpos")};
FeedbackPosTxtBlk.Setbinding(Textblock.TextProperty,BindingTxtBlk);
Change the function ReadFeedbackpos() to
private void ReadFeedbackpos()
{
Feedbackpos = Controller.Read();
}
Otherwise NotifyPropertyChanged("Feedbackpos"); will never get called.

Bind a Class property to controls Winform - C# [duplicate]

What is the best way to bind a property to a control so that when the property value is changed, the control's bound property changes with it.
So if I have a property FirstName which I want to bind to a textbox's txtFirstName text value. So if I change FirstName to value "Stack" then the property txtFirstName.Text also changes to value "Stack".
I know this may sound a stupid question but I'll appreciate the help.
You must implement INotifyPropertyChanged And add binding to textbox.
I will provide C# code snippet. Hope it helps
class Sample : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
InvokePropertyChanged(new PropertyChangedEventArgs("FirstName"));
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
#endregion
}
Usage :
Sample sourceObject = new Sample();
textbox.DataBindings.Add("Text",sourceObject,"FirstName");
sourceObject.FirstName = "Stack";
A simplified version of the accepted answer that does NOT require you to type names of properties manually in every property setter like OnPropertyChanged("some-property-name"). Instead you just call OnPropertyChanged() without parameters:
You need .NET 4.5 minimum.
CallerMemberName is in the System.Runtime.CompilerServices namespace
public class Sample : INotifyPropertyChanged
{
private string _propString;
private int _propInt;
//======================================
// Actual implementation
//======================================
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
//======================================
// END: actual implementation
//======================================
public string PropString
{
get { return _propString; }
set
{
// do not trigger change event if values are the same
if (Equals(value, _propString)) return;
_propString = value;
//===================
// Usage in the Source
//===================
OnPropertyChanged();
}
}
public int PropInt
{
get { return _propInt; }
set
{
// do not allow negative numbers, but always trigger a change event
_propInt = value < 0 ? 0 : value;
OnPropertyChanged();
}
}
}
Usage stays the same:
var source = new Sample();
textbox.DataBindings.Add("Text", source, "PropString");
source.PropString = "Some new string";
Hope this helps someone.

Windows 8.1 UI not updating when MVVM property set is called by UI element

My problem is, that the UI isn't updating if they call the setter of the property which they binded to.
Here's a sample to make it clear:
Let's say I have a textbox binded to a property like this.
<TextBox PlaceholderText="Task Name..." FontSize="24"
Text="{Binding TaskName, Mode=TwoWay}" />
And this is my property:
public string TaskName
{
get
{
return _taskName;
}
set
{
_taskName = "something";
RaisePropertyChanged();
}
}
If I write something into the textbox then "something" should appear inside of it, after it loses focus, but there isn't any change. However, if I change the value of the property with code, like this:
TaskName = "something";
Then the change will appear on the UI as well.
Some further information.
This is how I implemented the INotifyPropertyChange interface:
public class ViewModelBase : INotifyPropertyChanged
{
public static Navigator NavigationService;
public static void SetNavigationService(Navigator service)
{
NavigationService = service;
}
protected void GoBack()
{
NavigationService.GoBack();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I really don't know why is it behave like this. I search for it for hours, but can't find anything.
in the setter of the property you need to call
RaisePropertyChanged(x => x.TaskName)

WPF - Implementing System.ComponentModel.INotifyPropertyChanged for Base Class

I'd like to implent the System.ComponentModel.INotifyPropertyChanged interface for a property on a base class, but I'm not quite sure how to hook it up.
Here's the signature for the property I'd like to get notifications for:
public abstract bool HasChanged();
And my code in the base class for handling the change:
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
How do I handle the hookup of the event in the base class without having to call OnPropertyChanged() in each child class?
Thanks,
Sonny
EDIT:
OK... so I think that when the value for HasChanged() changes, I'm supposed to call OnPropertyChanged("HasChanged"), but I'm not sure how to get that into the base class. Any ideas?
Is this what you are after?
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//make it protected, so it is accessible from Child classes
protected void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Notice the OnPropertyChanged accessible level is protected. And then in your concrete class or child classes, you do:
public class PersonViewModel : ViewModelBase
{
public PersonViewModel(Person person)
{
this.person = person;
}
public string Name
{
get
{
return this.person.Name;
}
set
{
this.person.Name = value;
OnPropertyChanged("Name");
}
}
}
EDIT: after reading the OP question again, I realize that he does not want to call the OnPropertyChanged in the child class, so I am pretty sure this will work:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool hasChanged = false;
public bool HasChanged
{
get
{
return this.hasChanged;
}
set
{
this.hasChanged = value;
OnPropertyChanged("HasChanged");
}
}
//make it protected, so it is accessible from Child classes
protected void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
and in child class:
public class PersonViewModel : ViewModelBase
{
public PersonViewModel()
{
base.HasChanged = true;
}
}

Categories