Hey @RogerD,
Cool question, Here is a simple-ish way to achieve this in python, I think there are better ways with custom data structures but I wanted to keep this simple, that being said, this still feels tricky.
# Imports
import scriptcontext as sc
import Rhino
from Rhino.UI import RhinoEtoApp, EtoExtensions
from Eto.Drawing import Padding, Size
from Eto.Forms import Dialog, DropDown, DynamicLayout
from System.Collections.ObjectModel import ObservableCollection
dialog = Dialog()
dialog.Width = 300
dialog.Padding = Padding(8)
# One data source for simplicity. Dictionaries are always good.
data = {
100: [110, 120, 130],
110: [111, 112, 113],
120: [121, 122, 123],
130: [131, 132, 133],
200: [210, 220, 230],
210: [211, 212, 213],
220: [221, 222, 223],
230: [231, 232, 233],
300: [310, 320, 330],
310: [311, 312, 313],
320: [321, 322, 323],
330: [331, 332, 333],
}
# ObservableCollection will notify the UI of changes for us
dd1_data = ObservableCollection[object]()
dd1_data.Add(100)
dd1_data.Add(200)
dd1_data.Add(300)
dd2_data = ObservableCollection[object]()
dd3_data = ObservableCollection[object]()
dd1 = DropDown()
dd1.DataStore = dd1_data
dd2 = DropDown()
dd2.DataStore = dd2_data
dd3 = DropDown()
dd3.DataStore = dd3_data
# Cache indexes to avoid unnecessary state updates which can be a bit crashy
dialog.dd1_index = -1
dialog.dd2_index = -1
dialog.busy = False
def update_state(sender, args):
# Super safe busy check, prevents recursion!
if (dialog.busy):
return
dialog.busy = True
# Prevents crashing if there is no data, we should do this more, but I'm lazy
if dd1.SelectedIndex == -1:
return
if (dd1.SelectedIndex != dialog.dd1_index):
items = data[dd1.SelectedValue]
# Note we don't set a NEW value, we use the same collection
dd2_data.Clear()
for i in items:
dd2_data.Add(i)
dd2.SelectedIndex = 0
dd3.SelectedIndex = 0
dialog.dd2_index = 0
# This should run on 1 or 2 changing index
if (dd2.SelectedIndex != dialog.dd2_index or
dd1.SelectedIndex != dialog.dd1_index):
items = data[dd2.SelectedValue]
# Note we don't set a NEW value, we use the same collection
dd3_data.Clear()
for i in items:
dd3_data.Add(i)
dd3.SelectedIndex = 0
dialog.dd2_index = dd2.SelectedIndex
dialog.dd1_index = dd1.SelectedIndex
# We are no longer busy!
dialog.busy = False
dd1.SelectedIndexChanged += update_state
dd2.SelectedIndexChanged += update_state
dd1.SelectedIndex = 0
# Nice way to set up a UI with equal spacing
dynamic = DynamicLayout()
dynamic.Spacing = Size(4, 4) # Bit cramped otherwise
dynamic.BeginHorizontal()
dynamic.Add(dd1, True)
dynamic.Add(dd2, True)
dynamic.Add(dd3, True)
dynamic.EndHorizontal()
dialog.Content = dynamic
parent = RhinoEtoApp.MainWindowForDocument(sc.doc)
dialog.ShowModal(parent)