Perhaps there is a better way to do this, but here’s where I’m at right now. I would like to bind a ContextMenu to an ObservableCollection of some complex type. A property on the complex type will determine the icon that is displayed in the menu item. The only way I knew to do this was to create a ComplexTypeToMenuItem converter and associate that with the binding. However, as soon as I add the converter to the binding the context menu no longer updates when the collection has changed. If I remove the converter and rely on ComplexType.ToString() then the menu items update just fine. However, there are no icons in this case.
This can easily be reproduced with the following:
First the XAML
<Window x:Class="GoddamnContextMenuPosition.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GoddamnContextMenuPosition"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:ComplexTypeToMenuItemConverter x:Key="ComplexTypeToMenuItemConverter" />
</Window.Resources>
<Grid>
<TextBox Grid.Row="0" Text="Hooray">
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding Objects, Source={x:Static local:Settings.Instance}, Converter={StaticResource ComplexTypeToMenuItemConverter}}">
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
<Button Grid.Row="1" Content="Add" Height="25" Width="50" Click="Button_Click"></Button>
</Grid>
</Window>
And now the code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Settings.Instance.Objects.Add(new ComplexType()
{
Text = "NEW", Type = 4
});
}
}
public class ComplexType
{
public ComplexType()
{
this.TimeStamp = DateTime.Now;
}
public string Text
{
get;
set;
}
public int Type
{
get;
set;
}
public DateTime TimeStamp
{
get;
set;
}
}
public class ComplexTypeToMenuItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var values = value as IEnumerable<ComplexType>;
if (values == null)
{
return new List<MenuItem>
{
new MenuItem
{
Header = "Unknown Value"
}
};
}
IList<MenuItem> items = new List<MenuItem>();
foreach (ComplexType obj in values)
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri("1.png", UriKind.Relative);
bitmap.EndInit();
Image image = new Image();
image.Width = 16;
image.Source = bitmap;
MenuItem menuItem = new MenuItem();
menuItem.Header = string.Format("{0} - {1}", obj.Text, obj.TimeStamp.ToShortTimeString());
menuItem.Icon = image;
items.Add(menuItem);
}
return items;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Settings
{
private static readonly Settings settings = new Settings();
private Settings()
{
this.Objects = new ObservableCollection<ComplexType>
{
new ComplexType
{
Text = "Item #1",
Type = 1
}
};
}
public static Settings Instance
{
get
{
return settings;
}
}
public ObservableCollection<ComplexType> Objects
{
get;
private set;
}
}
Any help will be greatly appreciated!
You turn the
ObservableCollectioninto aList, which does no longer provide updates, hence the problem. In general you want to avoid converters inItemsSourcebindings.Do not use converters to do this sort of thing, use Data Templating.