0

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.

enter image description here

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; }
    }
}
Community
  • 1
  • 1
SpaceSteak
  • 224
  • 2
  • 15

3 Answers3

5

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.

Daniel Kelley
  • 7,579
  • 6
  • 42
  • 50
  • Thanks for the quick response. However, that didn't solve the problem. :( The UI's DataBinding still doesn't get updated. – SpaceSteak Aug 06 '14 at 14:27
  • 1
    @SpaceSteak Whenever you are having binding issues in WPF check your output window in VS (look for it in the view menu). If there are any errors it will be mentioned there. Is there anything? – MasterMastic Aug 06 '14 at 14:30
  • 1
    @SpaceSteak have you changed Text="{Binding Path=MyTimeString}" to Text="{Binding Path=MyTime}" ? There is no call to PropertyChanged(.."MyTimeString") so it obviously won't update. – fex Aug 06 '14 at 14:32
  • that worked, @Fex. Thank you! :D I had MyTimeString in the App class since I was setting App as the datacontext, but clearly that wasn't the right approach. I'll mark your other answer as Accepted. – SpaceSteak Aug 06 '14 at 14:37
  • 2
    @SpaceSteak why isn't this the answer? O_o – Kcvin Aug 06 '14 at 16:09
2
   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 )

fex
  • 3,488
  • 5
  • 30
  • 46
  • Thanks for the quick response. I tried moving the property change to its own function and changing it to "MyTime" however it's still not working. On VS2010 so no C#5.0. – SpaceSteak Aug 06 '14 at 14:34
  • @SpaceSteak Start debugging your application, click view, output and check are there any Binding errors - if so write them here – fex Aug 06 '14 at 14:37
1

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!
MasterMastic
  • 20,711
  • 12
  • 68
  • 90