WPF Contextmenu itemtemplate commandParameter binding returns null - c#

I have a problem. I am trying to create a context menu with databinding in wpf. The context menu items would are bound to an observable collection of objects. The population of context menu is fine - however, I want to add the command to it.
The way I am using it is:
in XAML
<Grid.Resources>
<local:RestoreCommand x:Key="RestoreCommand" />
<local:ShowBalloonCommand x:Key="BaloonCommand" />
<local:StartTaskCommand x:Key="StartTaskCommand" />
</Grid.Resources>
<ContextMenu ItemsSource="{Binding}" Name="taskBarContextMenu">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{StaticResource StartTaskCommand}"/>
<Setter Property="CommandParameter" Value="{Binding mainWindow}"/>
</Style>
</ContextMenu.ItemContainerStyle>
and
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Grid.Column="0"/>
<TextBlock TextWrapping="Wrap" Text="{Binding TimeElapsed, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="2"/>
and in C#:
taskbarIcon.ContextMenu.ItemsSource = TasksList;
And I have a class for the command
public class StartTaskCommand : ICommand
{
public void Execute(object parameter)
{
//THE PARAMETER IS ALWAYS NULL
var window = parameter as MainWindow;
if (window != null)
{
}
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
I have it set up simiarly for other two commands and it works fine. Anything I try to add as the parameter is always null - be it taskBarContextMenu, or mainWindow or menuItem...
Any ideas welcome.
====
I tried a following solution as suggested, but the command parameter is still null:
<tb:TaskbarIcon Name="taskbarIcon"
DoubleClickCommand="{StaticResource RestoreCommand}"
DoubleClickCommandParameter="{Binding ElementName=mainWindow}"
LeftClickCommand="{StaticResource BaloonCommand}"
LeftClickCommandParameter="{Binding ElementName=mainWindow}"
IconSource=".\256px-Out_of_date_clock_icon.ico"
MenuActivation="RightClick"
Tag="{Binding ElementName=mainWindow}"
>
<tb:TaskbarIcon.ContextMenu ><ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" Tag="{Binding}" Name="taskBarContextMenu">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{StaticResource StartTaskCommand}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}"/>
</Style>
</ContextMenu.ItemContainerStyle>

You need to add a tag to the menu's container and bind to it using placement target.
View this example:
<StackPanel x:Key="ConfigurationListItem" x:Shared="False" Tag="{Binding ElementName=UserControl}">
<StackPanel Orientation="Horizontal">
<Button>
<Button.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=UserControl, Path=LaunchCommand}" CommandParameter="{Binding}" />
<MouseBinding Gesture="LeftClick" Command="{Binding ElementName=UserControl, Path=SelectCommand}" CommandParameter="{Binding}" />
</Button.InputBindings>
</StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" Tag="{Binding}">
<MenuItem Header="Sync Environment Dependencies"
Command="{Binding Parent.PlacementTarget.Tag.SyncEnvironmentCommand, RelativeSource={RelativeSource Self}}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>

Related

How to avoid repeating blocks of XAML in a menu

I have to create a menu. It has 10 entries and they differ by one parameter.
Entry 1:
<MenuItem Visibility="{Binding MenuSelected.Type, Converter={StaticResource TypeToVisibilityConverter}, ConverterParameter='PAZ', Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding CmdContextMenu}" CommandParameter="PAZ" />
</i:EventTrigger>
</i:Interaction.Triggers>
<MenuItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Foreground="{Binding MenuSelected.Type, Converter={StaticResource TypeToColorConverter}, ConverterParameter='PAZ', Mode=OneWay}"
Text="{Binding MenuSelected.Type, Converter={StaticResource TypeToIconConverter}, ConverterParameter='PAZ', Mode=OneWay}" />
<TextBlock
Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="PAZ" />
</Grid>
</MenuItem.Header>
</MenuItem>
Entry 2:
<MenuItem Visibility="{Binding MenuSelected.Type, Converter={StaticResource TypeToVisibilityConverter}, ConverterParameter='APP', Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding CmdContextMenu}" CommandParameter="APP" />
</i:EventTrigger>
</i:Interaction.Triggers>
<MenuItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Foreground="{Binding MenuSelected.Type, Converter={StaticResource TypeToColorConverter}, ConverterParameter='APP', Mode=OneWay}"
Text="{Binding MenuSelected.Type, Converter={StaticResource TypeToIconConverter}, ConverterParameter='APP', Mode=OneWay}" />
<TextBlock
Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="APP" />
</Grid>
</MenuItem.Header>
</MenuItem>
As you can see, the only difference is between PAZ and APP in different points... and same goes on for all the others.
Is there a way I can avoid to repeat it 10 times just changing the 3 letters?
I do not want to use code-behind to create the menu dynamically... but to process it from XAML.
From your question I assume that CmdContextMenu and MenuSelected are properties on your main view model and not in a separate menu item type. If this is different, you have to adapt the code accordingly.
In order to remove the redundant code, create a collection for your menu items in your view model.
public class MyMainViewModel : INotifyPropertyChanged
{
public IEnumerable<string> MyTypes { get; } = new List<string>
{
"PAZ",
"APP"
};
// ...other properties and methods (like "CmdContextMenu" and "MenuSelected" I Assume).
}
Next, you have to change the value converters, because the ConverterParameter is not a dependency property and cannot be bound. Instead, you can use IMultiValueConverters that can bind multiple values. Adapt all of your converters to implement IMultiValueConverter. Here is an example of how a converter could look like. Of course, it depends on your implementation. In essence, use the values array to access bound values.
public class TypeToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]) ? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Next, create a data template for the header of your menu items. As you can see, the bindings are replaced with MultiBindings that use an IMultiValueConverter as Converter.
The first binding in each block is a RelativeSource binding to access the data context of the parent Menu, because I assume that the MenuSelected property is defined on your main view model. The other empty bindings will bind to the data context of the current menu item, which is an item from the MyTypes collection.
<DataTemplate x:Key="MyMenuItemHeaderTemplate" DataType="{x:Type system:String}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets">
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource TypeToColorConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</TextBlock.Foreground>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TypeToIconConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding}"/>
</Grid>
</DataTemplate>
Create a new header item style that uses this data template. The Visibility also uses a multi-value converter as above. Instead of using an event trigger, you can simply assign the command to the menu item directly and pass a CommandParameter, which is bound to the data context of the current menu item.
<Style x:Key="MyMenuItemStyle" TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="HeaderTemplate" Value="{StaticResource MyMenuItemHeaderTemplate}"/>
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource TypeToVisibilityConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding DataContext.CmdContextMenu, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
Finally, create a Menu and bind the MyTypes collection, as well as the style.
<Menu ItemsSource="{Binding MyTypes}" ItemContainerStyle="{StaticResource MyMenuItemStyle}"/>
ConverParameter property is not a DependencyProperty - so it cannot be Bound to.
You can use a MultiValue converter instead.
Instead of creating 10 menu items in xaml manually, you should be able to bind an ItemsCollection and define a DataTemplate for MenuItem

Custom control: binding in ItemContainerStyle

I'm working in a Explorer tree view (custom wpf control designed by me). I have this code in Generic.xaml:
<Style TargetType="{x:Type local:ExplorerControl}">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExplorerControl}">
<Border>
<TreeView Name="myTreeView" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem x:Name="myTemplate" Header="Remove" Command="{TemplateBinding RemoveCommand}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In ExplorerControl I have my Dependency Property:
public class ExplorerControl : Control{
public ExplorerControl()
{
Nodes = new ObservableCollection<Node>();
}
private ObservableCollection<Node> Nodes { get; }
public ICommand RemoveCommand
{
get { return (ICommand)GetValue(RemovedCommandProperty); }
set { SetValue(RemovedCommandProperty, value); }
}
public static readonly DependencyProperty RemovedCommandProperty =
DependencyProperty.Register("RemoveCommand", typeof(ICommand), typeof(ExplorerControl));
}
Node class
public class Node {
public string Name {get;set;}
}
My problem is that I don't know how to get that the MenuItem Command works
I have tried these:
If I use the same code with a button after the TreeView (with both in a Stackpanel) it works. So I think that the problem is the MenuItem DataContext
I tried to change the MenuItem DataContext however I didn't get it.
I hope you can help me.
Edit: I delete the part of code about DataContext. Thanks for you answers.
I use this control in my MainView:
<treeViewExplorerControl:ExplorerControl
SelectedItemName="{Binding SelectedItemName}"
SelectedItemPath="{Binding SelectedItemPath}"
RemoveCommand="{Binding ExplorerControlItemRemovedCommand}"/>
Proposed Solution:
In your MenuItem Commands try using Ancestral Binding.
<MenuItem x:Name="myTemplate" Header="Remove"
Command="{Binding RelativeSource={RelativeSource Path=RemoveCommand, AncestorType={x:Type ExplorerControl}, Mode=FindAncestor" />
I believe the reason your DataContext is changing is because you're pointing to Nodes and showing each Node in the MenuItem. However, Node does not contain the command you are trying to bind to.
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
If you are unsure of your DataContext you can use Snoop to see what is the current DataContext.
Extra Info:
I don't think you need to point DataContext of your TreeView. It's passed down automatically.
<TreeView.DataContext>
<local:ExplorerControl x:Name="explorer" />
</TreeView.DataContext>
You don't have to use a DependencyProperty with an ICommand. In the constructor of your ExplorerControl you can instantiate the ICommand to a DelegateCommand.
Make a DelegateCommand class that inherits from ICommand. This will be your concrete implementation of ICommand. You can find that here:http://www.wpftutorial.net/delegatecommand.html
Instantiate your ICommands with a DelegateCommand and pass your method, that is on ExplorerControl, to the constructor.
For example:
public class ExplorerControl : UserControl
{
public DelegateCommand RemoveCommand { get; set; }
public ExplorerControl()
{
RemoveCommand = new DelegateCommand(Remove);
}
private void Remove()
{
// Do something here.
}
}
Finally I found a solution.
First of all I found that about ContextMenu:
Because a ContextMenu in WPF does not exist within the visual tree of your page/window/control per se, data binding can be a little tricky.
Source
With the example, I wrote this code and I got that it works well:
<Style TargetType="{x:Type local:ExplorerControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExplorerControl}">
<Border>
<TreeView Name="myTreeView">
<TreeView.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FF003BB0" />
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal"
Tag="{Binding TemplatedParent,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem x:Name="myTemplate"
Header="Remove"
DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
Command="{Binding Path=Tag.RemoveCommand}"
CommandParameter="{Binding Path=DataContext}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I save in the Stackpanel tag, the reference of my explorerControl, then I use PlacementTarget to get the Stackpanel reference
I hope this code helps other people in future.

How to get parent event inside ControlTemplate?

I want to invoke a Command from view model inside radio button ControlTemplate. And do it when event Checked is raised
I tried this:
<ControlTemplate x:Key="RequestTypeControlTemplate" TargetType="RadioButton">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0,0,2">
<ContentPresenter Margin="10,0,10,0">
<i:Interaction.Triggers>
<!-- Checked event from parent -->
<i:EventTrigger EventName="Checked">
<!-- Command from view model and binding to tag of radio button as arg -->
<i:InvokeCommandAction Command="{Binding DataContext.OnTypeChangedCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentPresenter>
</Border>
</ControlTemplate>
But I think event goes from ContentPresenter, not from RadioButton. How do I get the event from RadioButton?
EDIT:
<RadioButton Content="All requests"
ContentTemplate="{StaticResource RadioButtonDataTemplate}"
GroupName="Filter"
IsChecked="True"
Style="{StaticResource RadioButtonStyle}"
Template="{StaticResource RequestTypeControlTemplate}"
Tag="{x:Static local:ContentType.Any}"/>
In viewModel
public ICommand OnTypeChangedCommand
{
get
{
return m_TypeChangedCommand ?? (m_TypeChangedCommand = new DelegateCommand<ContentType>(OnTypeChanged));
}
}
public void OnTypeChanged(ContentType type)
{
Debug.WriteLine(type);
}
You can't do that because the Routed event is only handled from the element (bubble) or to the element (tunnel)
What you can use is a DataTrigger on IsChecked property .
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding IsChecked,RelativeSource={RelativeSource AncestorType=RadioButton}}" Value=True>
<i:InvokeCommandAction Command="{Binding DataContext.OnTypeChangedCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding Tag, RelativeSource={RelativeSource AncestorType=RadioButton}}}" />
</ei:DataTrigger>
</i:Interaction.Triggers>

AppBarButton Command in MVVM does not Trigger RelayCommand

I have Windows Phone Class Library that uses the MVVM model. In my Generic.xaml I have the Style for my CustomElementControl.cs that has a CustomElementViewModel.cs. I also have a ViewModelLocator.cs that relates ViewModels to Controls that is named in this context as Locator, but that is not relevant to this case. Now, if I add a Command to a Button like so:
<Style TargetType="view:CustomElementControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="view:CustomElementControl">
<Page x:Name="pageRoot" DataContext="{Binding Source={StaticResource Locator}, Path=view:CustomElementViewModel, Mode=TwoWay}">
<TextBlock Text="something" Foreground="{Binding Path=ColorTest}" />
<Button Content="MyButton" Command="{Binding MultiSelectCommand}" />
</Page>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And catch in the CustomElementViewModel.cs like so:
private RelayCommand _multiSelectCommand;
public RelayCommand MultiSelectCommand {
get {
if(_multiSelectCommand == null) {
_multiSelectCommand = new RelayCommand(
() => {
this.SelectionMode = (this.SelectionMode == ListViewSelectionMode.Single) ? ListViewSelectionMode.Multiple : ListViewSelectionMode.Single;
});
}
return _multiSelectCommand;
}
}
It works fine, however, if I want to add the Command event to an AppBarButton like so:
<Style TargetType="viewCustomElementControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="view:CustomElementControl">
<Page x:Name="pageRoot" DataContext="{Binding Source={StaticResource Locator}, Path=view:CustomElementViewModel, Mode=TwoWay}">
<TextBlock Text="something" Foreground="{Binding Path=ColorTest}" />
</Page>
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="AllApps" Label="multiselect" Command="{Binding MultiSelectCommand}" />
</CommandBar>
</Page.BottomAppBar>
</Page>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The RelayCommand is never triggered. What am I missing here?
UPDATE
I've even tried setting the DataContext of the AppBarButton like so:
<AppBarButton Icon="AllApps" Label="multiselect" Command="{Binding DataContext.MultiSelectCommand, ElementName=pageRoot, Mode=OneWay}" />
And
<AppBarButton Icon="AllApps" Label="multiselect" DataContext="{Binding Source={StaticResource Locator}, Path=CustomElementViewModel, Mode=TwoWay} Command="{Binding MultiSelectCommand}" />
And none works.
UPDATE 2
Here's a sample: http://1drv.ms/1vRnaYH

Unable to access viewmodel properties from inside Listvew.Resources

I'm trying to bind the SelectedItem to a View. But the view is not able to access the viewmodel when it is inside the Resources block.
When the datacontext is re-assigned to the children, the binding works for textblocks but not for UserControl (NoteView)
Am I missing any Binding?
PFB revised(entire) code and inline comments.
<UserControl x:Class="Konduva.View.NoteSearchView"
<!-- other namespaces here -->
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
DataContext="{Binding NoteSearch, Source={StaticResource Locator}}">
<Grid>
<ListView ItemsSource="{Binding Notes}"
SelectedItem="{Binding SelectedNote}">
<ListView.Resources>
<DataTemplate DataType="{x:Type vm:NoteViewModel}">
<DockPanel>
<TextBlock Text="{Binding Title}" />
<Popup Placement="Right"
PlacementTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}"
IsOpen="{Binding (ListViewItem.IsSelected), RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}">
<StackPanel>
<!-- This is working --> <TextBlock Text="{Binding SelectedNote.Title}" />
<!-- This is not working --> <v:NoteView DataContext="{Binding SelectedNote}" />
</StackPanel>
</Popup>
</DockPanel>
</DataTemplate>
</ListView.Resources>
</ListView>
</Grid>
</UserControl>
NoteView:
<Grid>
<TextBlock Text="{Binding Title}" /> // This Text is not displayed
</Grid>
Update 3
Since you're using MvvmLight: in NoteView, try changing
DataContext="{Binding Note, Source={StaticResource Locator}}"
to
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="DataContext" Value="{Binding Note, Source={StaticResource Locator}}"/>
</Style>
</UserControl.Style>
Update 2
Encountered a similar problem a few minutes ago which I didn't fully understand so I'll throw in the same solution here to see if it helps. What happends if you change it to this?
<v:NoteView DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type Popup}},
Path=DataContext.SelectedNote}"/>
Update
I'm unable to reproduce this. Try adding this in your NoteView constructor. Do you reach DataContextChangedHandler when you change the selection in the ListView?
public NoteView()
{
InitializeComponent();
DependencyPropertyDescriptor dpd =
DependencyPropertyDescriptor.FromProperty(UserControl.DataContextProperty,
typeof(UserControl));
if (dpd != null)
{
dpd.AddValueChanged(this, new EventHandler(DataContextChangedHandler));
}
}
void DataContextChangedHandler(object sender, EventArgs e)
{
MessageBox.Show("DataContext Changed: " + DataContext);
}
First answer
Your DockPanel will get the NoteViewModel as a DataContext and not the ListView and since this DataContext is inherited by all Childs every child will end up with a NoteViewModel as DataContext. To use the ListView as a DataContext for the Popup you can do this. I'm not sure what the DataContext Binding for the StackPanel does though, so I might be missing something here..
<DataTemplate DataType="{x:Type vm:NoteViewModel}">
<DockPanel>
<TextBlock Text="{Binding Title}" />
<Popup Placement="Right"
PlacementTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}"
IsOpen="{Binding (ListViewItem.IsSelected), RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}">
<StackPanel>
<TextBlock Text="{Binding SelectedNote.Title}" />
<StackPanel>
<v:NoteView DataContext="{Binding SelectedNote}"/>
</StackPanel>
</StackPanel>
</Popup>
</DockPanel>
</DataTemplate>
Instead of inserting a NoteView directly and binding the DataContext, use a ContentPresenter:
<ContentPresenter Content="{Binding SelectedNote}>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<v:NoteView />
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>

Categories