Reputation: 199
I'm currently learning WPF, DataContexts & DataBinding. My goal is to have a Taskbar task (using NotifyIconWpf) that has a continuous thread running the background to monitor a network.
I've managed to get a UI element (shown in screenshot) bound to the ProgramClock class, but it does not update when the ProgramClock changes, most likely because something in the INotifyPropertyChanged parameters are wrong.
The closest similar problem I've found is UI not being updated INotifyPropertyChanged however I haven't been able to figure out what to change the DataPath in the XAML, or how to make INotifyPropertyChanged work properly.
Note that the BackgroundWorker thread successfully updates the App's static ProgramClock (checked with a separate WinForm) and that time is initially loaded in the WPF, so it's probably the PropertyChanged not being called properly.
ProgramClock
public class ProgramClock : INotifyPropertyChanged
{
private DateTime _myTime;
public event PropertyChangedEventHandler PropertyChanged;
private ClockController clockController;
public ProgramClock()
{
this._myTime = DateTime.Now;
clockController = new ClockController();
MessageBox.Show("created new clock");
}
public DateTime MyTime
{
get
{
return this._myTime;
}
set
{
if (_myTime == value) return;
_myTime = value;
//System.Windows.Forms.MessageBox.Show(PropertyChanged.ToString());
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(_myTime.ToString()));
}
}
public string MyTimeString
{
get { return this._myTime.ToString(); }
}
public void UpdateTime()
{
this.MyTime = DateTime.Now;
}
}
Bubble CS
public partial class InfoBubble : System.Windows.Controls.UserControl
{
public InfoBubble()
{
InitializeComponent();
this.DataContext = App.ClockBindingContainer;
}
}
Bubble XAML
<UserControl x:Class="FileWatcher.Controls.InfoBubble"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Border
Background="White"
BorderBrush="Orange"
BorderThickness="2"
CornerRadius="4"
Opacity="1"
Width="160"
Height="40">
<TextBlock
Text="{Binding Path=MyTimeString}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</UserControl>
Main app
public partial class App : System.Windows.Application
{
private TaskbarIcon tb;
private ResourceDictionary _myResourceDictionary;
public static ProgramClock _programClock = new ProgramClock();
private void Application_Startup(object sender, StartupEventArgs e)
{
NotifIconStarter();
}
public static ProgramClock ClockBindingContainer
{
get { return _programClock; }
}
}
Upvotes: 0
Views: 3368
Reputation: 21306
You are not notifying the change in the property you are binding to (which is MyTimeString
), so WPF knows MyTime
changes, but does MyTimeString
change too? was never notified.
Try to change this:
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(_myTime.ToString()));
To this:
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MyTimeString")); // Not MyTime!
Upvotes: 1
Reputation: 3558
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(_myTime.ToString()));
You should pass property name:
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MyTime");
However I suggest you to get PostSharp library - it has nice features that enable you to write normal properties and "decorate it by attribute" with automatic raising of PropertyChanged
. If you do not want to use PostSharp
at least create some method like:
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
and call it in your setter. ([CallerMemberName]
is C# 5.0 feature which automatically pass "caller" name (in setter it will pass property name )
Upvotes: 2
Reputation: 7747
One problem is in your invocation of the PropertyChanged
event. You need to pass the name of the property that is changing to the PropertyChangedEventArgs
not the new value.
So use:
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MyTime"));
instead of:
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(_myTime.ToString()));
However, you are actually binding to another property - MyTimeString
.
Ultimately the property you are binding to needs to raise the event.
Upvotes: 5