2
votes

I’m trying to achieve something that is conceptually quite simple but can’t seem to get it working.

I have a class called c1 it has 2 dependency properties in it an integer I and a string S. It implements INotifiyPropertyChanged.

public class c1: INotifyPropertyChanged 
{
    private int i;
    public int I { get { return i; } set { i = value; if(PropertyChanged != null) PropertyChanged(this,new PropertyChangedEventArgs("I")); } }
    private string s;
    public string S { get { return s; } set { s = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("S")); } }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

This class is referenced by a Silverlight user control SUC that also implements INotifiyPropertyChanged as a dependency property C, with a PropertyChangedCallback etc. As seen below.

public partial class SUC : UserControl, INotifyPropertyChanged
{

    public c1 C
    {
        get { return (c1)GetValue(CProperty); }
        set { SetValue(CProperty, value); }
    }
    public static readonly DependencyProperty CProperty =
        DependencyProperty.Register("C", typeof(c1), typeof(SUC), new PropertyMetadata(new c1(), new PropertyChangedCallback(c1Changed)));


    private static void c1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        SUC s = obj as SUC;
        if (s != null)
            s.CChanged((c1)e.NewValue);
    }

    public void CChanged(c1 c)
    {
        C = c;
        if(PropertyChanged!=null)
            PropertyChanged(this,new PropertyChangedEventArgs("C"));
    }
    public SUC()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private void bclick(object sender, RoutedEventArgs e)
    {
        C.S = C.S + " Clicked";
        MessageBox.Show(C.I.ToString() + " - " + C.S);
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

In my main page which also implements INotifiyPropertyChanged I have an instance of c1 and of SUC.

public partial class MainPage : UserControl, INotifyPropertyChanged
{


    public c1 MC
    {
        get { return (c1)GetValue(MCProperty); }
        set { SetValue(MCProperty, value); }
    }

    public static readonly DependencyProperty MCProperty =
        DependencyProperty.Register("MC", typeof(c1), typeof(MainPage), new PropertyMetadata(new c1()));


    private static void MCChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MainPage mp = d as MainPage;
        if (mp != null)
            mp.MCChanged();
    }

    public void MCChanged()
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("MC"));
    }

    public MainPage()
    {
        InitializeComponent();
        MC.S = "ssss";
        this.DataContext = this;
    }


    public event PropertyChangedEventHandler PropertyChanged;

}

I want to set the C property of the SUC user control via XAML. Like so

local:SUC x:Name="suc" C="{Binding MC, Mode=TwoWay}"

This works well in the c# code behind but not in XAML. The reason I need it in XAML is because I want to bind a collection of c1’s to SUC’s in a DataTemplate.

Any working examples with downloadable code would be most appreciated.

3

3 Answers

1
votes

It's a simple little bug in the constructor of the SUC class:

    public SUC()
    {
        InitializeComponent();
        this.DataContext = this; //this line shouldn't be here, delete and it will work
    }

That means the DataContext of SUC control is itself instead of the MainPage class which is what it needs to be in order to bind to MainPage.MC (the SUC class doesn't have an MC property).

Also, and I realise most of these were you probably just trying to get it to work, but MC does not need to be a DP, you don't need the 'C=c;' line in the SUC, and I wouldn't use the MainPage control class as a datacontext class as well, create another class to bind the DataContext to.

0
votes

The problem seems to be that you set the DataContext of the UserControl after you load the XAML. Either set it before the XAML is loaded (i.e. before InitializeComponent), or even better, set it in the XAML as such:

<local:MainPage ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
    ....
</local:MainPage>

The RelativeSource binding specifies that the DataContext of your MainPage should be itself, which seems to be what you want. This then eliminates the assignment of DataContext in code-behind, which is always a good thing in WPF/Silverlight.

Hope that helps.

0
votes

The DataContext of the UserControl's Controls can be different from the UserControl itself or the UserControl's Parent "Form" (or Parent Page, UserControl). You have to set the Binding in the Code Behind. See this post for more information: Silverlight UserControl Custom Property Binding

Also, You may want to create a Silverlight Control instead of a Silverlight UserControl