0

The Structure

I have a simple form that fires off a timer that checks for updates pretty regularly. The constructor of the form that starts on load looks like so:

public MainWindow()
    {
        InitializeComponent();
        otherWindow = new TheOtherWindow();

        if (Meta.hasUpdate)
        {
            updateImage.Source = new BitmapImage(new Uri("/MyProject;component/Images/updateTrue.gif", UriKind.Relative));
        }

        Thread updateMonitor = new Thread(() =>
        {
            UpdateManager updater = new UpdateManager();
            updater.StartUpdateMonitor();
        });

        updateMonitor.IsBackground = true;
        updateMonitor.Start();
    }

The Meta class contains some very basic information, storing various strings that are referenced in several places but are sometimes updated. Among that structure is this:

class Meta
{
    ...
    private static bool hasUpdate = false;

    public static bool GetHasUpdate()
    {
        return hasUpdate;
    }

    public static void SetHasUpdate(bool value)
    { 
        hasUpdate = value;
    }
}

The other piece is the UpdateManager class, which includes this a small routine to check for an update every 5 minutes.

class UpdateManager
{
    Timer timer;

    public void CheckForUpdates(Object source, ElapsedEventArgs e)
    {

        if (!isUpToDate())
        {
            timer.Stop();
            Meta.SetHasUpdate(true);

            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                MessageBox.Show("A new update is now available!);
            }));
        }
    }

    public void StartUpdateMonitor()
    {
        float updateInterval = 300000;

        timer = new Timer(updateInterval); // Milliseconds between checks.

        timer.Elapsed += CheckForUpdates;
        timer.AutoReset = true;
        timer.Enabled = true;
    }
}

The Problem

In short, I want to fire off an event whenever Meta.SetHasUpdate() is reached that then broadcasts this to all the forms in the application with the goal of changing a small icon to indicate that an update is available.

My attempts to do so have ended with me learning that implementing INotifyPropertyChanged does not play nice with Static members. This was my attempt in implementing that...

class Meta : INotifyPropertyChanged

{
    ...
    private static bool hasUpdate = true;

    public static bool GetHasUpdate()
    {
        return hasUpdate;
    }

    public static void SetHasUpdate(bool value)
    { 
        hasUpdate = value;
        NotifyPropertyChanged();
    }

    private static void NotifyPropertyChanged()
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(null, new PropertyChangedEventArgs("hasUpdate"));
        }
    }
}

Since these members need to be read back from multiple forms, I can't make them not static without passing an object around a lot, which I don't want to do.

How do fire off an event that multiple forms can receive from the Meta class in this case? Do I need to consider a different structure, or am I misunderstanding INotifyPropertyChanged?

mkautzm
  • 1,086
  • 2
  • 18
  • 34
  • Try a singleton Meta, use the Instance to get the object, and access its props. They can use INotify or propdp`s - if propdps you can get rid of the updater as they push theire changes via wpf 's bindningsystem to whoever bound to them – Patrick Artner Dec 01 '17 at 22:18
  • Static property change is doable but it’s different, and you have to get all the details right: https://stackoverflow.com/a/41823852/424129 – 15ee8f99-57ff-4f92-890c-b56153 Dec 01 '17 at 22:57

2 Answers2

1

While there can be many ways to solve this, (think DI of your Meta class into each of your pages' ViewModels and react to INPC..that would be preferred over singleton approach), one approach to consider is using Messaging rather than Events. Messages, (offered in most MVVM frameworks), are great way to communicate between loosely coupled components. If you leverage an MVVM Library like MVVM Light, then this is very easy as it includes a Messenger implementation. The main advantage of this approach is that the forms that you want to receive the notification don't necessarily need to hold on to a reference of the source, like you would with an Event based approach.

Simply have all interested forms register for a message, and react accordingly when received.

For example, with MVVM Light, we can take advantage of automatically broadcasting a message when a INPC property has been updated.

 private bool hasUpdate;
 public bool HasUpdate
 {
    {
        return hasUpdate;
    }

    set
    {
        // the last bool param indicates whether or not to broadcast a message to all interested parties. 
        Set(nameof(HasUpdate), ref hasUpdate, value, true);
    }
}

Then in a totally separate / unrelated part of the app, (usually in a ViewModel), we can do this to indicate that we are interested in such an update:

 Messenger.Default.Register<PropertyChangedMessage<bool>>(this, m => ReceiveHasUpdatedMessage(m));

and then in the receiving lambda:

 private void ReceiveHasUpdatedMessage(PropertyChangedMessage<bool> m)
 {
    // react accordingly.      
 }

This is just one simple use case of the Messenger that MVVM Light provides.. you can do pretty much anything you want. The premise here is that using this approach decouples interested parties from requiring a hard reference to the emitter.

flyte
  • 1,242
  • 11
  • 18
0

With a combination of everyone's very helpful advice, I've put together the following code. The MVVM solution is above, although I did not test it. If you aren't using MVVM though, this is what I did.

The UpdateManager class is the same. Meta has the following structure:

class Meta 
{
    private static bool hasUpdate = false;
    public static event PropertyChangedEventHandler StaticPropertyChanged;

    public static bool GetHasUpdate()
    {
        return hasUpdate;
    }

    public static void SetHasUpdate(bool value)
    { 
        hasUpdate = value;
        StaticNotifyPropertyChanged();
    }

    private static void StaticNotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
    }
}

Then, for any form I want to be aware of this kind of a change, I bolt in the following code:

public partial class SomeForm : Window
{
    public SomeForm()
    {
        InitializeComponent();

        Meta.StaticPropertyChanged += MethodThatTriggersOnUpdate;
        ...
    }

    private void MethodThatTriggersOnUpdate(object sender, EventArgs e)
    {
        myImage.Dispatcher.BeginInvoke(
            (Action)(() => myImage.Source = new BitmapImage(
            new Uri("/MyProject;component/Images/myNewImage.gif", UriKind.Relative))));
    }
    ...
}
mkautzm
  • 1,086
  • 2
  • 18
  • 34