-1

In my application I'm using a usercontrol called "ChannelControls" which I instance 6 times, on the mainwindow.

public partial class ChannelControls : UserControl
{
    CMiXData cmixdata = CMiXData.Instance;
    public ChannelControls()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public static readonly DependencyProperty ChannelSpriteCountProperty =
    DependencyProperty.Register("ChannelSpriteCount", typeof(string), typeof(ChannelControls), new PropertyMetadata("1"));
    [Bindable(true)]
    public string ChannelSpriteCount
    {
        get { return (string)this.GetValue(ChannelSpriteCountProperty); }
        set { this.SetValue(ChannelSpriteCountProperty, value); }
    }

I'm making using a custom class called cmixdata to hold all the data for my application (it will contains different properties with List of string, double etc...). The ChannelControls will contains many sliders, button and other usercontrols but at the moment I'm trying to bind just one of them.

Here is one part of this custom class that will hold the data, it has a private constructor as I need to access it from anywhere :

[Serializable]
public class CMiXData : INotifyPropertyChanged
{
    private static CMiXData _instance = null;
    public static CMiXData Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new CMiXData();
            }
            return _instance;
        }
    }
    private CMiXData() { } //prevent instantiation from outside the class


    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
        MessageBox.Show(propertyName);
    }


    private List<string> _SpriteCount = new List<string>(new string[] {"1", "1", "1", "1", "1", "1"});
    public List<string> SpriteCount
    {
        get { return _SpriteCount; }
        set
        {
            if(_SpriteCount != value)
            {
                _SpriteCount = value;
                RaisePropertyChanged("SpriteCount");
            }
        }
    }

And here is how I'm trying to bind the channelcontrol property ChannelSpriteCount to my singleton class : cmixdata.

<CMiX:ChannelControls x:Name="Layer0" Tag="0" Visibility="Visible" ChannelSpriteCount="{Binding SpriteCount[0], Mode=TwoWay}"/>

On the main usercontrol, which ChannelControls is instanced, the datacontext is set this way :

public partial class CMiX_UI : UserControl
{
    BeatSystem beatsystem = new BeatSystem();
    CMiXData cmixdata = CMiXData.Instance;

    public CMiX_UI()
    {
        InitializeComponent();
        this.DataContext = cmixdata;
    }

And on the xaml side :

<UserControl
        x:Class="CMiX.CMiX_UI"
        DataContext="{x:Static CMiX:CMiXData.Instance}"

But for some reason the property in cmixdata is not updated and always has the default value...

lecloneur
  • 424
  • 5
  • 20
  • Are `CMiX:Counter` and `ChannelControls` supposed to be the same UserControl class? – Clemens Sep 29 '17 at 10:02
  • CMiX:Counter is within the ChannelControls – lecloneur Sep 29 '17 at 10:04
  • Besides that `UpdateSourceTrigger=PropertyChanged` is pointless, the Binding should work, because you've set the DataContext of the ChannelControls to an instance of CMiXData, and that DataContext value should be inherited by child elements. You may try to set the DataContext before calling InitializeComponent. – Clemens Sep 29 '17 at 10:08
  • However, in general you should never explicitly set the DataContext of a UserControl, for the reasons mentioned e.g. [here](https://stackoverflow.com/questions/27834271/binding-to-custom-control-inside-datatemplate-for-itemscontrol/27834461#27834461) or [here](https://stackoverflow.com/a/40184402/1136211). – Clemens Sep 29 '17 at 10:09
  • What type is Counter.Count? – mm8 Sep 29 '17 at 10:13
  • Counter is a usercontrol, count is its property of type string that I'd like to bind. – lecloneur Sep 29 '17 at 10:14
  • Problem I know is that, there are 6 ChannelControl, so in cmixdata the list contains 6 elements. But how to bind to a specific element, in the example I show above I'm binding to the property, not to a specific element. – lecloneur Sep 29 '17 at 10:15
  • I have 6 instance of ChannelsControl, each one of them contains one instance of CMiX Counter in this example. So, you think I should bind a ChannelControls property to my custom class, and not the CMiX counter directly to this class right ? – lecloneur Sep 29 '17 at 10:25
  • Although it's the same instance and hence doesn't matter much, you should not set the DataContext twice. Do it **either** in XAML **or** in code behind. – Clemens Sep 30 '17 at 09:46
  • I did modify as you suggested, but cmixdata remains untouched for some reason. DataContext is only set in the XAML this time and ChannelControls as its binding mode set to "TwoWay". Would it be the OnPropertyChanged not firing somehow ? – lecloneur Sep 30 '17 at 09:53
  • "cmixdata remains untouched", no idea what that means. Are you expecting to somehow get notified when an element in the SpriteCount collection changes? Then you should replace `string` elements by instances of a view model item class that itself implements INotifyPropertyChanged. But's that far beyond the scope of this question. You should accept the answer here and ask a new question. Anything else is just confusing. – Clemens Sep 30 '17 at 10:35
  • Yes that's why I'm looking for I think. Thanks for the help anyway @Clemens, I appreciated. – lecloneur Sep 30 '17 at 10:40

1 Answers1

2

A UserControl should never have an "own" instance of a view model. Instead, it should have dependency properties that are bound to properties of an "external" view model.

Your ChannelsControl would declare a property like this (where I suppose that string is not an appropriate type for a count):

public partial class ChannelsControl : UserControl
{
    public static readonly DependencyProperty SpriteCountProperty =
        DependencyProperty.Register(
            nameof(SpriteCount), typeof(string), typeof(ChannelsControl));

    public string SpriteCount
    {
        get { return (string)GetValue(SpriteCountProperty); }
        set { SetValue(SpriteCountProperty, value); }
    }

    ...
}

In ChannelsControl's XAML, you would bind it like this:

<CMiX:Counter Count="{Binding SpriteCount,
                      RelativeSource={RelativeSource AncestorType=UserControl}}"/>

You would now use your UserControl like shown below, where you bind the Count property to a view model in the DataContext like this:

<Window.DataContext>
    <local:CMiXData />
</Window.DataContext>
...

<local:ChannelsControl SpriteCount="{Binding SpriteCount[0]}" ... />

You may now also use ChannelsControl in the ItemTemplate of an ItemsControl like this:

<ItemsControl ItemsSource="{Binding SpriteCount}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:ChannelsControl SpriteCount="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

EDIT: Set the Window's DataContext to your view model singleton instance like this:

<Window ... DataContext="{x:Static local:CMiXData.Instance}" >

Or in code behind, in the MainWindow constructor:

DataContext = CMiXData.Instance;
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Yes I tried that but ChannelControls will be duplicated 6 times, and they won't bind to the first element. ChannelControls 1 will bind to the first, ChannelControls 2 will bind to the second and so on... – lecloneur Sep 29 '17 at 10:19
  • I understand your answer, thanks for that, but supposed I want to dynamically create more instance of ChannelControls, I have no other choice than writing all bindings in code behind right ? – lecloneur Sep 29 '17 at 10:48
  • sorry but just checked today and it's still not working, I cant declare the datacontext in XAML because the constructor is private. I need this class to be accessible everywhere, so it has a private constructor. – lecloneur Sep 30 '17 at 08:43
  • Thanks for your support on this but there must a detail I'm missing somewhere, I've updated my post. – lecloneur Sep 30 '17 at 09:34