Custom WPF context menu with text on top - c#

I created a custom context menu template which looks like this:
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="Background" Value="{StaticResource menuBorderBrush}"/>
<Setter Property="Foreground" Value="{StaticResource menuForegroundBrush}"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border" Background="{StaticResource menuBackgroundBrush}" BorderThickness="5" BorderBrush="{StaticResource menuBackgroundBrush}">
<StackPanel>
<TextBlock TextAlignment="Center" Padding="5,5,5,10">
You are not logged in.
</TextBlock>
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now i'd like to be able to change the "You are not logged in." text programmatically. I tried to start by creating a custom control class which inherits from ContextMenu and change the x:Type in the XAML to this class but then the Properties (like Background, Foreground, ...) are unknown.
How can i do this without implementing a new ContextMenu from scratch?

You can add to the TextBlock Binding with TemplatedParent for this.
<TextBlock TextAlignment="Center" Padding="5,5,5,10"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag}" />
And add the text to the ContextMenu Tag property, like that.
<Grid.ContextMenu>
<ContextMenu Tag="Test menuItem text" />
</Grid.ContextMenu>
Update
There are some tricks to pass many properties. For example you can bind ContextMenu to viewModel with many properties. The view Model should have realization INotifyPropertyChanged behaviour. So you can write something like this, but with many properties.
public class OwnObject : INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; NotifyPropertyChanged( "Text" ); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void NotifyPropertyChanged( String info )
{
if ( PropertyChanged != null )
{
PropertyChanged( this, new PropertyChangedEventArgs( info ) );
}
}
}
And pass this viewModel object through the Tag property.
<Grid.ContextMenu>
<ContextMenu Tag="{Binding Object}" />
</Grid.ContextMenu>
And use it in the menu Style through the Tag.Text.
<TextBlock TextAlignment="Center" Padding="5,5,5,10"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag.Text}" />

Related

Button ContextMenu Image only shown at last item

I have a problem with a dynamic ContextMenu on a Button.
The Button is inside a DataGrid with the following Columndefinition in a ResourceDictionary:
<DataGridTemplateColumn Width="16" x:Key="ShowContextMenuColumn" x:Shared="False" KeyboardNavigation.IsTabStop="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Style="{StaticResource ShowContextMenuButtonStyle}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Data.ContextActionItems, Source={StaticResource proxy}}" DisplayMemberPath="DisplayText">
<ContextMenu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding Image}"/>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding Command}"/>
</Style>
</ContextMenu.Resources>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{markup:Type DataGridCell}" BasedOn="{StaticResource BaseDataGridCellStyle}">
<Setter Property="AutomationProperties.Name" Value="ContextMenu"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
The ObservableCollection<DisplayListActionItem> for the ContextActionItems is defined in an abstract generic base-class which is inherited by several ViewModels.
The DisplayListActionItem-Class looks like:
public class DisplayListActionItem : NotifyBase
{
public string DisplayText
{
get { return Get<string>(); }
set { Set(value); }
}
public ICommand Command
{
get { return Get<ICommand>(); }
set { Set(value); }
}
public ImageSource Image
{
get { return Get<ImageSource>(); }
set { Set(value); }
}
}
Everything just works fine. The only problem is, that the Image is only shown at the last item of the ContextMenu. If I remove the last item, then the image of the new last item is shown. I don't understand what's the reason for this.
I've already checked the Debug-Output for Binding-Errors, but there are none.
Any ideas what could cause this problem?
Solved it by myself.
The solution was to modify the ContextMenu-ItemContainerStyle instead doing the needed stuff in the Resources of the ContextMenu.
The new xaml for the Button looks like:
<Button Style="{StaticResource ShowContextMenuButtonStyle}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Data.ContextActionItems, Source={StaticResource proxy}}" >
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{Binding Image, Converter={converter:ImageSourceToImageConverter}}"/>
<Setter Property="Header" Value="{Binding DisplayText}"/>
<Setter Property="Command" Value="{Binding Command}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>

Item command cannot edit another item in the list

I have a sidebar (in a C# WPF program) that should display 4 "different" buttons (They are actually 2 different styles, which both have another style for the active state). The sidebar consists of an ItemsControl. I've now managed to create a list where the correct style is used based on an enum value (as shown below). Here's a small question: Can I do it this way, or should I rewrite it, and if so, how could something like this be built? Keywords or something that I have to look at are enough for me.
My real question now is: I have bound a command to every button, nothing complicated at first. The command now sets its own state to NormalActive for testing purposes. The 1st item in this list should be set from LiveActive to Live (so that you always see the currently selected item as you know it). And here's the problem: The button can set its own state, so when I click on button 3, the state of button 3 is set from Normal to NormalActive. But what doesn't happen is the change from LiveActive to Active from the 1st button. Even if I output the current state to the console before and after the change, it returns LiveActive for both. I also tried invoking the whole thing into the dispatcher if I'm not in the UI thread for some reason, it didn't work. So the button can set its own state, but not the one of another. But I don't get an error message or anything. Also the setter method of the property is called, it just doesn't change it. What could be the reason?
PluginListControl:
<Grid DataContext="{x:Static local:PluginListDesignModel.Instance}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:PluginListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
PluginListItemControl:
<UserControl.Resources>
<DataTemplate x:Key="PluginTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginActiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginActiveLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<ContentControl d:DataContext="{x:Static local:PluginListItemDesignModel.Instance}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource PluginLiveTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="3">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveLiveTile}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
PluginListItemViewModel: (The ViewModel for each list item)
public class PluginListItemViewModel : BaseViewModel
{
public string Name { get; set; }
public PluginTileStates State { get; set; }
public ICommand SetStateCommand { get; set; }
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public PluginListItemViewModel()
{
SetStateCommand = new RelayCommand(() => SetState());
}
#endregion
private void SetState()
{
PluginListDesignModel.Instance.Items[0].State = PluginTileStates.Live;
State = PluginTileStates.NormalActive;
}
}
Steps to reproduce:
Create a new WPF project, .NET Framework 4.6.1 (Visual Studio 2017).
Replace the grid in MainWindow with the following:
<Grid DataContext="{x:Static local:ListViewModel.Instance}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
Add a new UserControl named ListItemControl and replace the grid with:
<UserControl.Resources>
<Style x:Key="Tile" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
</Style>
<Style x:Key="ActiveTile" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green" />
</Style>
<DataTemplate x:Key="PluginTile" DataType="{x:Type local:ListItemViewModel}">
<Button Width="100" Height="60" Style="{StaticResource Tile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:ListItemViewModel}">
<Button Width="100" Height="60" Style="{StaticResource ActiveTile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Add a new class called BaseViewModel and replace class with:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Add new class called ListItemViewModel and replace class with:
public enum TileStates
{
Normal = 0,
Active = 1
}
public class ListItemViewModel : BaseViewModel
{
public TileStates State { get; set; }
public ICommand SetStateCommand { get; set; }
public ListItemViewModel()
{
SetStateCommand = new RelayCommand(() =>
{
ListViewModel.Instance.Items[0].State = TileStates.Normal;
State = TileStates.Active;
});
}
}
Add new class called ListViewModel and replace class with:
public class ListViewModel : BaseViewModel
{
public static ListViewModel Instance => new ListViewModel();
public List<ListItemViewModel> Items { get; set; } = new List<ListItemViewModel>
{
new ListItemViewModel
{
State = TileStates.Active
},
new ListItemViewModel
{
State = TileStates.Normal
}
};
}
Add new class called RelayCommand and replace class with:
public class RelayCommand : ICommand
{
private Action mAction;
public event EventHandler CanExecuteChanged = (sender, e) => { };
public RelayCommand(Action action)
{
mAction = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
mAction();
}
}
Install the NuGet-Packages: "Fody v4.0.2" and "PropertyChanged.Fody v2.6.0" (You probably have to restart Visual Studio after installation
If you now press the bottom button, it should get green and the top one should switch to red.
ListViewModel.Instance returns a new instance of the ListViewModel class each time it's invoked. It should return the same instance:
public static ListViewModel Instance { get; } = new ListViewModel();

Clicking a button in a listviewitem that is not selected and getting selected item

I have a question about a listview setup like the example below. When I click the button below in the expander header I want that item to be selected as well, but what I'm seeing is while the button command does work, the item selected is still the previous item selected, not the item my button is in. How can I have the Item selected when the button is clicked?
I tried setting up a ControlTemplate like this, but it did not work.
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListView ItemsSource="{Binding MyItemSource,
Mode=TwoWay}"
SelectedItem="{Binding MySelectedItem,
Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Expander IsExpanded="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}">
<Expander.Header>
<Button Command={Binding MyCommand}>Click Me</Button>
</Expander.Header>
<!-- content here -->
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I would suggest defining a command SelectItem in the main ViewModel which takes the item which is to be selected as a parameter. The execution method of this command can then set the MySelectedItem property, set a property IsSelected on the item ViewModel to true and invoke all further actions on the item itself (i.e. what is now executed by MyCommand). With the selection logic in the ViewModel and a clean binding you don't even need to use ListView at all but can stick to a plain ItemsControl:
The XAML then looks like this:
<ItemsControl ItemsSource="{Binding MyItemSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander IsExpanded="{Binding IsSelected}">
<Expander.Header>
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}, Path=DataContext.SelectItem}"
CommandParameter="{Binding"}>Click Me</Button>
</Expander.Header>
<!-- content here -->
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The MainViewModel would look something like this:
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<ItemViewModel> MyItemsSource { get; private set; }
public ItemViewModel SelectedItem { get... set... }
public ICommand SelectItem { get; private set; }
ctor()...
private void ExecuteSelectItem(ItemViewModel item)
{
SelectedItem = item;
foreach (var i in MyItemsSource) i.IsSelected = false;
item.IsSelected = true;
item.DoSomething();
}
}
I have always found it way easier to use ItemsControl an implement the few lines of selection logic myself, instead of dealing with the messy binding of the selection of a ListView. In my opinion it is a quite intuitive to implement custom selection behavior (multiple items, allowing only certain combinations, etc.). You can use the IsSelected property easily to apply a custom styling of selected items.
You can try something like this in your view model, add if statement in setter:
private object _mySelectedItem;
public object MySelectedItem
{
get { return _mySelectedItem; }
set
{
if (_mySelectedItem != value && value != null)
{
_mySelectedItem = value;
OnPropertyChanged("MySelectedItem");
}
}
}

Button using dependency properties for a separate image per state

I have a Button control that I want to be able to reuse throughout project. Each time button enters a new state, a different image will be displayed. For now, I have Normal State and Pressed State.
Here's the XAML portion of the control:
<Button
x:Class="customImageButton.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentControl Width="80">
<Grid>
<Image Name="Normal" Source="{Binding NormalState}"/>
<Image Name="Pressed" Source="{Binding PressedState}" Visibility="Hidden"/>
</Grid>
</ContentControl>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Here's the code-behind for the control:
namespace customImageButton
{
public partial class ImageButton : Button
{
public ImageButton()
{
this.InitializeComponent();
}
public ImageSource NormalState
{
get { return base.GetValue(NormalStateProperty) as ImageSource; }
set { base.SetValue(NormalStateProperty, value); }
}
public static readonly DependencyProperty NormalStateProperty =
DependencyProperty.Register("NormalState", typeof(ImageSource), typeof(ImageButton));
public ImageSource PressedState
{
get { return base.GetValue(PressedStateProperty) as ImageSource; }
set { base.SetValue(PressedStateProperty, value); }
}
public static readonly DependencyProperty PressedStateProperty =
DependencyProperty.Register("PressedState", typeof(ImageSource), typeof(ImageButton));
}
}
...and here is its use:
<local:ImageButton Content="CustomButton" HorizontalAlignment="Left"
VerticalAlignment="Top" NormalState="Resources/Normal.png"
PressedState="Resources/Pressed.png"/>
My problem is that the images I've provided are not displaying. The Build Action for both images is Resource and I have tried using absolute path; however, that provided the same result. What am I missing?
Two problems with the code as listed:
The bindings need to be TemplateBinding.
The TargetType should refer to the "ImageButton" type, not Button.
Like this:
<ControlTemplate
xmlns:local="clr-namespace:customImageButton"
TargetType="{x:Type local:ImageButton}">
<Grid>
<ContentControl Width="80">
<Grid>
<Image Name="Normal" Source="{TemplateBinding NormalState}"/>
<Image Name="Pressed" Source="{TemplateBinding PressedState}" Visibility="Hidden"/>
</Grid>
</ContentControl>
</Grid>
Note: The above builds and runs, but Visual Studio complains:
'ImageButton' ControlTemplate TargetType does not match templated type 'Button'.
I suggest putting the control template in its own Style in the Themes/Generic.xaml resource dictionary, rather than inline.

Setting Label.Content to Control on DataTrigger

recently I've tried to make label style which would allow to display image or textblock depending on property being set or not. I've bound proper objects to labels' DataContext and prepared reusable style for these labels. Default content is textblock with Name as its text but if IsIconSet property is true, then content would change into image with corresponding IconPath as source.
Similar approach works perfectly with label's properties like background or cursor but in described scenario it breaks up when IsIconSet has the same value in both instances. Then it displays nothing for first label and correct textblock/image for second label.
I've tried to attach converter to Name and IconPath bindings in style in order to check what value is being passed but it seems that it isn't even invoked on first label.
Has anyone managed to do something similar? Am I missing something fundamental? Or maybe there is another approach for such behaviour?
Any help will be appreciate.
Simplified code:
MainWindow
<StackPanel DataContext="{Binding First}">
<Label Style="{StaticResource LabelStyle}" />
</StackPanel>
<StackPanel DataContext="{Binding Second}">
<Label Style="{StaticResource LabelStyle}" />
</StackPanel>
Style
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="{Binding Name}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsIconSet}" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{Binding IconPath}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Classes
public class ViewModel : INotifyPropertyChanged
{
private LabelClass _first;
private LabelClass _second;
public LabelClass First
{
get => _first;
set
{
_first = value;
OnPropertyChanged();
}
}
public LabelClass Second
{
get => _second;
set
{
_second = value;
OnPropertyChanged();
}
}
public ViewModel()
{
First = new LabelClass("First", "Resources/first.png");
Second = new LabelClass("Second", "Resources/second.png");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class LabelClass : INotifyPropertyChanged
{
private string _name;
private string _iconPath;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string IconPath
{
get => _iconPath;
set
{
_iconPath = value;
OnPropertyChanged();
OnPropertyChanged("IsIconSet");
}
}
public bool IsIconSet => !string.IsNullOrEmpty(IconPath);
public LabelClass(string name, string iconPath = null)
{
Name = name;
IconPath = iconPath;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
LabelStyle can be used by multiple Labels, but TextBlock and Image from Content setter are created just once, there is only one instance for all labels, but it cannot be displayed in multiple places. so it is displayed in only one.
to fix the issue use ContentTemplate, like shown below.
<Setter Property="Content" Value="{Binding}"/> line means that entire DataContext is considered as Label Content. Is is necessary for bindings in ContentTemplate
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsIconSet}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding IconPath}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
alternatively, convert TextBlock and Image to non-shared resources:
<Image Source="{Binding IconPath}" x:Key="Img" x:Shared="False"/>
<TextBlock Text="{Binding Name}" x:Key="Txt" x:Shared="False"/>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="Content" Value="{StaticResource Txt}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsIconSet}" Value="True">
<Setter Property="Content" Value="{StaticResource Img}"/>
</DataTrigger>
</Style.Triggers>
</Style>

Categories