1

I have the following Singleton Pattern for the ViewModel of my Options:

    private static volatile GeneralOptionsViewModel instance;
    private static object syncRoot = new object();
    /// <summary>
    /// threadsave singleton
    /// </summary>
    public static GeneralOptionsViewModel Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new GeneralOptionsViewModel();
                }
            }

            return instance;
        }
    }

In my XAML I have a color picker from the extended toolkit package:

<xctk:PropertyGridEditorColorPicker Background="Transparent" Name="face" 
Margin="5,0" Width="50" BorderBrush="#32FFFFFF" BorderThickness="1" 
SelectedColor="{Binding FaceRectColor, Mode=OneWayToSource, 
UpdateSourceTrigger=PropertyChanged}"/>

As you can see it is bound to FaceRectColor property of the GeneralOptionsViewModel class which is defined like follows. Within the setter there is a conversion to MCvScalar (also a property of the same class), the format I later need for my application:

    public Color FaceRectColor
    {
        get
        {
            return faceRectColor;
        }
        set
        {
            if (faceRectColor != value)
            {
                faceRectColor = value;
                FaceRectColorScalar = new MCvScalar(value.B, value.R, value.G, value.A);
                SetProperty(ref faceRectColor, value);
            }
        }
    }

My problem now is, that the binding works and also the correct values are written to the variable, however when I call the singleton with the property from a different class - and from a different thread - it always shows zero for all color channels. However, if I break the program directly within the singleton class I can see the correct values. AFAIK the singleton should be threadsafe, so I'm looking for the reason of this behavior. My guess is some threading issue, since other properties from the singleton class are displayed correctly, but they are only called in the main thread.

Edit: In my case all property values of the singleton class are set before the worker thread is active. This means no changes during the time the worker thread is active.

Edit II: Here is the complete project for code evaluation. In the class CameraViewModel in line 202 is the relevant call for a function, where I want to pass the values from the singleton.

Roland Deschain
  • 2,211
  • 19
  • 50
  • What happens if you write it like this? `public static GeneralOptionsViewModel Instance { get; } = new GeneralOptionsViewModel();` – Clemens Nov 01 '18 at 09:33
  • @Clemens will have to try it out, but I won't be able to code until later today. Do you think the thread lock is the issue? – Roland Deschain Nov 01 '18 at 09:37
  • No idea, but as pointed out in the answer, you shouldn't be a using a singleton at all. – Clemens Nov 01 '18 at 09:43
  • @RolandDeschain: Is FaceRectColor a property of the GeneralOptionsViewModel class? – mm8 Nov 01 '18 at 10:50
  • @mm8 yes both FaceRectColor and FaceRectColorScalar are properties of the GeneralOptionsViewModel class. I try to access them from the class which starts the worker thread via `GeneralOptionsViewModel.Instance.FaceRectColorScalar` – Roland Deschain Nov 01 '18 at 11:19
  • @RolandDeschain: Please post your "worked" thread code and all relevant parts of the GeneralOptionsViewModel class. – mm8 Nov 01 '18 at 12:27
  • @mm8 made an edit with an link to the project code. Please keep in mind, that this is all WIP, so not the most beautiful code atm – Roland Deschain Nov 01 '18 at 12:39
  • @RolandDeschain: Your "singleton" contains a public constructor...Where are you trying to access its properties? – mm8 Nov 01 '18 at 12:42
  • @mm8 yeah, just realised that, my mistake...but shouldn't be the problem of my question. Access is in CameraViewModel, line 202. – Roland Deschain Nov 01 '18 at 12:53
  • @RolandDeschain: Make sure that the property has been set by the time the CaptureCameraFrame() method is called. – mm8 Nov 01 '18 at 12:59
  • @mm8 well that's the thing...the property is set earlier for sure. In the GUI I select a color, then press the 'Start Capture' button which starts the thread that uses the CaptureCameraFrame() method. By that time the FaceRectColor is already set. Of course I will have to disable the color picker in the future once the capturing has started.. – Roland Deschain Nov 01 '18 at 13:06
  • OK, I realised that the whole thing was my error making the viewmodel an singleton, because now that @mm8 pointed out that the constructor needs to be private, of course I can't instance it from my view xaml. So the whole problem most probably lies with the instancing of the class. I will ditch the Singleton pattern for this viewmodel and mark nvoigt's reply as answer. – Roland Deschain Nov 01 '18 at 13:14
  • @RolandDeschain: You are not binding to the singleton. See my answer for a solution. It should be enough to just set the DataContext in the view properly to get this working. – mm8 Nov 01 '18 at 13:15

2 Answers2

1

When your property changes and it does so on a different thread, the calls that are made to notify everybody (in particular the UI) of this change are running in the calling thread. Accessing the UI in a thread that is not the UI thread is a bad idea. It might sometimes work. But it will fail sooner or later.

The solution to your current problem is changing the property in the UI thread.

That said, maybe you should think about whether you need a Singleton. That's a huge red flag that something is wrong with the structure of your program. You don't need a Singleton. Nothing bad would happen if some other context had a second settings viewmodel. You seem to want a Singleton because it's so nice and easy to have a global variable. That is the drawback of a Singleton. It's disadvantage that you buy into because you need something from this pattern. If you find you are using this pattern only because it's disadvantage gives you an excuse to have a global variable, you are doing patterns wrong. It's an anti-pattern.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • For you first point: My problem (at least for now) is that the property is in the main thread and updates correctly. However, after for example I choose my color, the viewmodel for the general options are updated correctly, then via a button I start some work in a new thread. This new thread now needs the value from the singleton class, but for some reason it gets default values (although no exception is thrown. – Roland Deschain Nov 01 '18 at 09:33
  • For the second point: I didn't know that in general singleton should be avoided. I wanted to use one, because in this case I only have ONE place for general options, and the values set here are used all over the program, therefore a singleton seemed valid in this case to me. Alternative I could make the current singleton class a reference property in all other classes where I need values from it, but that seemed more complicated, less readable and more work to me.. – Roland Deschain Nov 01 '18 at 09:36
  • And to make the first comment more clear: My point is, there are no value updates, while the worker thread is active. All values in the singleton are set before that. I will change that in the OP. – Roland Deschain Nov 01 '18 at 09:40
  • Did you make a copy of the values to the thread, or is your thread simply accessing the singleton? – nvoigt Nov 01 '18 at 09:41
  • Actually I tried to directly access the singleton values. Another thing I tried was to make additional properties within the class with the worker thread, where the getter points to the singletons properties, but that's just more or less the same, is it? – Roland Deschain Nov 01 '18 at 09:44
  • You did not post the actual properties, so it's guesswork here, but dependency properties don't play well with threads, because they are for UI usage and expect to be used in the UI thread. So it should work if you make a copy of the actual values of dependency properties and the pass those to the thread. – nvoigt Nov 01 '18 at 09:47
  • 1
    ok, if I have to make copies anyway and cannot directly access the singleton property values I guess I just ditch it altogether and test how that works. – Roland Deschain Nov 01 '18 at 09:51
1

Your "singleton" contains a public constructor which effectively makes it a non-singleton. And you are not binding to the singleton in your GeneralOptionsView.

If you really want GeneralOptionsViewModel to be a singleton, you should implement it like this:

public sealed class GeneralOptionsViewModel : ViewModelBase
{
    private static readonly GeneralOptionsViewModel _instance = new GeneralOptionsViewModel();
    private GeneralOptionsViewModel()
    {
        GetAvailableCameraList();
        DetectorTypeList = new List<string>() { "Cascade Detector" };
        SelectedDetectorTypeIndex = 0;
    }

    public static GeneralOptionsViewModel Instance => _instance;

    //...
}

You should then set the DataContext of your view to the singleton:

<Grid DataContext="{Binding Source={x:Static local:GeneralOptionsViewModel.Instance}}">
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks for this answer. I take it however, that - as pointed out above - it is not recommended to make it a singleton? – Roland Deschain Nov 01 '18 at 13:38
  • In general, you don't want to implement a view model as a singleton but this is not really related to your original question. – mm8 Nov 01 '18 at 13:41
  • OK, thank you for the patience. Made the changes now, removing the singleton pattern and referencing the GeneralOptionsViewModel class correctly and now it is working as intended. – Roland Deschain Nov 01 '18 at 13:49
  • @RolandDeschain: That's great. It still doesn't answer your original question though. – mm8 Nov 01 '18 at 13:50