I'm using MS Ribbon control in a C# WPF application. This control has several RibbonGroups
. One of these has RibbonButtons
which can change their background color when a certain event occurs.
As long as this RibbonGroup
is not collapsed, the colored RibbonButtons
are visible and user can take notice of the changed color. But if app window gets smaller and my RibbonGroup
is collapsed, the colored RibbonButtons
are out of the view.
I tried to change the background color of the RibbonGroup
, but this value is set to both parts - visible and invisible - of the collapsed RibbonGroup
.
Setting background color of the RibbonGroup.Header
colorises only the TextBlock
with title and moreover this action makes the down-arrow invisible when RibbonGroup
is collapsed.
This is what I'm aiming at:
Any ideas are appreciated!
--------- UPDATE -------------
My current implementation looks like this. I used Bindings
to set background colors of the Buttons
and a Multibinding
to set background of the RibbonGroup
and to respond to changes of IsCollapsed
and IsDropDownOpen
properties.
The problem of this approach is - I have to use the "right" color at the "right" moment: transparent when the group is not collapsed, light gray when menu is dropped down and so on.
...
xmlns:converters="clr-namespace:ControlFunctions.Converters"
...
<UserControl.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="RibbonBackground" Color="#f0f0f0" />
<converters:ButtonBackgroundToGroupBackgroundConverter x:Key="ButtonBackgroundToGroupBackgroundConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Ribbon>
<RibbonTab Header="Home">
<RibbonGroup x:Name="_functionGroup" Header="Functions">
<RibbonGroup.Background>
<MultiBinding Converter="{StaticResource ButtonBackgroundToGroupBackgroundConverter}" FallbackValue="{StaticResource RibbonBackground}" >
<Binding ElementName="_functionGroup" Path="IsCollapsed" />
<Binding ElementName="_functionGroup" Path="IsDropDownOpen" />
<Binding Path="Background_Button1" />
<Binding Path="Background_Button2" />
<Binding Path="Background_Button3" />
</MultiBinding>
</RibbonGroup.Background>
<RibbonButton Label="Button 1" Background="{Binding Path=Background_Button1}" />
<RibbonButton Label="Button 2" Background="{Binding Path=Background_Button2}" />
<RibbonButton Label="Button 3" Background="{Binding Path=Background_Button3}" />
</RibbonGroup>
</RibbonTab>
</Ribbon>
Converters.cs
public class ButtonBackgroundToGroupBackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//no value set yet
if (values[0] == DependencyProperty.UnsetValue || values[0] == null || values[1] == DependencyProperty.UnsetValue || values[1] == null) return Binding.DoNothing;
if ((bool)values[0] == false) return null; //the group is not collapsed -> no background color, leave it transparent
if ((bool)values[1]) return DependencyProperty.UnsetValue; //the group is collapsed AND menu is dropped down -> set Ribbon background color (=FallbackValue)
for (int i = 2; i < values.Length; i ) if (values[i] != null) return values[i]; //one of the buttons is colored -> use its color for the group
return null; //none of the buttons is colored -> no background color for the group, leave it transparent
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
CodePudding user response:
My solution isn't pretty, but it does work.
<!--A ribbon with SizeChanged event-->
<Ribbon DockPanel.Dock="Top" SizeChanged="Ribbon_SizeChanged">
<RibbonTab Header="One">
<!--A couple ribbon groups to fill up space-->
<RibbonGroup Header="One">
<RibbonButton Label="Button Number One"/>
</RibbonGroup>
<RibbonGroup Header="Two">
<RibbonButton Label="Button Number Two"/>
</RibbonGroup>
<!--The ribbon group we want to change the color of, in this case named "Group"-->
<RibbonGroup Header="Three" Name="Group">
<RibbonButton Label="Button Number Three"/>
</RibbonGroup>
</RibbonTab>
</Ribbon>
private void Ribbon_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (Group.IsCollapsed && IWantToChangeRibbonGroupColor)
{
//Find the RibbonToggleButton inside the ribbon group
var button = FindVisualChild<RibbonToggleButton>(Group);
button.Background = Brushes.Green;
}
}
//A common helper method I use for finding element children
public static ChildType FindVisualChild<ChildType>(DependencyObject obj) where ChildType : DependencyObject
{
int num = VisualTreeHelper.GetChildrenCount(obj) - 1;
for (int i = 0; i <= num; i )
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is ChildType)
{
return (ChildType)child;
}
child = FindVisualChild<ChildType>(child);
if (child != null)
{
return (ChildType)child;
}
}
return null;
}
This could be wrapped up into a neat little attached behavior, but I don't feel like going through that effort at the moment.
There isn't a property I know of that will let you change what you want, but there's nothing stopping you from digging through the visual tree and changing the background color manually. Using the visual tree debugging tools in Visual Studio, you can see what elements make up the collapsed RibbonGroup
.
In this case, it looks like the best interior element to change the color of would be the RibbonToggleButton
.
The aim of the code is to find out when the RibbonGroup
collapses, get a reference to the RibbonToggleButton
inside it, and then set Background
.
Unfortunately, there is no event I could find for when a RibbonGroup
switches in or out of its collapsed state. But at least there is a property: RibbonGroup.IsCollapsed
. I'm reasonably assuming that IsCollapsed
will only change when the size of the overall Ribbon changes, so I chose to check IsCollapsed
inside of Ribbon.SizeChanged
. (Note: I tried using RibbonGroup.SizeChanged
first, but this did not work correctly. The RibbonGroup
would sometimes revert to its original color.)
In the example code I gave, I got a reference to the RibbonGroup
I wanted to use by giving that RibbonGroup
a name, but as mentioned you could implement it other ways, such as by designing an attached behavior. Once I have the RibbonGroup
I loop through all the child elements, looking for that RibbonToggleButton
. When I find it, I set the background. This should continue to work so long as the ControlTemplate
for RibbonGroup
remains unchanged.
Depending on your implementation of IWantToChangeRibbonGroupColor
, you'll probably also want to run the color changing method when the value of IWantToChangeRibbonGroupColor
changes. So that the color will change even when the size of the ribbon doesn't.