WPF MVVM ContextMenu Bound to ObservableCollection<string> Command Not Firing - wpf

I am trying to bind an ObservableCollection to a ContextMenu using MVVM. But when i try to fire the command nothing is happening. also, i need to pass the string as command parameter to the event.
Below is the xaml code:
<ContextMenu Name="ctxAddApplication" ItemsSource="{Binding Path=ApplicationTypes}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding AddRequirementCommand}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
Below is the View Model Code:
public ObservableCollection<string> ApplicationTypes { get; private set; }
public ComposableCommand AddRequirementCommand { get; private set; }
this.AddRequirementCommand = new ComposableCommand(this.AddRequirementView);
private void AddRequirementView(object applicationName) {}
Please help !!!

Just in case you need the code:
<ContextMenu Name="ctxAddApplication" ItemsSource="{Binding Path=ApplicationTypes}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.AddRequirementCommand}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

The data context for each menu item will be whatever it is bound to. In your case, a string because your ApplicationTypes property is a collection of strings. Thus, your binding to set the command won't work because there is no AddRequirementCommand property on type String.

Inside ContextMenu view for each item is bound to the item from the collection.
<Setter Property="Command" Value="{Binding AddRequirementCommand}" />
this will try to locate 'AddRequirementCommand' in string class. Use RelativeSource in this Binding. Also use VS debugger and Output window to see binding errors, it helps a lot usually.

Related

Avalondock close document with MVVM

We have a working avalondock implementation that listens to onclosing events, if the document is not saved the user gets a chance to save it etc. Works well.
Now a user wants a close button from the File menu and it should work like the built in close button (The little X by the document name).
Only way I have find is not very MVVM friendly.
I databind the CloseCommand to the dockable items ViewModel like
<Setter Property="CloseCommand" Value="{ Binding Model.CloseCommand, Mode=TwoWay}" />
Then from the ViewModel i have a method
public ICommand CloseCommand { get; set; }
public void Close()
{
if (CloseCommand.CanExecute(this))
{
CloseCommand.Execute(this);
}
}
This works and all the behaviour from pressing the built in close button is retained. But I think its a ugly hack. I'm dependant on that the View databinds the CloseCommand down to the viewmodel etc. There must be a more MVVM way of triggering close?
I solved it like this
VM
public ICommand CloseCommand { get; set; }
public void Close()
{
if (CloseCommand.CanExecute(this))
{
CloseCommand.Execute(this);
}
}
View
<xcad:DockingManager.LayoutItemContainerStyle>
<Style TargetType="{x:Type xcad:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}" />
<Setter Property="IconSource" Value="{Binding Model.Icon}"/>
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>
<Setter Property="CloseCommand" Value="{ Binding Model.CloseCommand, Mode=TwoWay}" />
</Style>
</xcad:DockingManager.LayoutItemContainerStyle>

Bind to property of parent from an explicit enumerable in XAML

Not sure if this is possible. I have an observable collection "OptionsList" with simple objects that have a "Name" and "IsEnabled" property.
Theres a menu that looks like
Configuration
|--Option1
|--Option2
|--Option3
|--Enabled
The first sub menu "Option1,Option2,Option3" bind correctly but then from within the I try to access those items from the first sub menu and bind to their data context but i cant seem to access them via RelativeSource for some reason.
<MenuItem Header="Configuration">
<MenuItem Header="Service" ItemsSource="{Binding OptionsList}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="ItemsSource">
<Setter.Value>
<x:Array Type="MenuItem">
<MenuItem Header="Enabled" IsCheckable="True"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}, Path=DataContext}"
IsChecked="{Binding Path=IsEnabled}"/>
</x:Array>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
The IsChecked is bound with wrong Path. The implicit source of the Binding here is already a DataContext which is bound to the DataContext of the parent MenuItem. So with the Path DataContext.IsEnabled - it will actually look for DataContext.DataContext.IsEnabled - of course that cannot be resolved.
You can simply remove the DataContext.:
IsChecked="{Binding IsEnabled}"
Another problem is the DataContext will auto flow down the child MenuItem, so you don't need to set the DataContext for the inner MenuItems, which actually does not work (I've tried it and somehow the RelativeSource binding does not work - it's not some kind of disconnected visual tree - because the DataContext is flown down OK - so it is very strange in this case):
<MenuItem Header="Enabled" IsCheckable="True" IsChecked="{Binding IsEnabled}"/>
Here is a safer approach in which we use a HierarchicalDataTemplate. Note that the ItemsSource is set to a dummy array with 1 element. Inside the ItemTemplate we will use a Binding walking up to the parent element for the IsChecked property
<MenuItem Header="Service" ItemsSource="{Binding OptionsList}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate>
<HierarchicalDataTemplate.ItemsSource>
<x:Array Type="{x:Type sys:Int32}">
<sys:Int32>0</sys:Int32>
</x:Array>
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="Enabled"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked"
Value="{Binding DataContext.IsEnabled, RelativeSource={RelativeSource AncestorType=MenuItem}}"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
I think the item display doesn't work because of some kind of reuse of the MenuItems in WPF (Not sure whether this is a bug in MenuItem). Playing around with x:Shared="False" didn't fixed it.
There is a different approach to achieve your goal:
Create a helper class that is a child of your option class and provide one instance of this helper as child of the option.
Bind in XAML to this helper class
Here is some code that shows in detail how to do:
XAML:
<MenuItem Header="Service" ItemsSource="{Binding OptionsList}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="ItemsSource" Value="{Binding ToggleItem}" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding IsEnabled}" />
</Style>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
C#
public class OptionHelper
{
private readonly Option owner;
public OptionHelper(Option owner)
{
this.owner = owner;
}
public bool IsEnabled
{
get { return this.owner.IsEnabled; }
set { this.owner.IsEnabled = value; }
}
}
public class Option : INotifyPropertyChanged
{
public ObservableCollection<OptionHelper> ToggleItem { get; private set; }
public Option(string name, bool isEnabled)
{
this.ToggleItem = new ObservableCollection<OptionHelper>() { new OptionHelper(this) };
this.name = name;
this.isEnabled = isEnabled;
}
// your code here...
}
I know this is not the perfect solution but it works... Wonder if someone find a solution without the helper.

Having a nested ViewModel and want to bubble up a command to the owning viewmodel

I have a WPF application with a workarea that resembles a file system on a computer. I have a ViewModel that holds the top folder and a list of subfolders:
public interface IRepositoryViewModel : IViewModelBase
{
ObservableCollection<IRepositoryTreeFolderModel> RootFolders { get; set; }
}
The IRepositoryTreeFolderModel is described like this:
public interface IRepositoryTreeFolderModel : IViewModelBase
{
Folder Folder { get; set; }
ObservableCollection<IRepositoryTreeFolderModel> SubFolders { get; set; }
bool IsSelected {get;set;}
bool IsExpanded {get;set;}
}
Please note that this is "recursive" with the same viewmodel nested. The purpose of this is to represent a tree structure of folders. I represent the data structure using a treeview, everything is working perfect. Now i need to have my "outer" IRepositoryViewModel know when i select a Folder in the tree. This is where i think im missing something. What im trying to do is to make a RoutedCommand, and consume it in my outer "IRepositoryViewModel"
My XAML for the treeView in the workarea is like this:
<TreeView Background="{x:Null}" ItemsSource="{Binding RootFolders}" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubFolders}">
<StackPanel Orientation="Horizontal">
<Image Source="/GWManagerAdmin;component/Graphics/Navigation/folder.png" Stretch="None" />
<TextBlock Text="{Binding Path=Folder.Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Do i need to raise my RoutedEvent in my IsSelected implementation manually (how to do this?) or is there a better approach for doing this. The problem here is that i do now know which instance of the IRepositoryTreeFolderModel has been selected, so i cant wire up a classic eventhandler and propagate it up - would also like to utilize the full potential of the RoutedCommand - i feel this is what I should do in this scenario...
Use Dan Wahlin's DataContext Proxy for this.
Just some idea from me. First you can read this http://joyfulwpf.blogspot.com/2009/05/mvvm-invoking-command-on-attached-event.html to build up yours.
In XAML, you can invoke command from parent by this snippet:
Command={Binding RelativeResource={RelativeResource FindAncestor, AncestorType={x:Type TreeView}}, Path=DataContext.YourCommand}
CommandArgument={Binding}
Hope it helps. I have not implemented myself. Just an idea.

"Tag" ... Special functionality in WPF?

MSDN says "Gets or sets an arbitrary object value that can be used to store custom information about this element." which means I can store anything I want in this property.
But if you bind to this property (with property of type String having a value say "XYZ") and use it in Trigger conditions it doesn't work!
<Trigger Property="Tag" Value="XYZ">
<Setter Property="Background" Value="Red" />
</Trigger>
It does not set the background red. You can try and assume myElement to be a TextBlock! Why is it like this?
Tag has no special functionality in WPF.
This works for me:
<TextBlock Tag="{Binding Data}"
x:Name="tb">
<TextBlock.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBlock.Tag"
Value="XYZ">
<Setter Property="TextBlock.Background"
Value="Lime" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
And setting the Data object property to "XYZ" in an event.
The Tag is a construct held over from Winforms days (and possibly there from before that!). It was used as a convenient place to associate an object with a UI element, such as a FileInfo with a Button, so in the Button's event handler you could simply take the event sender, cast it to a Button, then cast the Tag value to a FileInfo and you have everything you need about the file you want to open.
There is one situation, however, where I've found the Tag is useful in WPF. I've used it as a holding spot that can be accessed by a ContextMenu MenuItem, which can't use the normal RelativeSource bindings you'd use to traverse the visual tree.
<ListBox.ItemContainerStyle>
<Style
TargetType="ListBoxItem">
<Setter
Property="Tag"
Value="{Binding ElementName=TheUserControlRootElement}" />
<Setter
Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem
Header="_Remove"
ToolTip="Remove this from this list"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Command="{Binding PlacementTarget.Tag.Remove, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
From the ContextMenu, I cannot access the Remove command which is defined in the UserControl class where this snippet is defined. But I can bind the root to the Tag of the ListBoxItem, which I can access via the ContextMenu.PlacementTarget property. The same trick can be used when binding within a ToolTip, as the same limitations apply.
MainWindow.xaml:
<Window x:Class="wpftest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock x:Name="test" MouseDown="test_MouseDown"
Tag="{Binding TestProperty}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Tag" Value="XYZ">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
private void test_MouseDown(object sender, MouseButtonEventArgs e)
{
((TestViewModel)DataContext).TestProperty = "XYZ";
}
private sealed class TestViewModel : INotifyPropertyChanged
{
private string _testPropertyValue;
public string TestProperty
{
get { return _testPropertyValue; }
set
{
_testPropertyValue = value;
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs("TestProperty"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Updated: Tag property now is bound to TestProperty.

WPF, XAML: How to style a ListBoxItem using binding on property of ListBox ItemsSource object?

I have a ListBox which is bound to ObservableCollection of LogMessages.
public ObservableCollection<LogMessage> LogMessages { get; set; }
public LogMessageData()
{
this.LogMessages = new ObservableCollection<LogMessage>();
}
Each Message has two parameters:
public class LogMessage
{
public string Msg { get; set; }
public int Severity { get; set; }
//code cut...
}
ListBox is getting filled with those Items, and I need to color-code (change a background color of ListBoxItem) list depending on a Severity parameter of a LogMessage item.
Here's what I have now in XAML of user control showing the log:
<UserControl.Resources>
<AlternationConverter x:Key="BackgroundSeverityConverter">
<SolidColorBrush>Green</SolidColorBrush>
<SolidColorBrush>Yellow</SolidColorBrush>
<SolidColorBrush>Red</SolidColorBrush>
</AlternationConverter>
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Severity,
Converter={StaticResource BackgroundSeverityConverter}}"/>
</Style>
<DataTemplate x:Key="LogDataTemplate">
<TextBlock x:Name="logItemTextBlock" Width="Auto" Height="Auto"
Text="{Binding Msg}"/>
</DataTemplate>
</UserControl.Resources>
and an actual ListBox:
<ListBox IsSynchronizedWithCurrentItem="True"
ItemTemplate="{DynamicResource LogDataTemplate}"
ItemsSource="{Binding LogFacility.LogMessages}"
x:Name="logListBox" Grid.Row="1"
ItemContainerStyle="{StaticResource BindingAlternation}" />
The AlternationConverter is used because the Severity parameter of message is of type Int (0..3), and we can easily switch between styles using that one.
The concept is clear, but so far it does not work for me. The Background color of ListBoxItem did not change.
Use ItemContainerStyle:
<ListBox ItemsSource="{Binding LogMessages}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="{Binding Severity, Converter={StaticResource YourBackgroundConverter}}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Like Bojan commented, it's the RelativeSource which shouldnt be there.
Use {Binding Path=Severity, Converter={StaticResource BackgroundSeverityConverter}} when you're binding to your data object. RelativeSource.TemplatedParent is for binding to ListBoxItem.
Additionally, something of a pet peeve of mine, you could consider using triggers, for example:
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Severity}" Value="1">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Severity}" Value="2">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
<!-- etc.. -->
</Style.Triggers>
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
But that's just a personal preference....what you have there should work fine if you fix the binding.

Resources