Click here to Skip to main content
13,799,868 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

56.5K views
1.1K downloads
10 bookmarked
Posted 27 Feb 2011
Licenced Apache

WPF: ContextMenu Strikes Again. DataContext Not Updated

, 27 Feb 2011
Rate this:
Please Sign up or sign in to vote.
ContextMenu's data context is not updated when its parent's data context changes

Long Story Short

You probably already know that ContextMenu class is not part of the WPF visual tree (see here and here for some problems that this causes).

Still, ContextMenu acquires DataContext of its parent control (since .NET 3.5?). The problem is, this acquisition happens only once. If parent's data context changes at a later time, the ContextMenu's data context will not be updated. This will cause issues if the view with the context menu is bound to something variable, like a selected item of a listbox.

Workaround

The workaround is to explicitly bind menu's data context to parent's datacontext as follows:

<ContextMenu DataContext="{Binding PlacementTarget.DataContext, 
	RelativeSource={RelativeSource Self}}" >

This magical spell tells WPF to create a permanent binding between the menu's data context and its "placement target" (i.e. parent) data context, which continues to work even after parent's data context is changed. You need this spell only if you expect parent's data context to change during the life of the parent.

Sample

Sample screen shot

I created a simple sample that illustrates the problem. It contains a list box with country names and two user controls: the "good" and the "bad". Both controls show country capital. They also have right click menu to show country language. The good control shows correct language of the selected country. The bad control shows correct language when the menu is first invoked, and then keeps showing that language even if selected country changes.

This happens because the menu gets created first on right click and then acquires the (correct) data context from the parent. On subsequent right clicks, the same menu object is reused (as proved by the "Same menu?" command), and its data context never changes, unless we create an explicit binding for it.

Here are some key pieces of code (certain details, including "Same menu" command were omitted for clarity):

class Country
{
    public string Name { get; set; }
    public string Capital { get; set; }
    public string Language { get; set; }
}

static class Countries
{
    public static readonly Country[] List = new[]
    {
        new Country { Name = "USA", Capital="Washington", Language="English"},
        new Country { Name = "Spain", Capital="Madrid", Language="Spanish"},
        new Country { Name = "France", Capital="Paris", Language="French"},
        new Country { Name = "Brazil", Capital="Brasilia", Language="Portuguese"},
        new Country { Name = "Thailand", Capital="Bangkok", Language="Thai"},
    };
 } 

class LanguageCommand : ICommand
{
    public void Execute(object parameter)
    {
        object safeParameter = parameter ?? "null";
        MessageBox.Show(safeParameter.ToString());
    }
}
<!-- MainWindow.xaml -->
<Window Title="MainWindow" Height="350" Width="525">
    <DockPanel LastChildFill="True">
        <ListBox Name="CountryList" ItemsSource="{x:Static local:Countries.List}" />
        <UniformGrid Rows="2" Columns="1" DataContext="{Binding SelectedItem, 
		ElementName=CountryList}">
            <local:GoodControl />
            <local:BadControl />
        </UniformGrid>
    </DockPanel>
</Window>
        
<!-- GoodControl.xaml -->
<UserControla x:Class="ContextMenuDataContext.GoodControl">
    <UserControl.Resources>
        <local:LanguageCommand x:Key="LanguageCommand" />
    </UserControl.Resources>
    <UserControl.ContextMenu>
        <ContextMenu DataContext="{Binding PlacementTarget.DataContext, 
		RelativeSource={RelativeSource Self}}" >
            <MenuItem Header="Language" Command="{StaticResource LanguageCommand}" 

		CommandParameter="{Binding Language}" />
        </ContextMenu>
    </UserControl.ContextMenu>
    <TextBlock Text="{Binding  Capital}" />
</UserControl>

<!-- BadControl.xaml -->
<UserControl x:Class="ContextMenuDataContext.BadControl">
    <UserControl.Resources>
        <local:LanguageCommand x:Key="LanguageCommand" />
    </UserControl.Resources>
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Language" Command="{StaticResource LanguageCommand}" 

		CommandParameter="{Binding Language}" />
        </ContextMenu>
    </UserControl.ContextMenu>
    <TextBlock Text="{Binding Capital}" />
</UserControl />

Conclusion

Default mechanism for data context binding works well in most cases, because most views never change data context during their lives. The problems start if the data context changes. The most annoying issue is that even if the data context changes, everything will work right the first time the menu is invoked. However, stale data will be returned for subsequent invocations. This may go unnoticed for quite some time. Explicitly bind context menu's data context to PlacementTarget.DataContext to avoid this bug.

History

  • 27th February, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Share

About the Author

Ivan Krivyakov
Technical Lead Thomson Reuters
United States United States
Ivan is a hands-on software architect/technical lead working for Thomson Reuters in the New York City area. At present I am mostly building complex multi-threaded WPF application for the financial sector, but I am also interested in cloud computing, web development, mobile development, etc.

Please visit my web site: www.ikriv.com.

You may also be interested in...

Comments and Discussions

 
QuestionHow to raise can execute? Pin
danny048330-May-18 3:04
memberdanny048330-May-18 3:04 
Questionreally helped me with the datacontext problem Pin
Arthur Arapetian4-Aug-17 17:40
memberArthur Arapetian4-Aug-17 17:40 
GeneralExcellent decision! Pin
deadeane10-Sep-14 2:36
memberdeadeane10-Sep-14 2:36 
GeneralMy vote of 5 Pin
mityapo23-Sep-12 7:12
membermityapo23-Sep-12 7:12 
GeneralMany Thanks Pin
ahirth14-May-12 1:54
memberahirth14-May-12 1:54 
GeneralThank U very much!!! Pin
lavige77710-May-12 22:23
memberlavige77710-May-12 22:23 
GeneralThanks! Pin
Robert Hahn2-Aug-11 0:09
memberRobert Hahn2-Aug-11 0:09 
GeneralGreat article Pin
Wiesław Šoltés25-Jul-11 12:21
memberWiesław Šoltés25-Jul-11 12:21 
GeneralExcellent! Pin
Mark Cranness16-Mar-11 23:27
memberMark Cranness16-Mar-11 23:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.181214.1 | Last Updated 27 Feb 2011
Article Copyright 2011 by Ivan Krivyakov
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid