WPF Disabled DatagridRow and ContextMenu - wpf

I have a datagrid binded to a collection of custom objects.
This datagrid allows the user to access a context menu when he right clicks a row. I do this through TextBlock styling:
<Style x:Key="DatagridTextblockStyle"
TargetType="{x:Type TextBlock}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First action" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
It also displays rows which might be disabled if the custom object's "IsActive" bool property is false.
I do this through the DataGrid.RowStyle:
<DataGrid ItemsSource="{Binding MyCustomObjects}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}"
Value="True">
<Setter Property="IsEnabled"
Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
This works fine.
The problem however is that when a row is disabled, the context menu is not available anymore.
I can't find a way around that.
Any ideas?

Set the ContextMenuService.ShowOnDisabled attached property to true in the ElementStyle:
<Style x:Key="DatagridTextblockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="ContextMenuService.ShowOnDisabled" Value="True" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First action" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>

Related

How to change Button Image when ListView is empty through a trigger

I would like to change the image of an WPF Button on ListView empty, but I don't now which is the property I must set from the trigger.
<Style x:Key="DisableOnEmptyLvStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False"/>
<!-- which button property I must set here? -->
</DataTrigger>
</Style.Triggers>
</Style>
<Button Style="{StaticResource DisableOnEmptyLvStyle}">
<Image Source="{StaticResource myImage}"/>
</Button>
Once ListView has items, then the image must change to the normal one.
Any ideas how to do this?
You should never set the Content directly using a Style, simply because the Style is instantiated once and potentially reused across multiple controls. The issue is that the value that you assign in the Setter for Content is also instantiated once and shared, but a control can only have a single parent in WPF.
What can go wrong? If you set or change the Content in a style and reference it for e.g. multiple Buttons you will see that only the last button shows the image. The first button gets its image set as Content, then the next button sets the image as Content and effectively removes the content of the first button.
There are two options that you can consider.
Create the images as resources and force each reference in XAML to get a new instance of the image by setting the x:Shared attribute to False.
When set to false, modifies WPF resource-retrieval behavior so that requests for the attributed resource create a new instance for each request instead of sharing the same instance for all requests.
<Window.Resources>
<!-- ...your image sources for "myImage" and "myImage". -->
<Image x:Key="myImageConrol" x:Shared="False" Source="{StaticResource myImage}"/>
<Image x:Key="myOtherImageConrol" x:Shared="False" Source="{StaticResource myImage}"/>
<Style x:Key="DisableOnEmptyLvStyle" TargetType="{x:Type Button}">
<Setter Property="Content" Value="{StaticResource myImageConrol}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Content" Value="{StaticResource myOtherImageConrol}">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Button Style="{StaticResource DisableOnEmptyLvStyle}"/>
Create data templates and swap them out. This works because - as the name says - they are templates and its controls will be instantiated for each control they are applied to.
<Style x:Key="DisableOnEmptyLvStyle" TargetType="{x:Type Button}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{StaticResource myImage}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{StaticResource myOtherImage}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Button Style="{StaticResource DisableOnEmptyLvStyle}"/>
Please try the following:
<Style x:Key="DisableOnEmptyLvStyle" TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource myImageAtLeastOne}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource myImageEmpty}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Button Style="{StaticResource DisableOnEmptyLvStyle}" />

How to hide the menu item of context menu in wpf xaml based on condition

In my xaml, I have used wpf ContextMenu to display the menu items in wpf datagid. I need to hide the menu items based on the condition. I tried the following but its not working.
<ContextMenu x:Key="contextMenuTextCell">
<MenuItem Name="copyDealContextMenu"
Header="Copy Deal"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.CopyDeal}"
CommandParameter="{Binding}">
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{ Binding ElementName= BlotGrid,Path=DataContext.ProductType }" Value="FXO">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem>
</ContextMenu>
How to hide the menu items in context menu?
Thanks
There are 2 reason this did not work.
1) is that ContextMenu does not reside in the same VisualTree as the element it is set for ( i.e. it's PlacementTarget). There for you would not be able to bind to an element with ElementName.
2) you put your Style like it's a Content of the MenuItem. ( I didn't notice it at first as well..). it needs to be set to the DependencyProperty 'Style' of your MenuItem.
<Grid x:Name="BlotGrid" Background="Red">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Name="copyDealContextMenu"
Header="Copy Deal"
CommandParameter="{Binding}">
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget.DataContext.IsXXX}" Value="True">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>
</Grid>

Set a trigger's property to a child

How can I set the trigger to be depend on a children's property?
Like I want to change the header of my Expander depending whether the Expander's ListView
does have children or not.
But I always get an Comilor error, that HasItems can not be resolved...
<Expander Header="Expand to add new ports">
<Expander.Resources>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="Content.HasItems" Value="False">
<Setter Property="Header" Value="No children" />
</Trigger>
</Style.Triggers>
</Style>
</Expander.Resources>
<ListView ItemsSource="{Binding Path=SomeItems}">
</ListView>
You can use a DataTrigger bound to the ListView using ElementName:
<Expander>
<Expander.Resources>
<Style TargetType="{x:Type Expander}">
<Setter Property="Header" Value="Expand to add new ports" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ListView, Path=HasItems}" Value="False">
<Setter Property="Header" Value="No children" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Resources>
<ListView x:Name="ListView" ItemsSource="{Binding Path=SomeItems}">
</ListView>
</Expander>
Also note that if you set a property along with the declaration of the control the Setter in the trigger won't have any effect.
Use this:
<Setter Property="Header" Value="Expand to add new ports" />
Instead of this:
<Expander Header="Expand to add new ports">
There are multiple issues in your code -
First, you should set the property in style setter instead of declaring it locally.
Second, use DataTrigger in place of Trigger.
<Expander>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Header" Value="Expand to add new ports"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomeItems.Count}" Value="0">
<Setter Property="Header" Value="No children" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<ListView ItemsSource="{Binding Path=SomeItems}">
</ListView>
</Expander>

WPF display message instead of menu items in menu when bound menus list is empty

I have a menu that looks something like this:
<MenuItem x:Name="menu" ItemsSource="{Binding Items}" Style="{StaticResource ItemStyle}">
<MenuItem.Header>
...
</MenuItem.Header>
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="PropertyNameOne" Value="{Binding}"/>
<Setter Property="PropertyNameTwo" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Items is a NotifyingCollection. However, when the Items list is empty, I would like to display a message, like "No items in menu.", instead of the current behavior, which is a tiny little empty box. Is there a way to do this just in XAML?
You could use a DataTrigger to set the ItemContainerStyle:
<MenuItem x:Name="menu">
<MenuItem.Header>
...
</MenuItem.Header>
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem} BasedOn="{StaticResource ItemStyle}">
<Setter Property="ItemsSource" Value="{Binding Items}">
<Setter Property="MenuItem.ItemContainerStyle">
<Setter.Value>
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="PropertyNameOne" Value="{Binding}"/>
<Setter Property="PropertyNameTwo" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count}">
... Alternate Content ItemsSource and ttemContainerStyle
</DataTrigger>
</Style.Triggers>
...
That's the general idea anyway, I'm sure you can fill in the details with your particular needs. (i.e., defining a disabled child MenuItem with your placeholder text)

Using Picture or Icon instead of DataGridCheckBoxColumn in WPF

I want to change the CheckBox which is inside of a DataGridColumn into an image when it is checked and another when it is unchecked, How can i do ?
Ps: My DataGridCheckBoxColumn is defined like this:
<DataGridCheckBoxColumn Header="Priority" Binding="{Binding PRIORITY, Converter={StaticResource converter}}"/>
Converter is converting bytes to boolean.
Use the ElementStyle and EditingElementStyle properties to create and set a different Template for the CheckBox which fits that.
e.g.
<DataGridCheckBoxColumn Binding="{Binding IsActive}">
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image MaxWidth="32" MaxHeight="32">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Images/Error.ico" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}}" Value="True">
<Setter Property="Source" Value="Images/Default.ico" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
This makes the column display an image based on IsChecked, the URIs are just hardcoded and the CheckBox is disabled because editing in the ElementStyle does not change any properties on the bound object. Its only purpose is to display the approriate image.
(The EditingElementStyle is not set here so if the user clicks the cell again to edit it a normal CheckBox appears which can be checked or unchecked.)

Resources