Next Generation Emulation banner

Part 3 - New WPF/MVVM Tutorial(C#)

7K views 49 replies 3 participants last post by  @ruantec 
#1 · (Edited)
Now that we got the very basics let's start doing something meaningful ;)

As noticed in my previous tutorial the use of ObservableCollection is recommended everywhere on the net and is indeed one of the first ways to solve a very common problem of the MVVM pattern. The problem is that you actually lose some nice features and also can't actually send update to the control when one of the elements of the collection changed while the elements count remain intact. In order to solve that problem i came to the idea of adding the 'ObservableList'. It basically does the same as the ObservableCollection but it supports everything the ObservableCollection shoud've supported to begin with.

So let's start by using our previous project and do some changes in order to make it look better. I personally like the use of folders to store my logic so our first step is going to be move the classes where they actually belong:

1. Create a folder called 'Behaviours' and drop the EventBehaviour.cs there.
2. Create a folder called 'Interfaces' and drop the 'IManageable.cs' there.
3. Create another folder called 'ViewModels' and drop the MainViewModel.cs, ViewModelBase.cs and ViewModelManager.cs.

Now you can also fix the namespaces if you wish... so now it should look like this:
Text Font Line Document Computer program


Now let's create another folder called 'Common' and a sub-folder called 'Collections' and let's create the OversableList. Here is the new common re-usable class to solve the problem:
Code:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMTutorial.Common.Collections
{
    public interface IObservableList<T> : IList<T>, INotifyCollectionChanged { }

    public class ObservableList<T> : List<T>, IObservableList<T>, INotifyPropertyChanged
    {
        public bool IsNotifying {get; set;}
        public event PropertyChangedEventHandler PropertyChanged;
        //////////////////////////////////////////////////////////////////////////////
        // Notify handling
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        //////////////////////////////////////////////////////////////////////////////
        public ObservableList()
        {
            IsNotifying = true;
            // interface to notify about Count changes.
            CollectionChanged += delegate { OnPropertyChanged("Count"); };
        }

        public ObservableList(IEnumerable<T> collection)
        {
            IsNotifying = true;
            // interface to notify about Count changes.
            CollectionChanged += delegate { OnPropertyChanged("Count"); };
            if (collection != null)
                AddRange(collection);
        }

        public new void Add(T item)
        {
            base.Add(item);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item);
            OnCollectionChanged(e);
        }

        public new void AddRange(IEnumerable<T> collection)
        {
            base.AddRange(collection);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(collection));
            OnCollectionChanged(e);
        }

        public new void Clear()
        {
            base.Clear();
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            OnCollectionChanged(e);
        }

        public new void Insert(int i, T item)
        {
            base.Insert(i, item);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item);
            OnCollectionChanged(e);
        }

        public new void InsertRange(int i, IEnumerable<T> collection)
        {
            base.InsertRange(i, collection);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection);
            OnCollectionChanged(e);
        }

        public new void Remove(T item)
        {
            base.Remove(item);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item);
            OnCollectionChanged(e);
        }

        public new void RemoveAll(Predicate<T> match)
        {
            var backup = FindAll(match);
            base.RemoveAll(match);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, backup);
            OnCollectionChanged(e);
        }

        public new void RemoveAt(int i)
        {
            T backup = this[i];
            base.RemoveAt(i);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, backup);
            OnCollectionChanged(e);
        }

        public new void RemoveRange(int index, int count)
        {
            List<T> backup = GetRange(index, count);
            base.RemoveRange(index, count);
            var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, backup);
            OnCollectionChanged(e);
        }

        public new T this[int index]
        {
            get { return base[index]; }
            set
            {
                T oldValue = base[index];
                var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue);
                OnCollectionChanged(e);
            }
        }

        protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (IsNotifying && CollectionChanged != null)
                try
                {
                    CollectionChanged(this, e);
                }
                catch (System.NotSupportedException)
                {
                    var alternativeEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
                    OnCollectionChanged(alternativeEventArgs);
                }
        }
    }
}

Now that we added that class let's start doing something great now and let's implement a semi-advanced listbox with a picture and the name of the picture in a listbox. How do we do that? in order to accomplish that you need to think first what you need. As mentioned we have the following:

1. Image that is going to hold a picture.
2. A label to hold the name of the picture.

Simply enough right? now... in a codebehind enviroment you are going to start by loading the files then loading the picture and doing some crazy stuff just to put your picture inside your list. Why making your life that complicated? let's start doing it in a different way :). First of all we need a class that is going to represent one item. That class is going to hold a Uri that points to the picture and a string to hold the name of it so let's get started!

Start by creating a class in your MainViewModel.cs or just create another Folder called 'Classes' and store it there if you wish. In this particular case i'm going to go straight ahead and just create the class in the MainViewModel.cs file but before i'm just going to delete all the previous code except the empty constructor. I'm also going to proceed and delete all the controls of the XAML as well.

Now my MainViewModel.cs should look like this:
Text Font Line Document


Now that we got the item let's create a collection in our MainViewModel that is going to contain all the items we want to load in our list. Start by adding the 'using MVVMTutorial.Common.Collections;' namespace so that our new collection class is recognized and add the following to create your collection:
Code:
private ObservableList<PictureItem> items;
        /// <summary>
        /// Collection that is going to hold our item collection
        /// </summary>
        public ObservableList<PictureItem> Items
        {
            get { return items; }
            private set { SetProperty(ref items, value); }
        }
As i previously showed all i do here is create a private variable that is going to hold my collection. As the type i use my ObservableList and as a itemType i use the PictureItem class i just recently wrote. With that in mind let's go ahead and create a method that is going to load pictures from a specified path. Let's start by adding the 'System.IO' so that we can read the content of a specified folder like this:
Code:
private void LoadPictures(string path)
        {
            var files = Directory.GetFiles(path, "*.jpg", SearchOption.TopDirectoryOnly);
            items = new ObservableList<PictureItem>();
            //Fill the collection
            foreach(var file in files)
            {
                // Create a new pictureItem instance
                var item = new PictureItem();
                item.Picture = new Uri(file);
                item.Name = Path.GetFileNameWithoutExtension(file);
                // Use the private variable instead of the public one to avoid un-necessary binding calls
                items.Add(item);
            }
            //After the process is done update by notifying
            InvokePropertyChanged("Items");
        }
As you can see i modify the private variable and after i'm done i notify the changes. This is very important to increase performance because if you use the public member when adding items is going to send update after adding every item causing the control to re-draw over and over. Now let's switch back to our main XAML and add a Listbox control in order to display the pictures:
Code:
<Window x:Class="MVVMTutorial.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:behaviours="clr-namespace:MVVMTutorial"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding Items}"/>
    </Grid>
</Window>
Now run the application and everything should compile just fine... after running the application it should look like this:
Blue Vehicle door Red Automotive exterior Automotive lighting


As you can see the code works but what's wrong? what you see is just a visual representation of items and since the listbox doesn't know how to handle them it just display the string. However as we can clearly see each item is a PictureItem! in our next step let's go ahead and teach the Listbox how to deal with that special item type. To do that we're going to modify the Listbox ItemTemplate property container and teach the control how to propertly display each item. To do that we're going to need the following controls in our template:

1. Grid which is the holder of the content. The grid column definition is going to be also declared so that one portion is going to be the image and the rest the label.
2. A image control with a pixel of 50x50
3. A label control

To do that modify the Listbox XAML like this:
Code:
<Window x:Class="MVVMTutorial.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:behaviours="clr-namespace:MVVMTutorial"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding Items}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="60"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Image Grid.Column="0" Width="50" Height="50" Source="{Binding Picture}"/>
                        <Label Grid.Column="1" Content="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
As you can see i modified the ItemTemplate by using the mentioned controls and added each control binding in order to display the desired content.

So now when we run our application it should look like this if the path is correct:
Text Blue Line Font Computer program


We are now free to modify the private variable and when we're done just notify the changes and we can interact with them anytime!

----------------------------------------------------------------------------------------------

The tutorial doesn't end here... i will complete it when i get enough time tomorrow. In the mean time it should keep those interested busy for a while ;) also attached the latest tutorial project for those interested.

Regards
@ruantec
 

Attachments

See less See more
5
#5 ·
Hi Ruantec
I've started migrating your classes into my project and was very pleased to see that it worked. As my project is half baked and I started coding my WPF project in windows form style (using code behind... yip I know... dumb, but hey I'm learning), I'm clueless about Validating my fields along with the pretty red box and info mark-up that I've currently got in my existing project... how would I retro-fit your classes to facilitate my "Update" button on Parent UI which utilises commands? (yes... also code behind :eek:) Also need to work with multiple UserControls in one UI. Correct me if I'm wrong but I think you mentioned that you would at some point be presenting a tutorial relating to this sort of thing?
Also to help me on my way how would you reference a field (MVVM way) on the parent UI from a child UserControl that uses your ViewModel sample. Just need to keep in mind that my "Parent UI" is coded in "Code Behind" ...Hope this is not to ambiguous a question.
 
#6 ·
Hmmmmm not quite sure what you're trying to do... could you be more specific please? you know... my english isn't the best one :p

There is nothing against Custom controls using codebehind tho. so that's OK. However most of the time(if not always) you don't have to use custom controls with codebehind as you practically use them as template and all your logic in the ViewModel. I'll cover that on the next part of my tutorial to explain more how it works. What i personally don't understand is why you want to access the parent viewmodel of a child element list? maybe if you're more specific i can see how to explain that better. You can also draw your concept so that i get an idea if you wish too.
 
#7 ·
What I've got are a whole bunch of UserControls within my main control... I need to get the database row ID from the parent to filter out my child records within my POCO statement... before I just referenced it by sending it to child function via a parameter... was thinking it would be better to reference it from the child UserControlViewModel.

Thanks... will try and explain further if needed?
PS. Also how to do validation using your classes?
 
#8 ·
I think i know what you mean but that's not really necessary... will try to figure out a way to explain that or to show you how to comunicate easily. In case i'm wrong... could you build a small project to show me what you're trying to do? or just make a picture that explain what you're trying to do... i'm more of a visual programmer so visual help me more to comprehend concepts than plain text :p

About validation... when building your bindings to a textbox for example you can determine the "TwoWay" property followed by the "UpdateTrigger" so it modify your viewmodel property on each change. While on your ViewModel(Setter) you have the chance to validate the value before or after is being set to the property ;). Another method is by creating the "PropertyChange" event on your viewmodel constructor which is going to listen to property changes. Each time a property change is going to trigger that event and will provide the name of the property that changed. At that point in time you can apply your checks and validation and set whatever you want.
 
#9 ·
What I have done is...
First Image: Depicts my folder structure with the UI's I'm referring to.
Second Image: This is my main UI that holds other UserControls like Multiple Isolation List, Job Safety Analysis etc. This carries a unique ID of 12 (top right of tab called "Work Permit" aka main UI)
Third Image: This is the Multiple Isolation List detail which is a sub set of data which is referenced by my "Work Permit - ID 12.

My query is as follows (Within my MultipleIsolationListViewModel):
PTWModel.Isolation isolation = efdb.Isolations.SingleOrDefault(p => p.RefPermitID == 12);
Where "12" is my Unique ID from my main UserControl. I need to reference the "PermitID" on my main UI from my MultipleIsolationListViewModel.
I've got my PermitID populated into my datagrid via a simple select statement. It's hidden in my datagrid. In the past I've used the following to extract the PermitID value and send it to my "childUserControls"

object selected = datagridDraftPermitList.SelectedItem;
DataRow row = ((DataRowView)selected).Row;
string refPermitID = string.Format("{0}", row["PermitID"]);

... and then to pass variable to UserControl...

childMultipleIsolationListDetailUControl.getMultipleIsolationListDetailUControl(refPermitID);

I was hoping to get away from this sort of coding as it does not lend itself to good coding practices...

 
#12 · (Edited)
Image 3:
Text Line Font Yellow Screenshot


Just a note:
I've noticed that I can't seem to bind my UserControl

xmlns:BindingUserControl="clr-namespacePTWS.MainPanelControls.FormControls.Certificates"
...
<BindingUserControl:MultipleIsolationListDetailUControl x:Name="childMultipleIsolationListDetailUControl" />

using your
DataContext = new MultipleIsolationListViewModel();
Its seems to throw an error... will look into it later.
 
#13 ·
Thanks for the explanation... right now i'm very busy and is not going to change until saturday. Will look through your pictures and posts to fully understand what you're trying to do and obviously post a way how you can get the desired results.
 
#14 ·
Hello again Ruantec.
Just playing at moment with a comboBox... I've created a ObservableList to populate the combobox and everything looks ok, but for the "SelectedValue" which comes from a different ObservableList collection value. I've found that you cant set a datacontext for the UI and then try and set another in some other component... is this correct?

The way I've managed to get everything to work is to assign datacontext to induvidual grids etc. and then set the ItemSource for combobox to my ObservableList collection. As I said all good apart from fact that I cant seem to set the SelectedValue... any help on this would be appreciated

ViewModel: Note the IsolationAuthorisedByID)
Code:
private void getIsolationDetails()
        {
            PTWEntities efdb = new PTWEntities();
            Isolation isolation = efdb.Isolations.SingleOrDefault(p => p.RefPermitID == 9);
 
            isolationDetailItems = new ObservableList<IsolationDetails>();
 
            var item = new IsolationDetails();
 
            ...
            item.IsolationAuthorisedByID = int.Parse(isolation.IsolationAuthorisedByID.ToString());
            ...
 
            isolationDetailItems.Add(item);
 
            InvokePropertyChanged("IsolationDetailItems");
 
        }
Code:
 private void getAuthorisedByLookupItems()
        {
            PTWEntities efdb = new PTWEntities();
 
            var query = from u in efdb.User_view
                        where u.isPAU_001 == true
                        select new { u.UserID, u.FirstName, u.LastName };
 
            var results = query.ToList();
            authorisedByLookupItems = new ObservableList<IsolationAuthorisedByLookup>();
 
            var authorisedByLookupItem = new IsolationAuthorisedByLookup();
 
            foreach (var user in results)
            {
                var _user = new IsolationAuthorisedByLookup();
 
                _user.UserID = int.Parse(user.UserID.ToString());
                _user.FullName = user.FirstName + ' ' + user.LastName;
 
                authorisedByLookupItems.Add(_user);
 
            }
 
            InvokePropertyChanged("AuthorisedByLookupItems");
        }
UI: (Note the "SelectedValue needs to be the item.IsolationAuthorisedByID value)
Code:
<ComboBox Height="23" HorizontalAlignment="Left"
                          ItemsSource="{Binding AuthorisedByLookupItems}"
                          Grid.Row="1"
                          Margin="200,3,0,0"
                          Name="comboBoxIsolationAuthorisedBy"
                          DisplayMemberPath="FullName"
                          SelectedValuePath ="UserID"
                          SelectedValue="{Binding IsolationAuthorisedByID}"
                          VerticalAlignment="Top" Width="201" Padding="3" />
 
#15 ·
Sorry for the lack of update but i've been a bit busy ;)

The selected value should be a TwoWay binding followed but the update trigger. This is necessary to set the value on change on your viewmodel and at the same time to be able to set any value on your viewmodel as well. Your selectedvalue should be as follow:

SelectedValue="{Binding IsolationAuthorisedByID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Those two properties are required in many scenarios where you want to get changed values on your viewmodel. The desired property setter shouldn't be private btw.
 
#16 ·
I made those changes and am still not getting the ComboBox SelectedValue to pick up the binding of the
"IsolationDetailItems.IsolationAuthorisedByID". If I hard code this value then it works, so somehow my IsolationAuthorisedByID is not getting through from my IsolationDetailItems collection to my combobox.selectedvalue. Is it because ive got mixed Contexts... I.E you can't bind from outside of the designated collection or am I still missing something?
 
#17 · (Edited)
I think you have a logic problem there. Actually after re-reading your post i realized that what you need is the SelectedItem and not SelectedValue. When you fill a combobox or any other container you do it using a collection that contains items. Each item can be the item that you actually selected so all you need is a property with the type of your item and just bind your SelectedItem to it. By doing that you get the item you need and with it all the properties and values.

I've modified the tutorial project to show you what i mean. Check in the viewmodel the new property "SelectedItem" and in XAML the new SelectedItem binding. Set a breakpoint in the setter of that property and you will see that is going to get in each time you select an item. You can also set a desired item from your code as well if wished and is going to have the same effect. If you have any further question i will be here later to answer them ;)
 

Attachments

#18 ·
Hi Ruantec
I've looked at your solution and am a bit confused... I'm taking it that what you have in code is after the fact of the page loading and user interacting with the control... IE User comes in and is presented by a listbox but nothing has been selected... user clicks on item and now through your updated code sample we have a record of which one has been selected... am I right in deducing this?

I'm more wanting the listbox to be pre selected via a userID stored in the database... So in database I've got say UserID set to 3. I now load the UserControl and am presented with a combobox with all the users populated in it by the collection "AuthorisedByLookupItems" and then the appropriate user (UserID = 3)... value from database passed into Items Collection (this would be the IsolationAuthorisedByID now set to 3) is automatically selected... I'll see if what you sent me triggers anything.

Thanks
 
#19 ·
I think you're very confused then ;)

The selection binding works in two ways just as i showed on my previous post. That means that it either changes on selection or you can set it from your code hence why "TwoWay". That being said you can fill up any combobox, listbox or whatever you want with a list and use the selecteditem property to change the selected item at will. In my previous modification i just wanted to show you that it changes each time you change something on your UI by user interaction but all properties in viewmodels can be modified at will and they will reflect in your UI.

I made a quick and dirty sample for you to show you how it works. Hope it makes sense to you... if not feel free to ask anything and i will try to answer your questions.
 

Attachments

#22 ·
Yay! You got it to work. Thanks...
Just to confirm would the following be the way...

Code:
SelectedAuthorisedByItem = authorisedByLookupItems.FirstOrDefault(selectedItem => selectedItem.UserID == IsolationDetailItems.FirstOrDefault().IsolationAuthorisedByID);
... and is there any optimal place to put this I.E in the "public MultipleIsolationListViewModel()"

.. and I'm still stuck on how to retrieve a value from Parent UI/Code behind to filter on my query...

Code:
PTWEntities efdb = new PTWEntities();
            Isolation isolation = efdb.Isolations.SingleOrDefault(p => p.RefPermitID == 9);
... where 9 is the record ID on my parent UI (I say UI as I've coded it with code behind :eek:)

Thanks again.
 
#23 · (Edited)
Glad it worked :)

You can actually have codebehind if you wish in some of your components and at the same time you can also add viewmodels to those too and use both ways simultaneously. That being said you can use "TwoWay" binding to retrieve values on your viewmodels after change. For example you have a UI parent element which has the same viewmodel as the one you're using right now then you set a value in codebehind(it need to be set at some point right?). When you declare a twoway binding is going to reflect the changes on your viewmodel so the value is already there when you need it. Will try to write a small test later in case you don't understand it... however for such situations you usually write a component that use codebehind and has dependency properties. Those dependency properties allow you to use binding and get values easily. The workaround i'm suggesting is pretty much the same but is done the dirty way :p

You are very close to get the idea entirely you just need to get your view a bit wider. Right now you are just looking at a small spot instead of the idea as a whole hence why is so difficult for you to solve those problems. I will be here to help you so feel free to ask any further questions ;)
 
#24 ·
Hi Ruantec
Just a quick one... is there anything peculiar about setting up a combobox within a datagrid using your structure? (seem to be getting blank bropdown)
Your comment above about me only looking at a small portion of the view... do you have anything basic I can look at that incorporated multi user controls within one parent that would demonstrate a "wider view"?
Thanks for all your help.
PS. I'm getting the idea now regarding what you posted back to me and have reignited my enthusiasm in my project… guess there is hope for me yet eh!
 
#25 ·
As i already posted you seem to have got a big portion of the idea already. What i've noticed so far is that is still a bit blurry in your mind which is why i used the "Small view". The idea is to comprehend how binding and the whole structure work in this kind of advance coding style. In other words never think about controls in general but see connections in your logic. To be honest you seem to be the first person that is finally getting the idea correctly and is going in the right path which makes me happy.

What i mean with a "Wider" view is to look at the connections of a bigger portion of your code rather than thinking how do i get this or that from a control. In other words you aren't looking for controls as you do on traditional coding but you build connections. I will prepare a better example when i get some time in the coming days to show you what i mean. I will also try to explain it better too so that you get the idea... as i said... you're very close to fully comprehend the concept.
 
#26 ·
Something I've noticed... Am I correct in saying that if you have a control with its datacontext set and you have another control within that control you can't assign another datacontext/ItemsSource to the child control?

EG...
This works:
Code:
<StackPanel  Grid.Row="1">
    <TextBox DataContext="{Binding IsolationScheduleListItems}" Text="{Binding IsolatedByID}" Grid.Row="1" HorizontalAlignment="Left" Width="100" Height="23" />
    <ComboBox ItemsSource="{Binding AuthorisedByLookupItems}" SelectedValue="1" DisplayMemberPath="FullName" SelectedValuePath ="UserID" Height="23" Width="100" />
</StackPanel>
but this doesn't:

Code:
<StackPanel DataContext="{Binding IsolationScheduleListItems}" Grid.Row="1">
    <TextBox  Text="{Binding IsolatedByID}" Grid.Row="1" HorizontalAlignment="Left" Width="100" Height="23" />
    <ComboBox ItemsSource="{Binding AuthorisedByLookupItems}" SelectedValue="1" DisplayMemberPath="FullName" SelectedValuePath ="UserID" Height="23" Width="100" />
</StackPanel>
 
#27 · (Edited)
No, each control can have a different data context if you wish. Every control in WPF have the exact same base which is the "FrameworkElement". However when a element is inside another element that already have a datacontext it automatically inherit that datacontext by default. By inheriting that datacontext doesn't mean it can't be changed. In fact... datacontexts can be changed at will anytime and everywhere. That is the dynamic i absolutely love about WPF. Btw that is one of the parts i meant with the "Wider" view as when you understand that you get a whole new picture in your mind to build your connections for your application.

NOTE:
By experience i can tell that datacontext inheritance goes as far as 2 levels tho. that means a child will inherit the datacontext and a child of that child may as well inherit the datacontext of its parent element. Further childs of those childs may not inherit the datacontext by default. Haven't tested since long time tho. but as far as i know it works that way for some unknown reasons to me.
 
This is an older thread, you may not receive a response, and could be reviving an old thread. Please consider creating a new thread.
Top