2

I am setting the DataContext for my View in the View's Constructor to an instance of my ViewModel, just standard stuff. Shortly thereafter, an UPDATE_RECENT_DOCUMENTS_LIST Event fires from the Event Aggregator which my ViewModel catches correctly. A property is changed and the onPropertyChanged method is called, but it fails as the PropertyChanged event is null.

The very next thing I do is an action to the UI which raises a CREATE_PROJECT Event and the same ViewModel is receiving events, except now, the PropertyChanged event is no longer null and everything works as expected.

Is there a specific amount of time that has to pass after setting the DataContext before it registers to the PropertyChanged Event? Is there an event I can wait for that ensures the PropertyChanged event is not null?

Also, I did not run into this problem using standard .NET events, just after integrating Prism and using the very convenient EventAggregator.

I am showing my code behind of the View and the ViewModel, omitting the View XAML for brevity.

ToolBarView.xaml.cs:

namespace ToolBarModule
{   

public partial class ToolBarView : UserControl
    {                           
        public ToolBarView(ToolBarViewModel toolBarViewModel)
        {
            InitializeComponent();             
            this.DataContext = toolBarViewModel;                                
        }        
    }
}

ToolBarViewModel.cs

namespace ToolBarModule
{

public class ToolBarViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private ToolBarCommands baseCommands;
    private IEventAggregator eventAggregator;

    private KickStartEvent kickStartEvent;
    private SubscriptionToken subscriptionToken;

    private ObservableCollection<IDocumentReference> recentDocuments = new ObservableCollection<IDocumentReference>();

    private ActionCommand newTest;
    private ActionCommand openTest;
    private ActionCommand saveTest;        
    private ActionCommand exitApplication;

    public ToolBarViewModel(){}

    public ToolBarViewModel(IEventAggregator eventAggregator)
    {
        this.eventAggregator = eventAggregator;
        baseCommands = new ToolBarCommands(eventAggregator);
        kickStartEvent = eventAggregator.GetEvent<KickStartEvent>();
        subscriptionToken = kickStartEvent.Subscribe(kickStartEventHandler, ThreadOption.UIThread, true, toolBarEventHandlerFilter);            
    }

    public ICommand NewTest
    {
        get
        {
            if (newTest == null)
            {
                newTest = new ActionCommand(baseCommands.NewTestAction);
            }
            return newTest;
        }
    }

    public ICommand OpenTest
    {
        get
        {
            if (openTest == null)
            {
                openTest = new ActionCommand(baseCommands.OpenTestAction);
            }
            return openTest;
        }
    }

    public ICommand SaveTest
    {
        get
        {
            if (saveTest == null)
            {
                saveTest = new ActionCommand(baseCommands.SaveTestAction);
            }
            return saveTest;
        }
    }


    public ICommand ExitApplication
    {
        get
        {
            if (exitApplication == null)
            {
                exitApplication = new ActionCommand(baseCommands.ExitApplicationAction);
            }
            return exitApplication;
        }
    }

    public ObservableCollection<IDocumentReference> RecentDocuments
    {
        get
        {
            return recentDocuments;
        }

        set
        {
            recentDocuments = value;
            onPropertyChanged("RecentDocuments");
        }
    }

    private void onPropertyChanged(string propertyChanged)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this,new PropertyChangedEventArgs(propertyChanged));
        }

    }

    private void kickStartEventHandler(KickStartEventsArgs e)
    {
        switch (e.EventType)
        {
            case KickStartEventsArgs.KickStartEventType.CREATE_PROJECT:
                onPropertyChanged("RecentDocuments");
            break;                


            case KickStartEventsArgs.KickStartEventType.UPDATE_RECENT_DOCUMENTS_LIST:
            RecentDocuments.Clear();

            foreach (IDocumentReference recentDocs in e.KickStartTestList)
            {
                RecentDocuments.Add(recentDocs);
            }
            onPropertyChanged("RecentDocuments");
            break;
        }
    }
}

}

estarkey7
  • 63
  • 3
  • 8
  • Did you named your UserControl in XAML ? – Saber Amani Aug 09 '12 at 00:19
  • Provide your UserControl's XAML. – Saber Amani Aug 09 '12 at 00:23
  • Are you creating the ToolBarView yourself or are using Resolve or a RegionManager ?....perhaps you need to wait until the control has been loaded into the Visual tree...i.e. wait till after the Loaded event....you can set the DataContext in your Loaded event if you like – Colin Smith Aug 09 '12 at 00:29
  • Thanks guys for the comments. @colinsmith - I did try to set the DataContext in the loaded event before my post, and that didn't help. I also verified that the event was firing after the DataContext was set, which make me believe that the actual binding happens at >t0 after the DataContext command. – estarkey7 Aug 09 '12 at 17:57

2 Answers2

1

You can also try to set the DataContext of a Grid or an Element below the UserControl. For me it worked.

Example (Doesn't work if you use DependencyProperty):

Code Behind:

public MyUserControl()
{
     InitializeComponent();
     this.DataContext = new { LabelText = "Hello World!" };
}

XAML

<UserControl x:Class="CoolProject.ViewModel.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<Label x:Name="myLabel" Content="{Binding LabelText}"/>

Example 2 (My working code):

Code Behind:

public MyUserControl()
{
     InitializeComponent();
     this.myGrid.DataContext = new { LabelText = "Hello World!" };
}

XAML

<UserControl x:Class="CoolProject.ViewModel.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">

<Grid x:Name="myGrid">
    <Label x:Name="myLabel" Content="{Binding LabelText}"/>
</Grid>

Alwin
  • 786
  • 9
  • 19
0

You have to name your UserControl in XAML and use it in binding. Something like following code:

<UserControl x:Name="uc" >
.
.
.
<TextBox Text="{Binding UserName, Mode=TwoWay, ElementName=uc}"/>

Where uc is a name of your UserControl, and Also try to set DataContext when UserControl loaded.

Hope this help.

Saber Amani
  • 6,409
  • 12
  • 53
  • 88
  • My UserControl is not named, but after naming it, the PropertyChanged event is still null. – estarkey7 Aug 09 '12 at 17:58
  • Did you set ElementName of bindings ? – Saber Amani Aug 09 '12 at 21:44
  • All my bindings work fine, it's just that the PropertyChanged event is null immediately after my control is loaded. What I had to do it delay sending the event around 500ms to give the PropertyChanged event time to bind, and that fixed the problem. Thank you for your contributions Saber! – estarkey7 Aug 10 '12 at 14:00