ContextMenu not updating parent change - wpf

I have following, straightforward example
<ContextMenu x:Shared="false">
<MenuItem Header="UsuĊ„" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
<MenuItem CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="On"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Parent.IsEnabled}" Value="False">
<Setter Property="Header" Value="Off"/>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
For the first time when it is created it sets Header's value to On, but in time property IsEnabled is set to false and no change is reflected. Any help would be appreciated.

In this case it seems like it's because the element's Parent didn't change. Try using a different RelativeSource (example untested):
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1}}"
Value="False">

Related

Get selected TabControl name in DataTrigger binding

I have two TabControls in my window (tabQueryControl, and tabControl) and I've created style with a ContextMenu which I set to both the TabControls on a tab right click. However, depending on the tabcontrol which has been right clicked, I want to hide some context menu items. This is my code in the Style.
<Style x:Key="OutputContextMenuStyle" TargetType="{x:Type TextBlock}">
<Setter Property="ContextMenu" Value="{DynamicResource OutputContextMenu}"/>
</Style>
<ContextMenu x:Key="OutputContextMenu">
<MenuItem Header="View in DataViewer" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cw:ChromeWindow}}, Path=DataContext.ViewCommand}" CommandParameter="OutputWindow">
<MenuItem.Icon>
<Image Source="/Data_Viewer;component/Resources/NodeIcons/view_in_dataviewer.png"/>
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<!-- if the name of the parent tab control is tabQueryControl, we hide this context menu item -->
<DataTrigger Binding="{Binding Path=TabControl.Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}" Value="tabQueryControl">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TabControl.Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}" Value="tabControl">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
In the DataTrigger I try to get the name of the selected tab control, and set the visibility of the menu item depending on the name, but when I run the code the visibility is collapsed in both tabcontrols. I think the issue is in my binding for each data trigger.
First remove TabControl. from binding path.
After the binding works you can find, that the style doesn't work as expected. For both controls the menu item is visible. The problem is, that the triggers do work on the same ContextMenu instance.
To avoid it add x:Shared="false" to the ContextMenu:
<Style x:Key="OutputContextMenuStyle" TargetType="{x:Type TextBlock}">
<Setter Property="ContextMenu" Value="{DynamicResource OutputContextMenu}"/>
</Style>
<ContextMenu x:Key="OutputContextMenu" x:Shared="false">
<MenuItem Header="View in DataViewer" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cw:ChromeWindow}}, Path=DataContext.ViewCommand}" CommandParameter="OutputWindow">
<MenuItem.Icon>
<Image Source="/Data_Viewer;component/Resources/NodeIcons/view_in_dataviewer.png"/>
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<!-- if the name of the parent tab control is tabQueryControl, we hide this context menu item -->
<DataTrigger Binding="{Binding Path=Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}" Value="tabQueryControl">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}" Value="tabControl">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
So it should work as expected.

How to bind a DataTrigger to a non-bound attribute?

This is not in a template, it is a <Label> in the body of my XAML document. Notice that the Content of the label is set to 'PENDING'. This is monitoring a server connection and at various times the code-behind might change the value of the content to CONNECTED or ERROR. When that happens, I would like for the color of the text to change. I thought this would do it, but it doesn't... all I get is black text.
<Label x:Name="lbl_Connected" Content="PENDING" FontWeight="Bold" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="PENDING">
<Setter Property="Label.Foreground" Value="Yellow"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="ERROR">
<Setter Property="Label.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
Can someone tell me how I should be doing this?
Just remove the ".Value" part from the Binding's Path, i.e.:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
Anyway if I were you, I would use a Binding in order to set the Label content and a converter to handle the Foreground color.

Replace a text-converter dynamically

I have a textblock of a user-control which uses a converter to display figures. This converter gets loaded in a ResourceDictionary at the start of the application.
Now I'd like to exchange this converter with another one, to display a different format, depending on a parameter in the app-settings. Unfortunatley using just a trigger doesn't work. Is it possible to load a converter dynamically into the user-control, and put a reference on it from a textblock?
Edit: Here's my trigger attempt:
<TextBlock>
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type mycontrol}, AncestorLevel=1}, Path=mode}" Value="0">
<Setter Property="TextBlock.Text" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type cg:Fader}, AncestorLevel=1}, Path=Figure, Converter={StaticResource ConverterA}}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type mycontrol}, AncestorLevel=1}, Path=mode}" Value="1">
<Setter Property="TextBlock.Text" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type cg:Fader}, AncestorLevel=1}, Path=Figure, Converter={StaticResource ConverterB}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The reason why triggers are not working in your example is in bindings. Overall Style + DataTriggers are perfectly suitable for dynamic template selection.

WPF Styling and Templating over many buttons?

I can't seem to get the correct combination to get the desired effect:
Current XAML:
<Button Content="Foo" prism:Click.Command="{Binding FooCommand}"
Visibility="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={ncon:VisibilityBooleanConverter}}" />
<Button Content="Bar" prism:Click.Command="{Binding BarCommand}"
Visibility="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={ncon:VisibilityBooleanConverter}}" />
I want to extract out the Visibility="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={ncon:VisibilityBooleanConverter}}" as a style to apply over all the Buttons (within this UserControl resources). I can't seem to get the correct combination going here to make that happen.
Basically, what it does is instead of just disabling the button based on the ICommand.CanExecute it takes that DependencyProperty and binds it to the Visibility of the Button using a boolean-visiblity converter so the button is not only disabled, but also collapsed.
Style would look like this i suppose:
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility"
Value="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={ncon:VisibilityBooleanConverter}}"/>
</Style>
Doesn't that work?
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={ncon:VisibilityBooleanConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>

Why won't IsChecked change on this toggle button?

I have the following xaml for a toggle button:
<ToggleButton Margin="0,3" Grid.Row="3" Grid.ColumnSpan="2" Command="{Binding DataContext.SelectAllCommand, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="Select All"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Select None"/>
<Setter Property="Command" Value="{Binding DataContext.SelectNoneCommand, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
But the IsChecked property never gets updated when clicking the button.
If I use snoop and force the IsChecked to True then the trigger kicks in.
I tried your ToggleButton and it's working fine. The only problem I see with it is that you set Command explictly. It should be done with a Setter instead (like you did with Content) to not break the Trigger.
<ToggleButton Name="toggleButton" Margin="0,3" Grid.Row="3" Grid.ColumnSpan="2">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="Select All"/>
<Setter Property="Command"
Value="{Binding DataContext.SelectAllCommand,
Mode=OneWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Select None"/>
<Setter Property="Command" Value="{Binding DataContext.SelectNoneCommand, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
If you're still unable to Click it, I'd check if IsHitTestVisible or similar is set to False further up the Visual Tree.
If you want to compare your version to my test app to see what's not working, I uploaded it here: http://www.mediafire.com/?ik24ftsfw2wwfwb
Fixed it by parameterising my command, binding to a new property on my viewmodel and moving Command into the style:
<ToggleButton Margin="0,3" Grid.Row="3" Grid.ColumnSpan="2" IsThreeState="False"
IsChecked="{Binding DataContext.IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="Select All"/>
<Setter Property="Command" Value="{Binding DataContext.SelectAllCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Select None"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Don't you just love WPF sometimes!
Just a guess, but remove the Binding Mode=OneWay and try again.

Resources