MenuItem.Icon appears only on the last item - wpf

I have a Menu with the following style on the items:
<Style TargetType="MenuItem">
<Setter Property="Template" Value="{StaticResource MenuItem}"/>
<Setter Property="Icon">
<Setter.Value>
<TextBlock FontWeight="Bold">Ic</TextBlock>
</Setter.Value>
</Setter>
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="ItemsSource" Value="{Binding SubItems}"/>
</Style>
(Name, and SubItems are properties of the class MenuItem.
the ItemsSource property of the menu is bound to an object of type List<MenuItem> )
problem is the "Icon" part appears only on the last item of the menu:
Furthermore, if I click to expand the "Playlist" Item here's what happens:
I ultimately want to bind each Item to its own Icon, but that doesn't seem to work either.
Any ideas what causes this misbehavior and how to fix it?
update I've seen this: MenuItem style with icon creates only one icon
but it didn't work for me because a. x:Shared=false made a XamlParseException and b. if I moved it out of the Style.Resources it didn't make an exception but simply didn't work. Note that I do need this INSIDE the Style because ultimately I want it to be bound to a property of the class I'm binding the MenuItem to.

Don't Share the TextBlock
<TextBlock x:Key="tb" x:Shared="false" FontWeight="Bold">Ic</TextBlock>
<Style TargetType="{x:Type MenuItem}">
...
<Setter Property="Icon" Value="{StaticResource tb}"/>
</Style>

Related

XAML separate binding of ContainerStyle and ContentTemplate in ListBox

I tried this for a while now and searched in the web without success... Now I dare to ask on stackoverflow myself.
So, the aim is to separate the definitions of the ItemContainerStyle and the ContentTemplate of a ListBoxItem. I have defined the ListBoxItemStyle in a ResourceDictionary and two different DataTemplates in the Window.Resources.
I now like to set the style of the ListBoxItem according to the one defined in the ResourceDictionary and change the DataTemplate with a trigger (IsMouseOver).
My (not working) Code so fare looks like this:
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20,60,10,10"
Grid.Row="1" Grid.Column="0"
PreviewMouseMove="DragMove_PreviewMouseMove"
PreviewMouseLeftButtonDown="Drag_PreviewMouseLeftButtonDown"
ItemsSource="{Binding Persons}" VerticalContentAlignment="Center"
Style="{StaticResource DefaultListBoxStyle}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Style" Value="{StaticResource DefaultListBoxItemStyle}"/>
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_default}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_infoButtons}"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
where DefaultListBoxStyle is the style defined in the ResourceDictionary and PersonsListBoxItemTemplate_default & PersonsListBoxItemTemplate_infoButtons are the DataTemplates defined in the Window.Resources.
Now I get an error, that the style-object must not have an impact on the style-property of the object it belongs to.
I tried different ways, but without success... I also tried to define the style first and then change the template, but it does not work either.
Thank you all for your help!
So you can't set Style Property in Style with Setter. For this you need to use BasedOn:
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DefaultListBoxItemStyle}">
<Setter ... />
</Style>

Problems with WPF ToolTipService properties (BetweenShowDelay, etc.) in Microsoft.Windows.Controls.Ribbon control

I have an very, very huge application in WPF with a Ribbon in it. The ribbon contain a bunch of RibbonControls, each binded to a different command. On every control, we put a ToolTip. We overrided these ToolTip templates to use a control of our own, which gives more information. We could call il a superToolTip.
The override of the tooltip templates is working just fine. Now we want to unify the way the tooltips are showing. What I mean is we want the same initialShowDelay, ShowDuration, etc., for every single tooltips in the application (there are tooltips elsewhere than in the ribbon, which use the same home made control that the ones of the ribbon). So, I binded the ToolTipService.InitialShowDelay, ToolTipService.BetweenShowDelay, ToolTipService.ShowDuration properties to global constants in the application.
InitialShowDelay :
The property InitialShowDelay is working just fine for almost every control in the application... The only one not working is RibbonSplitButton, which keep the default value of 400...
BetweenShowDelay :
The property BetweenShowDelay is working just fine when the tooltip is on a ListBoxItem... but not working in the Ribbon nor in a complex control of our own (a Property Grid).
These properties are set in the control on which the tooltip is set, and not on the tooltip themselves.
I honestly have absolutely no idead why it is behaving this way... Anyone has any idea on what could cause this or how to solve it?
If you need more information, do not hesitate to ask, i really am desperate about this.
Thank you very much!
The problem was that the condition for BetweenShowDelay wasn't respected, you need to have a value set for the property "ToolTip", in this case you were using a template, so the value was at null. You can resolve it this way :
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Utils:ToolTipControl DataContext="{Binding ToolTipInfo}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and then place the dummy in the specified button :
<!-- RibbonButton -->
<Style TargetType="{x:Type ribbon:RibbonButton}" BasedOn="{StaticResource RibbonControlStyle}" >
<Style.Triggers>
<DataTrigger Value="true" >
<DataTrigger.Binding>
<Binding Converter="{StaticResource IsBoundConverter}" />
</DataTrigger.Binding>
<Setter Property="Command" Value="{Binding}" />
<Setter Property="ribbon:RibbonControlService.Label" Value="{Binding Name}" />
<Setter Property="ribbon:RibbonControlService.SmallImageSource" Value="{Binding Icon}" />
<Setter Property="ribbon:RibbonControlService.LargeImageSource" Value="{Binding LargeIcon}" />
<Setter Property="Visibility" Value="{Binding Visibility}" />
<Setter Property="ToolTip" Value="dummy"/> <!-- Use dummy value to force tooltip to show and to Bind the tooltip-->
</DataTrigger>
<DataTrigger Value="false" >
<DataTrigger.Binding>
<Binding Converter="{StaticResource IsBoundConverter}" />
</DataTrigger.Binding>
<Setter Property="Background" Value="#FF900000" />
</DataTrigger>
</Style.Triggers>
</Style>
That way the dummy value will be override.
:D
Here is some code showing how I implemented my ToolTips
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
...>
...
<!-- Ribbon Tooltips Style -->
<Style TargetType="ribbon:RibbonToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Utils:ToolTipControl DataContext="{Binding ToolTipInfo}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<!-- RibbonControl -->
<Style x:Key="RibbonControlStyle">
<Setter Property="ribbon:RibbonControlService.ToolTipTitle" Value="dummy" /><!-- Use dummy value to force tooltip to show -->
<Setter Property="ToolTipService.InitialShowDelay" Value="{x:Static Utils:ToolTipViewModel.ToolTipInitialDelay}"/>
<Setter Property="ToolTipService.ShowDuration" Value="{x:Static Utils:ToolTipViewModel.ToolTipShowDuration}"/>
<Setter Property="ToolTipService.BetweenShowDelay" Value="{x:Static Utils:ToolTipViewModel.ToolTipBetweenShowDelay}"/>
<!-- This style is used to select the "Editors" tab when opening Editor without a world, and to select the "Home" tab otherwise -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsWorldLoaded, Source={x:Static ViewModels:ViewportSettingsViewModel.Instance}}" Value="false">
<Setter Property="ribbon:Ribbon.SelectedIndex" Value="2"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsWorldLoaded, Source={x:Static ViewModels:ViewportSettingsViewModel.Instance}}" Value="true">
<Setter Property="ribbon:Ribbon.SelectedIndex" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
Also, for the splitbutton problem it was that the tooltipservice werent set for the child of the splitbutton ( the two parts) wich are named PART_HeaderButton and PART_ToggleButton. So even if you create your own style it will be override by the style of the ribbonsplit button ( see this link for the splitbutton.xaml file :
https://wpfcontrolextension.svn.codeplex.com/svn/trunk/Common/RibbonControlsLibrary/v3.5/Themes/Generic.xaml
So to bypass this overriding problem ( because we don't have access to the part directly we have to go via code. Im my case, I overrided the RibbonSplitButton class and the OnLoadTemplate method. That way with getchild we can access the property of the part and change them.
public partial class DuniaRibbonSplitButton : RibbonSplitButton
{
public DuniaRibbonSplitButton()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var HeaderButton = base.GetTemplateChild("PART_HeaderButton");
var ToggleButton = base.GetTemplateChild("PART_ToggleButton");
OverrideAttributes(HeaderButton as Control);
OverrideAttributes(ToggleButton as Control);
}
private void OverrideAttributes(Control control)
{
control.ToolTip = "Dummy";
ToolTipService.SetInitialShowDelay(control, ToolTipViewModel.ToolTipInitialDelay);
ToolTipService.SetShowDuration(control, ToolTipViewModel.ToolTipShowDuration);
ToolTipService.SetBetweenShowDelay(control, ToolTipViewModel.ToolTipBetweenShowDelay);
}
}

Use GroupSizeReductionOrder in RibbonControlsLibrary with MVVM

I am using the RibbonControlsLibrary (.Net 4.0) for my ribbons. My implementation is completely realized using MVVM.
Now I like to use the benefit of the resizing feature. But I can't set the reduction order of the group boxes. This is because I have to define the names of the group boxes in a special order. I can't set the names of the group boxes, because I am using data templates.
I tried to bind the name of the ribbon group box user controls to a property on the datacontext, but as I supposed this doesn't work.
So, do I have any chance to set the ribbon group box user control names to a property of the data context?
Setting the RibbonGroups' names
There may be others solutions, mine is to implement a Behavior that sets the RibbonGroup's name in the OnAttached method.
public class RibbonGroupNameBehavior : Behavior<RibbonGroup>
{
protected override void OnAttached()
{
base.OnAttached();
GroupeCommandesViewModel groupeCommandesViewModel = this.AssociatedObject.DataContext as GroupeCommandesViewModel;
if (groupeCommandesViewModel != null)
{
this.AssociatedObject.Name = groupeCommandesViewModel.Nom;
}
}
}
You may want to add a try-catch block around the assignment...
Using the behavior
As you are using DataTemplates, I think you'll have to assign the behavior to the RibbonGroups with a Style.
I have been using this solution with success.
As you'll see, my behavior's code does not provide the required dependency property, you'll have to add it.
Here is my shortened Ribbon's style :
<r:Ribbon.Style>
<Style TargetType="{x:Type r:Ribbon}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type r:RibbonTab}">
<Setter Property="ItemsSource" Value="{Binding ListeCommandes, Converter={StaticResource CommandesToGroupesConverter}}" />
<Setter Property="GroupSizeReductionOrder" Value="{Binding ListeCommandesOrdreRedimensionnement}" />
<Setter Property="ItemContainerStyleSelector">
<Setter.Value>
<ribbonVM:RibbonGroupStyleSelector>
<ribbonVM:RibbonGroupStyleSelector.GenericStyle>
<Style TargetType="{x:Type r:RibbonGroup}" BasedOn="{StaticResource RibbonGroupStyle}" />
</ribbonVM:RibbonGroupStyleSelector.GenericStyle>
</ribbonVM:RibbonGroupStyleSelector>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</r:Ribbon.Style>
And my RibbonGroup's style :
<Style x:Key="RibbonGroupStyle" TargetType="{x:Type r:RibbonGroup}" BasedOn="{StaticResource RibbonControlStyle}">
<Setter Property="Header" Value="{Binding Libellé}" />
<Setter Property="ItemsSource" Value="{Binding Commandes}" />
<Setter Property="CanAddToQuickAccessToolBarDirectly" Value="False" />
<Setter Property="ihm_behaviors:RibbonGroupNameBehavior.IsEnabled" Value="True" />
</Style>
Databinding the GroupSizeReductionOrder property
If you look at the definition of the GroupSizeReductionOrder property, you'll see it's a StringCollection with a TypeConverter StringCollectionConverter.
From what I have seen, this converter only converts from string to StringCollection.
So your property shoud be either a string or a StringCollection.

WPF Context Menu for each Row in a GridView

I have a GridView which is has several columns which are all bound to a data structure in my ViewModel layer, so what I want to do is to
Have a Context Menu that will pop up when the user clicks on one of the rows of data in the GridView (so far I have only been able to make the context menu appear when I click on the Header of the column I put it on.
Have the contents of the context menu vary depending on the contents of the current row the user has clicked on.
Any suggestions? thanks
You can set up the ContextMenu in the ItemContainerStyle, to make it dependent on the row you could employ some DataTriggers.
Edit: Structure should take the following form if you decide to swap out the whole ContextMenu on a condition:
<Style TargetType="{x:Type ListViewItem}">
<Style.Resources>
<ContextMenu x:Key="DefaultMenu">
<!-- Rest of menu here -->
</ContextMenu>
<ContextMenu x:Key="NonDefaultMenu">
<!-- Rest of menu here -->
</ContextMenu>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding SomeProperty}" Value="SomeValue">
<Setter Property="ContextMenu" Value="{StaticResource NonDefaultMenu}"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="ContextMenu" Value="{StaticResource DefaultMenu}"/>
</Style>
You normally could also define the ContextMenu in place as well but if you add event-handlers it gets buggy, the syntax would be:
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<!-- Rest of menu here -->
</ContextMenu>
</Setter.Value>
</Setter>

Can you define multiple TargetTypes for one XAML style?

In HTML/CSS you can define a style which can be applied to many types of elements, e.g.:
.highlight {
color:red;
}
can be applied to both P and DIV, e.g.:
<p class="highlight">this will be highlighted</p>
<div class="highlight">this will also be highlighted</div>
but in XAML you seem to have to define the TargetType for styles, otherwise you get an error:
<Style x:Key="formRowLabel" TargetType="TextBlock">
is there a way to allow a XAML style to be applied to multiple elements or even to leave it open as in CSS?
The setters in WPF styles are checked during compile time; CSS styles are applied dynamically.
You have to specify a type so that WPF can resolve the properties in the setters to the dependency properties of that type.
You can set the target type to base classes that contain the properties you want and then apply that style to derived classes. For example, you could create a style for Control objects and then apply it to multiple types of controls (Button, TextBox, CheckBox, etc)
<Style x:Key="Highlight" TargetType="{x:Type Control}">
<Setter Property="Foreground" Value="Red"/>
</Style>
...
<Button Style="{StaticResource Highlight}" Content="Test"/>
<TextBox Style="{StaticResource Highlight}" Text="Test"/>
<CheckBox Style="{StaticResource Highlight}" Content="Test"/>
<!-- Header text style -->
<Style x:Key="headerTextStyle">
<Setter Property="Label.VerticalAlignment" Value="Center"></Setter>
<Setter Property="Label.FontFamily" Value="Trebuchet MS"></Setter>
<Setter Property="Label.FontWeight" Value="Bold"></Setter>
<Setter Property="Label.FontSize" Value="18"></Setter>
<Setter Property="Label.Foreground" Value="#0066cc"></Setter>
</Style>
<!-- Label style -->
<Style x:Key="labelStyle" TargetType="{x:Type Label}">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
I think both of these methods of declaring a style might answer your question.
In the first one, there is no TargetType specified, but the property names are prefixed with 'Label'. In the second one, the style is created for Label objects.
Another method to do it is:
<UserControl.Resources>
<Style x:Key="commonStyle" TargetType="Control">
<Setter Property="FontSize" Value="24"/>
</Style>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ListBox"/>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ComboBox"/>
</UserControl.Resources>
I wanted to apply a style to a Textblock and a TextBox but the selected answer didn't work for me because Textblock doesn't inherit from Control, in my case I wanted to affect the Visibility property, so I used FrameworkElement
<Style x:Key="ShowIfRequiredStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowIfRequiredStyle, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
<TextBox Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
This works for the Visibility property because both items inherit from Frameworkelement and the property is defined there. Of course this will not work for properties defined only in Control, you can search the hierarchy tree and try to find a base class, anyway I thought this could help someone since this is a top search result and the selected answer is a little incomplete.
There is an alternative answer to the question. You CAN leave the TargetType parameter off the style altogether which will allow it to apply to various different controls, but only if you prefix the property name with "Control."
<Style x:Key="Highlight">
<Setter Property="Control.Foreground" Value="Red"/>
</Style>
Obviously, this only works for properties of the base control class. If you tried to set ItemsSource say, it would fail because there is no Control.ItemsSource
I got this working
<Style x:Key="HeaderStyleThin" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Black" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background=" Value="Red" />
</Style>
</Style.Resources>
</Style>

Resources