Cannot set background color of ComboBox in Eto Forms

The attached, simple script for a form has a ComboBox on the top-right (The list). I want to change its background color. But nothing changes when I add this line to its control definition:

cb.BackgroundColor = Colors.Green

What is the proper way to do this or, despite being listed in the documentation for Eto Forms, does this not work for a ComboBox? Note that changing the background color of other controls on this form does work (see background behind the word: something).

image

ETO Example Revised.py (10.5 KB)

#Purpose: This script will demonstrate how to do different things with an ETO form

import rhinoscriptsyntax as rs
import Rhino
import Rhino.UI
import Eto.Drawing as drawing
#from Eto.Drawing import Colors 
from Eto.Drawing import Bitmap, Size, Font, FontStyle, Color, Colors, FontFamily, ColorHSL
import Eto.Forms as forms
from Eto.Forms import TextAlignment
from System.Collections.Generic import Dictionary
from scriptcontext import sticky

hues = [(233/255.,1,0.01),(233/255.,1,0.05),(233/255.,1,0.08),(233/255.,1,0.13),(233/255.,1,0.18), # 4
(233/255.,1,0.23),(233/255.,1,0.34),(233/255.,1,0.45),(230/255.,1,0.5),(223/255.,1,0.5), # 9
(216/255.,1,0.53), (210/255.,1,0.53),(206/255.,1,0.5),(202/255.,1,0.5),(198/255.,1,0.5), # 14 # Magenta = 213
(194/255.,1,0.5),(190/255.,1,0.5),(186/255.,1,0.5),(182/255.,1,0.5),(178/255.,1,0.5), # 19
(170/255.,1,0.5),(162/255.,1,0.5),(157/255.,1,0.5),(152/255.,1,0.5),(148/255.,1,0.5), # 24 # Blue = 170
(145/255.,1,0.5),(142/255.,1,0.5),(139/255.,1,0.5),(136/255.,1,0.5),(133/255.,1,0.5), #29
(130/255.,1,0.48),(128/255.,1,0.48),(122/255.,1,0.48),(116/255.,1,0.47),(108/255.,1,0.47), #34 # Blue_Green = 128
(100/255.,0.8,0.47),(90/255.,0.6,0.47),(85/255.,0.75,0.47),(68/255.,0.80,0.47),(58/255.,0.9,0.48), #39 # Green = 85
(49/255.,0.9,0.48),(43/255.,1,0.45),(40/255.,1,0.47),(36/255.,1,0.49),(33/255.,1,0.5), # 44 # Yellow = 42
#(30/255.,1,0.5),(27/255.,1,0.5),(24/255.,1,0.5),(21/255.,1,0.5),(18/255.,1,0.5), #49 # Orange = 21
(30/255.,1,0.5),(27/255.,1,0.5),(24/255.,1,0.5),(21/255.,1,0.5),(18/255.,1,0.5), #49 # Orange = 21
(15/255.,1,0.5),(12/255.,1,0.5),(9/255.,1,0.5),(8/255.,0.9,0.4375),(8/255.,0.9,0.375), # 54 # Red = 9
#(15/255.,1,0.5),(12/255.,1,0.45),(9/255.,1,0.4),(8/255.,0.9,0.4),(8/255.,0.9,0.375), # 54 # Red = 9
(8/255.,0.85,0.30),(8/255.,0.85,0.24),(8/255.,0.9,0.19),(8/255.,1,0.14),(8/255.,0.95,0.12), #59
(6/255.,0.8,0.1),(0/255.,0,0.15),(0/255.,0,0.25),(0/255.,0,0.35),(0/255.,0,0.45), #64
(0/255.,0,0.55),(0/255.,0,0.65),(0/255.,0,0.75),(0/255.,0,0.90),(0/255.,0,1.), #69
(0/255.,0,1.)] # Duplicate last color for interpolation of last point.


#================ This section defines sticky variables ========================
class TestDialogArgs():
	# Initializer
	def __init__(self):
		self.ABC = 0
		self.pickABC = 0
		self.expand = False
		self.box_visible = False
		self.box_something = 'Something'

#================ This is the section that defines the form ====================
class TestDialog(forms.Dialog[bool]):
	
	def __init__(self, args):
		if sticky.has_key('test_settings'): self.Args = sticky['test_settings']
		else: self.Args = args
		my_font_size = 10
		# Define the tool tips 
		ToolTips = Dictionary[str,str]()
		ToolTips['A'] = 'Pick one of these letters'
		ToolTips['The list'] = 'This combo box should change based off of the letter'
		ToolTips['OK'] = 'Perform the task'
		ToolTips['Cancel'] = 'Close this task'
		ToolTips['Text'] = 'Type something here'
		ToolTips['Expand'] = 'Expand the form'

		# Build the form 
		self.Title = 'Experiment with ETO'
		self.Padding = drawing.Padding(10)
		self.Resizable = False

		# Create a groupbox for the radio button list
		self.m_groupbox = forms.GroupBox(
			Font = Font('Segoe UI', my_font_size, FontStyle.Bold),
			Text = 'Pick a letter',
			Size = Size(150,90),
			Padding = drawing.Padding(0, 5, 0, 5),
			TextColor = Colors.Brown)
		grouplayout = forms.DynamicLayout()
		grouplayout.Spacing = drawing.Size(5, 5)
		# create a radio button list for the groupbox above
		self.m_radiobuttonlist = forms.RadioButtonList(
			DataStore = ['A', 'B', 'C'],
			ToolTip = ToolTips['A'],
			Orientation = forms.Orientation.Vertical,
			SelectedIndex = self.Args.ABC)
		self.m_radiobuttonlist.SelectedIndexChanged += self.OnRadiobuttonChange  # try to get the combo box to change with the radio buttons
		grouplayout.AddRow(self.m_radiobuttonlist)
		self.m_groupbox.Content = grouplayout
		self.m_radiobuttonlist.Enabled = True

		# create a group for the combo list box
		self.n_groupbox = forms.GroupBox(
			Text = 'The list',
			ToolTip = ToolTips['The list'],
			Padding = drawing.Padding(5, 5, 5, 5),
			Font = Font('Segoe UI', my_font_size, FontStyle.Bold),
			TextColor = Colors.Brown)
		grouplayout = forms.DynamicLayout()
		grouplayout.Spacing = drawing.Size(5, 5)
		# create a combo list box for the group above.
		cb = forms.ComboBox()
		cb.DataStore = ['first pick A', 'pick A', 'third pick A']
		cb.Font = Font('Segoe UI', my_font_size, FontStyle.Bold)
		cb.TextColor = Colors.Blue
		cb.BackgroundColor = Colors.Green
		cb.SelectedIndex = self.Args.pickABC
		cb.Enabled = True
		cb.MouseWheel += self.OnWheel # OK to specify delegate for event here in this format.
		self.n_combobox = cb
		"""
		self.n_combobox = forms.ComboBox(
			DataStore = ['first pick A', 'pick A', 'third pick A'],
			Font = Font('Segoe UI', my_font_size, FontStyle.Bold),
			TextColor = Colors.Blue,
			SelectedIndex = self.Args.pickABC,
			MouseWheel += self.OnMouseWheel) # No legal to include this here.
		"""
		grouplayout.AddRow(self.n_combobox)
		self.n_groupbox.Content = grouplayout

		# Create a label
		cl = forms.Label()
		cl.Text = 'Enter here'
		cl.Font = Font('Segoe UI', 11, FontStyle.Bold)
		cl.TextAlignment = TextAlignment.Left
		cl.TextColor = Colors.Green
		cl.MouseDown += self.OnUp
		self.m_label = cl
		"""
		self.m_label = forms.Label(
			Text = 'Enter here:',
			Font = Font('Segoe UI', 11, FontStyle.Bold),
			TextAlignment = TextAlignment.Left,
			TextColor = Colors.Green)
		"""
		num = 50
		# Create a text box
		self.m_textbox = forms.TextBox(
			Text = self.Args.box_something,
			Font = Font('Segoe UI', 10, FontStyle.Bold),
			TextColor = Colors.Brown,
			BackgroundColor = ColorHSL.ToColor(ColorHSL(hues[num][0],hues[num][1],hues[num][2])),
			ToolTip = ToolTips['Text'])

		# Create a checkbox for expanding the page
		self.n_checkbox = forms.CheckBox(
			Text = 'Expander',
			Checked = self.Args.expand,
			Font = Font('Segoe UI', 12, FontStyle.Bold),
			TextColor = Colors.Red,
			ToolTip = ToolTips['Expand'])
		self.n_checkbox.CheckedChanged += self.OnExpandCheckedChanged
		self.n_checkbox.MouseDoubleClick += self.OnUp
		
		# Create a text box
		self.not_visible = forms.TextBox(
			Visible = self.Args.box_visible,
			Text = 'Was not visible',
			Font = Font('Segoe UI', 10, FontStyle.Bold),
			TextColor = Colors.Brown,
			ToolTip = ToolTips['Text'])

		# main form Okay button
		self.DefaultButton = forms.Button(
			Text = 'OK',
			Font = Font('Segoe UI', 12, FontStyle.Bold),
			TextColor = Colors.Green,
			ToolTip = ToolTips["OK"])
		self.DefaultButton.Click += self.OnOKButtonClick

		# main form Cancel button
		self.AbortButton = forms.Button(
			Text = 'Cancel',
			Font = Font('Segoe UI', 12, FontStyle.Bold),
			TextColor = Colors.Red,
			ToolTip = ToolTips["Cancel"])
		self.AbortButton.Click += self.OnCloseButtonClick

		#layout the form
		layout = forms.DynamicLayout()
		layout.Spacing = drawing.Size(5, 5)
		layout.AddRow(self.m_groupbox, self.n_groupbox)
		layout.AddRow(None)
		layout.AddRow(self.m_label, self.m_textbox)
		layout.AddRow(None)
		layout.AddRow(self.n_checkbox)
		layout.AddRow(None)
		layout.AddRow(self.not_visible)
		layout.AddRow(None)
		layout.AddRow(self.DefaultButton, self.AbortButton)

		self.Content = layout
		
		# These 3 lines set RadioButton font to Bold. Use Default for regular font.
		self.Styles.Add[forms.RadioButton](None, self.setFont)
				
	def setFont(self, radioButton):
		radioButton.Font = drawing.SystemFonts.Bold()

	# this section created functions for the main script
	def GetRadioButton(self):
		return self.m_radiobuttonlist.SelectedIndex
	
	def OnWheel(self, sender, e):
		print e.Delta.Height
		i = 1 if e.Delta.Height > 0 else -1
		next = min(2, max(0, self.n_combobox.SelectedIndex + i))
		print 'Current index = {} next = {}'.format(self.n_combobox.SelectedIndex, next)
		self.n_combobox.SelectedIndex = next

	def OnUp(self, sender, e):
		a = 1
		print a
		
		# this section is for changing the combo list based off of the radio button selection
	def OnRadiobuttonChange(self, sender, e):
		if self.m_radiobuttonlist.SelectedIndex == 0:
			self.n_combobox.DataStore = ['first pick A', 'second pick A', 'third pick A']
			self.n_combobox.SelectedIndex = 0
			print "should be A"
			# does something go here?

		elif self.m_radiobuttonlist.SelectedIndex == 1:
			self.n_combobox.DataStore = ['first pick B', 'second pick B', 'third pick B']
			self.n_combobox.SelectedIndex = 0
			print "should be B"
			
		elif self.m_radiobuttonlist.SelectedIndex == 2:
			self.n_combobox.DataStore = ['first pick C', 'second pick C', 'third pick C']
			self.n_combobox.SelectedIndex = 0
			print "should be C"
	def OnExpandCheckedChanged(self, sender, e):
		if self.n_checkbox.Checked:
			self.not_visible.Visible = True
			self.ClientSize = drawing.Size(self.ClientSize.Width, self.ClientSize.Height + 25)
		else:
			self.not_visible.Visible = False
			self.ClientSize = drawing.Size(self.ClientSize.Width, self.ClientSize.Height - 25)
	
	def GetComboBox(self):
		return self.n_combobox.Text

	def GetText(self):
		return self.m_textbox.Text
		self.Close(False)
		
	def Expander((self, sender, e)):
		self.ClientSize = drawing.Size(200,200)

	def SaveArgs(self):
		# Save latest setting in Args so sticky can recover them at next startup.
		self.Args.ABC = self.m_radiobuttonlist.SelectedIndex
		self.Args.pickABC = self.n_combobox.SelectedIndex
		self.Args.expand = self.n_checkbox.Checked
		self.Args.box_visible = self.not_visible.Visible
		self.Args.box_something = self.m_textbox.Text

	def OnCloseButtonClick(self, sender, e):
		# Save settings.
		self.SaveArgs()
		sticky['test_settings'] = self.Args
		self.Close(True)
		print "This command was cancelled."
		exit()

	def OnOKButtonClick(self, sender, e):
		# Save settings.
		self.SaveArgs()
		sticky['test_settings'] = self.Args
		self.Close(True)

def TestETO():
	#========================= Use the ETO form ===============================
	args = TestDialogArgs()
	dialog = TestDialog(args);
	rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
	letter = dialog.GetRadioButton()
	if letter is None: return
	combo = dialog.GetComboBox()
	if combo is None: return
	test = dialog.GetText()
	if test is None: return

	#============================ test the output ==============================
	print letter
	print combo
	print test

if __name__ == "__main__":
	TestETO()

Regards,
Terry.

In WPF (which Eto targets at Windows) for some Controls you would need to change the style of the underlying Elements (or for a User Control which holds Data, the Style of the DataTemplate) which are/is (if I‘m not wrong) Textboxes arranged in a Grid here. I don‘t have any clue on how to do that within Eto since I usually do those things in WPF/XAML. But basically you need to understand that some controls consists of multiple child controls. And eventhough you change the Background this may not affect the Style then.
Other elements like ListViews work similar.

Tom,

I notice that there is a ComboBox.ApplyStyles for Eto Forms but have not discovered how to use this.

What would the code for doing this look like in WPF/XAML? Perhaps I can use that to find an equivalent in Eto Forms.

Regards,
Terry.

Ok Bad news. You completly need to override the default style. This is easy in Visual Studio in Xaml since you can copy it from the Mircosoft Template and completly customize it. But in Eto I have no idea on how to do this.

CustomColor

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2" x:Class="WpfApp1.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="500">

<!-- THE COMPLETE COMBOBOX - STYLE -->
    <Window.Resources>
        
        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle StrokeDashArray="1 2" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" Margin="2"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
<!-- THE CHANGE IS IN HERE -->
        <LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="HotPink" Offset="0.0"/>
            <GradientStop Color="Pink" Offset="0.5"/>
            <GradientStop Color="HotPink" Offset="1.0"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
        <SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
        <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
        <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
        <SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
        <LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFECF4FC" Offset="0.0"/>
            <GradientStop Color="#FFDCECFC" Offset="1.0"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
        <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
        <LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFEBF4FC" Offset="0.0"/>
            <GradientStop Color="#FFDCECFC" Offset="1.0"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
        <SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
        <LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFDAECFC" Offset="0.0"/>
            <GradientStop Color="#FFC4E0FC" Offset="1.0"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
        <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
        <LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFDAEBFC" Offset="0.0"/>
            <GradientStop Color="#FFC4E0FC" Offset="1.0"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
        <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
        <SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
        <Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="IsTabStop" Value="false"/>
            <Setter Property="Focusable" Value="false"/>
            <Setter Property="ClickMode" Value="Press"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border x:Name="templateRoot" SnapsToDevicePixels="true" Background="{StaticResource ComboBox.Static.Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{StaticResource ComboBox.Static.Border}">
                            <Border x:Name="splitBorder" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" SnapsToDevicePixels="true" Margin="0" HorizontalAlignment="Right" BorderThickness="1" BorderBrush="Transparent">
                                <Path x:Name="arrow" VerticalAlignment="Center" Margin="0" HorizontalAlignment="Center" Fill="{StaticResource ComboBox.Static.Glyph}" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z"/>
                            </Border>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                                    <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
                                    <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
                                    <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Border}"/>
                                <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Background}"/>
                                <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Border}"/>
                            </MultiDataTrigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.MouseOver.Glyph}"/>
                            </Trigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Border}"/>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Border}"/>
                                <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Background}"/>
                                <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Border}"/>
                            </MultiDataTrigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Pressed.Glyph}"/>
                            </Trigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Border}"/>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Border}"/>
                                <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Background}"/>
                                <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Border}"/>
                            </MultiDataTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Disabled.Glyph}"/>
                            </Trigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Border}"/>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
                                    <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Background}"/>
                                <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Border}"/>
                                <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Background}"/>
                                <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Border}"/>
                            </MultiDataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
            <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
                </Grid.ColumnDefinitions>
                <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                    <Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
                        <Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                            <ScrollViewer x:Name="DropDownScrollViewer">
                                <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                                    <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                        <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                                    </Canvas>
                                    <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Grid>
                            </ScrollViewer>
                        </Border>
                    </Themes:SystemDropShadowChrome>
                </Popup>
                <ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
                    <Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
                    <Setter Property="Color" TargetName="shadow" Value="#71000000"/>
                </Trigger>
                <Trigger Property="HasItems" Value="false">
                    <Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsGrouping" Value="true"/>
                        <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                </MultiTrigger>
                <Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
                    <Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
                    <Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
        <Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="AllowDrop" Value="true"/>
            <Setter Property="MinWidth" Value="0"/>
            <Setter Property="MinHeight" Value="0"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
            <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
            <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
                </Grid.ColumnDefinitions>
                <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                    <Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
                        <Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                            <ScrollViewer x:Name="DropDownScrollViewer">
                                <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                                    <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                        <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                                    </Canvas>
                                    <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Grid>
                            </ScrollViewer>
                        </Border>
                    </Themes:SystemDropShadowChrome>
                </Popup>
                <ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
                <Border x:Name="border" Background="{StaticResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
                    <TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                </Trigger>
                <Trigger Property="IsKeyboardFocusWithin" Value="true">
                    <Setter Property="Foreground" Value="Black"/>
                </Trigger>
                <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
                    <Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
                    <Setter Property="Color" TargetName="shadow" Value="#71000000"/>
                </Trigger>
                <Trigger Property="HasItems" Value="false">
                    <Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsGrouping" Value="true"/>
                        <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                </MultiTrigger>
                <Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
                    <Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
                    <Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
        <Style x:Key="ComboBoxStyle1" TargetType="{x:Type ComboBox}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource ComboBox.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ComboBox.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
            <Setter Property="Padding" Value="6,3,5,3"/>
            <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
            <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
            <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
            <Setter Property="Template" Value="{StaticResource ComboBoxTemplate}"/>
            <Style.Triggers>
                <Trigger Property="IsEditable" Value="true">
                    <Setter Property="IsTabStop" Value="false"/>
                    <Setter Property="Padding" Value="2"/>
                    <Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>




<!-- THE WINDOW VIEW -->


    <StackPanel>
        <ComboBox SelectedIndex="1" Style="{DynamicResource ComboBoxStyle1}" Margin="10" Width="300">


            <ComboBoxItem Content="Item1" Background="White"/>
            <ComboBoxItem Content="Item2" Background="Blue"/>

        </ComboBox>
    </StackPanel>
</Window>

Tom,

Woo! That is massive. So me thinks just putting up with a while background in the ComboBox’s will be fine for now. I should focus more on the maps that can be created by the script that I am working on like this elevation map with contours:

The elevation map with contours was created for this 3D mesh model of a site with 1000’s of logs:

Rhino enables us to measure the volume of the log piles using a second script:

Here is a perspective view of that log pile:

Thanks for taking a look at this as the code may come in handy in the future.

Regards,
Terry.

3 Likes

I mean for any User Control you can always change the default GUI as a last resort. It just results in the situation that you get all this data. But I mean I haven’t written it. Its just copying the Mircosoft default style which is really just one Click within Visual Studio. If you plan to distribute your software only on Windows PCs, then I would always go for WPF directly. There is a lot of benefit to it, although it can be a bit overwhelmin in the beginning. But having a good book about it, helps you in getting a way into it.

I started with WPF and have just finished converting to Eto Forms. This is why I was trying to adjust the ComboBox so it would look as nice as my WPF implementation. I want my scripts usable on Mac machines also so this is why I went to Eto Forms.

Just out of curiosity… How do you get the heightmap out of a drone made image?

The drone pictures are processed by a photogrammetry program (like Agisoft Metashape) which generates a 3D mesh model. I then export a .obj file of the mesh model and import this into Rhino. Then the model can be viewed in a Rendered View with its texture, reminding you of a big photo of the entire site, or in Shaded View with the mesh vertices colored by Z or Slope. Since it is an actual 3D model, all the height information is present and can be analyzed to find area, volume, height, etc. For a tree orchard planted on a grid, the height, canopy width and tree volume can be estimated for each of the 10,000 trees on an 80 acres site. From this the tree and orchard yield can be estimated. Rhino works well for this application because of it wealth of built in function and ease of adding custom functions.

1 Like

This is cool :slight_smile: :+1: Thanks for the info…

@Terry_Chappell
sorry for resurrecting this topic.
How did you solve this problem at the end? can you post the scripts associated with changing the bg color of ComboBox in Eto Forms?

1 Like

I see.
So you haven’t managed to solve the problem…

I have not worked on this for a while but when I look at my code I can find an example of changing the background color of a Button:

At the top of the script I have:
import Eto.Forms as forms

Down inside the script I have:

forms.Button(
Text = ‘Show Options’,
Width = 95,
Font = Font(‘Microsoft Sans Serif’, 8., FontStyle.Bold),
TextColor = EtoColors.Blue,
BackgroundColor = ShowOptionsButtonBackgroundColor,
ToolTip = ToolTips[‘ShowOptionsButton’])

where

ShowOptionsButtonBackgroundColor = EtoColorHSL(170,0,34)

If you change Button to ComboBox this may work for your case. But unless the Eto code has been updated since October of 2019, it will not.

Regards,
Terry.

Most controls have 1 foreground and 1 background color, but most control use more colors. If you want to change the complete appearance, you need to reinvent the user control, or you override the style.

But there is a problem… Eto is a multiplatform framework. This means the underlying UI framework is different on any operating system. If you change the style on Windows, you also need to change the style on macOS within that underlying framework. This is complicated. That is the tradeoff of such technologies. Limited control/access, but it works in both worlds.
If you want more control over the design, choose a specific framework and get rid of Eto. If you want to stick to Eto, you do yourself a favor of not changing the style.

I see. Many thanks for the answer @TomTom

I have another post here asking for help but didn’t get any reply

   Styles.Add<Eto.Wpf.Forms.Controls.GroupBoxHandler>("secondaryBox", h => { 
                h.Control.Background = System.Windows.Media.Brushes.Black;
                h.Control.BorderThickness = new System.Windows.Thickness(5);
                h.Control.BorderBrush = System.Windows.Media.Brushes.Red;
            });

I am trying to change the border’s color with Wpf.
It seems to me that I can’t change these unexposed style if I stick to Eto?

Ok so you are on Windows and you want to access the underlying WPF…

I’m sure there is a hack possible to do that. However, if you are on Windows, I would just create a custom WPF window in Visual Studio, put it into a custom plugin or into a plain .dll (loaded during runtime) and then just call this window in Rhino by doing:

// Create a custom WPF window with the VS Designer using the XAML markup
MyCustomView window = new MyCustomView();
// (if MVVM, inject the viewmodel)
window.DataContext = new MyCustomViewModel();
// open the window, use .ShowDialog() for a different behaviour
window.Show ();  

With this approach, you can make use of the powerful Visual Studio Designer, which also allows you to copy and override the style of any user control. See MSDN documentation pages on how to do that.
See my post from above regarding the ComboBox. But at first this is overwhelming and difficult.
So I would also suggest buying a book about WPF and just learn how this works. It has a steep learning curve, but it’s very powerful if you understand this technology, especially in combination with Visual Studio! Once you know it’s easy to create custom controls with advanced designs.

In this post I was showing this process, injecting a view from Python in Rhino using a custom WPF application in order to load in OxyPlot into Rhino. The cool thing here is that the Window was developed as a standalone application, which can be tested and run without using Rhino at all. I just reference the executable while executing the Python script.