views:

99

answers:

2

Hi there, I've been trying to resolve this issue for some time. I'm trying to bind a TextBlock's text to a string property using the INotifyPropertyChanged interface. For some reason the PropertyChangedEventHandler is always null when the value of the property is changed thus the target never gets updated... Any suggestions? Code below:

XAML code:

<UserControl x:Class="MoleDashboard.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:datacontrols="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
xmlns:prop="clr-namespace:MoleDashboard"


<UserControl.Resources>
<prop:YearScreen x:Key="YearScreenProps"/>
</UserControl.Resource>

<TextBlock Margin="10 5" x:Name="DataGridLabel" Visibility="Visible" Text="{Binding   YearProperty, Source={StaticResource YearScreenProps}, Mode=OneWay}"/>

Bound property code:

public class YearScreen : INotifyPropertyChanged
{
    private string metricProperty;

    private string yearProperty;

    public event PropertyChangedEventHandler PropertyChanged;

    public YearScreen()
    {
    }

    public string YearProperty
    {
        get { return yearProperty; }
        set { yearProperty = value; this.OnPropertyChanged("YearProperty"); }
    }

    public string MetricProperty
    {
        get { return metricProperty; }
        set { metricProperty = value; this.OnPropertyChanged("MetricProperty"); }
    }


    public void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }


}
A: 

This is based on the comments provided and not just the question above.

Basically the problem is that you are creating updating a second instance of your ViewModel (called a YearScreen in your code) and updating that.

A ViewScreen is already being created and bound to your Xaml, by the inclusion of:

<UserControl.Resources>
    <prop:YearScreen x:Key="YearScreenProps"/>
</UserControl.Resource>

You are then creating a second ViewScreen elsewhere in code (via new ViewScreen()) and updating that, however there is no connection between the 2 ViewScreen instances, so nothing will update in the Xaml page.

One Possible (quick) solution:

Create your ViewScreen as a singleton. That is add a static accessor of type ViewScreen in the class and set it from the constructor.

    public class YearScreen : INotifyPropertyChanged
    {
        private static YearScreen _This;

        public static YearScreen This { get { return _This; } }

[snip]

        public YearScreen()
        {
            _This = this;
        }

The you can access the "single" instance of your ViewScreen from elsewhere using the static singleton accessor e.g.:

YearScreen.This.YearProperty = DateTime.Now.ToString():

There are better patterns for sharing ViewModels than singletons, but that will get you going.

The pattern you started with is ViewFirst creation (the view creates the ViewModel). ModelFirst does the opposite, but is bad as the model knows how it is displayed. Using a controller object to create the View and ViewModel and connect them is a better alternative, but that is then getting quite complicated. Using injection of single instance objects is a better option, but involves a whole load of new concepts. Lookup Silverlight Prism after you solve your current problems.

Enough already
That was me fiddling around, I changed it to "YearProperty" but still nothing!
SilverlightNewbie
@SilverlightNewbie: How are you changing the YearProperty value on that specific instance of your ViewModel?
Enough already
In MainPage_Loaded() I have:
SilverlightNewbie
yearScreen = new YearScreen(); then I have a combo that sets the YearProperty like this: yearScreen.YearProperty = this.CmbBxYearSelector.SelectedItem.ToString();
SilverlightNewbie
@SilverlightNewbie: So basically you are creating a second YearScreen, in code, *completely disconnected from the one created for you in the view XAML* and setting its properties... and expecting the one attached to the view to update. That will never work as they are separate instances. I will update answer shortly to give suggestions.
Enough already
@HiTech Magic: Yep it looks like it, I was never sure how (and where) to set the property value....
SilverlightNewbie
@HiTech Magic: Thanks for your solution and comments. I resolved the problem and most importantly I actually understood why I got it wrong first place :). Many thanks
SilverlightNewbie
A: 

Instead of creating the ViewModel in the resources you should set it into the DataContext of the view from external code.

If you really want to put it in the Resources like that you can get it out of the resources in the code behind Loaded method or in the constructor after the initializecomponent call. Like so:

private YearScreen model;

public MainPage()
{
    this.Loaded += MainPage_Loaded;
    this.InitializeComponent();
}

void MainPage_Loaded(object sender, EventArgs e)
{
    this.model = (YearScreen)this.Resources["YearScreenProps"];
}

Maybe expose it as a property so you can then access it externally. But personally I'd rather create the model externally than pass it into the View instead. Put it into the DataContext.

justin.m.chase
@just in case: That would work if I had a single model on the page...The thing is that I'm really prototyping and trying things out at this stage...What I figured out is that I can do YearScreen yearScreen=this.Resources["YearScreenProps"] and then set the value. Thanks for reply anyway
SilverlightNewbie
Why would having multiple models change things? You just put them into a collection or into a parent object and bind that at the root of the page.
justin.m.chase