I am working on a winform with a combobox…and I want to have key and value binding to the combobox.
my combobox is using this code:
self.levelcombo.DataSource = mycomboboxitem
self.levelcombo.DisplayMember = "key"
self.levelcombo.ValueMember = "value"
where I have already made a class:
class comboboxitem():
def __init__(self, key, value):
self.key = key
self.value = value
def value(self):
return self.__dict__.values()
def key(self):
return self.__dict__.keys()
and I try to bind them together:
mycomboboxitem = comboboxitem(list1, list2)
but it never succeeds
I am sure something is wrong but just cant figure it out
To make a combobox you need a list. It could be any kind of list actually but if you want to have a sensible name to display it would need to be a list of Objects.
let’s say this is your Object definition:
class MyItem():
def __init__(self, key, value):
self.Key = key
self.Value = value
it requires a key and a value variable to be initiated and then has a Key and Value attributes assigned to the initial keys and values.
let’s say you have this initial data:
keys = ["a", "b", "c"]
values = [10,20,30]
you can create a list of your objects from these two lists like so:
list_of_items = [MyItem(i,j) for i,j in zip(keys, values)]
Then in your WPF window your combobox needs to have this list as its ItemsSource:
self.levelcombo.ItemsSource = list_of_items
and then to show the key items as visible in the Combobox you need to assign its DisplayMemberPath to the attribute of your object:
self.levelcombo.DisplayMemberPath ="Key"
6 Likes
You can also use DataTable
an example
import clr
import sys
import System
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('System.Data')
from System.Data import DataTable
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
class MainForm(Form):
def __init__(self):
allViewFamilyType = FilteredElementCollector(doc).OfClass(ViewFamilyType).WhereElementIsElementType()
arrayGroupType = [[Element.Name.GetValue(g), g] for g in allViewFamilyType]
self._tableViewFamType = DataTable("ViewFamilyType")
self._tableViewFamType.Rows.Add() # add a empty row
self._tableViewFamType.AcceptChanges()
self._tableViewFamType.Columns.Add("Name", System.String)
self._tableViewFamType.Columns.Add("ViewType", DB.Element)
# populate dataTable
[self._tableViewFamType.Rows.Add(i, j) for i, j in arrayGroupType]
self.choice = None
self.InitializeComponent()
def InitializeComponent(self):
#
self._comboBoxType = System.Windows.Forms.ComboBox()
self._comboBoxType.Location = System.Drawing.Point(40, 50)
self._comboBoxType.Size = System.Drawing.Size(221, 21)
self._comboBoxType.DataSource = self._tableViewFamType # or self._tableViewFamType.Copy()
self._comboBoxType.DisplayMember = "Name"
self._comboBoxType.ValueMember = "ViewType"
self._comboBoxType.DropDownStyle = ComboBoxStyle.DropDownList # force ready only
self._comboBoxType.SelectedIndexChanged += self.ComboBox1SelectedIndexChanged
#
self.ClientSize = System.Drawing.Size(300, 200)
self.Controls.Add(self._comboBoxType)
self.Name = "MainForm"
self.Text = "MainForm"
self.ResumeLayout(False)
def ComboBox1SelectedIndexChanged(self, sender, e):
if isinstance(sender.SelectedItem['ViewType'], System.DBNull):
self.choice = None
else:
self.choice = sender.SelectedItem['ViewType']
objForm = MainForm()
objForm.ShowDialog()
OUT = objForm.choice
4 Likes
@viktor_kuzev thank you so much!!!
@c.poupin I will study this after combobox, thanks!
@viktor_kuzev one more thing, when I try to display a default value when the script runs, I try to set using:
self.levelcombo.SelectedIndex = 2
but it shows error:
ValueError: InvalidArgument=Value of ‘2’ is not valid for ‘SelectedIndex’.
Parameter name: SelectedIndex
do you have any idea?
do you mind sharing more of your code
the combobox code:
self.levelcombo = SF.ComboBox()
self.levelcombo.Name = "levelcombo"
self.levelcombo.DropDownStyle = SF.ComboBoxStyle.DropDownList
self.levelcombo.DataSource = mycomboboxitem
self.levelcombo.DisplayMember = "key"
self.levelcombo.ValueMember = "value"
self.levelcombo.SelectedIndex = 2
self.levelcombo.Location = SD.Point(270, 45)
self.levelcombo.Size = SD.Size(150, 20)
self.Controls.Add(self.levelcombo)
something like that
do I need to add “SelectedIndexChanged”?
With a List, you need to use Items
property
here another example
import clr
import sys
import System
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
from collections import namedtuple
class MainForm(Form):
def __init__(self, pyDict):
MyItem = namedtuple('MyItem', ['Key', 'Value'])
self._lstObj = [MyItem(key_, value_) for key_, value_ in pyDict.items()]
self.choice = None
self.InitializeComponent()
def InitializeComponent(self):
#
self._comboBoxType = System.Windows.Forms.ComboBox()
self._comboBoxType.Location = System.Drawing.Point(40, 50)
self._comboBoxType.Size = System.Drawing.Size(221, 21)
self._comboBoxType.Items.AddRange(System.Array[System.Object](self._lstObj))
self._comboBoxType.DisplayMember = "Key"
self._comboBoxType.ValueMember = "Value"
self._comboBoxType.SelectedIndexChanged += self.ComboBox1SelectedIndexChanged
#
self.ClientSize = System.Drawing.Size(300, 200)
self.Controls.Add(self._comboBoxType)
self.Name = "MainForm"
self.Text = "MainForm"
self.ResumeLayout(False)
def ComboBox1SelectedIndexChanged(self, sender, e):
self.choice = sender.SelectedItem.Value
self.Close()
mydict = {'a': 1, 'b': 2, 'c' : 3}
objForm = MainForm(mydict)
objForm.ShowDialog()
OUT = objForm.choice
3 Likes
@c.poupin it works with DataSource for me:
import clr
import sys
import System
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
from collections import namedtuple
class MyItem():
def __init__(self, key, value):
self.Key = key
self.Value = value
keys = ["a", "b", "c"]
values = [10,20,30]
list_of_items = [MyItem(i,j) for i,j in zip(keys, values)]
class MainForm(Form):
def __init__(self):
self.choice = None
self.InitializeComponent()
def InitializeComponent(self):
#
self._comboBoxType = System.Windows.Forms.ComboBox()
self._comboBoxType.Location = System.Drawing.Point(40, 50)
self._comboBoxType.Size = System.Drawing.Size(221, 21)
self._comboBoxType.DataSource = list_of_items
self._comboBoxType.DisplayMember = "Key"
self._comboBoxType.ValueMember = "Value"
self._comboBoxType.SelectedIndexChanged += self.ComboBox1SelectedIndexChanged
#
self.ClientSize = System.Drawing.Size(300, 200)
self.Controls.Add(self._comboBoxType)
self.Name = "MainForm"
self.Text = "MainForm"
self.ResumeLayout(False)
def ComboBox1SelectedIndexChanged(self, sender, e):
self.choice = sender.SelectedItem.Value
self.Close()
objForm = MainForm()
objForm.ShowDialog()
OUT = objForm.choice
@newshunhk my previous code was with WPF syntax, apologies if that has misled you, however here I modified a bit the example from Cyril and the same things apply, though it’s DisplayMember (not DisplayMemberPath) and DataSource (not ItemsSource)
2 Likes
thank you so much for all of you
by the way I found the solution for this, causing self.levelcombo.SelectedIndex = 2 doesn’t work:
I check the items no. with print(self.levelcombo.Items.Count) , it displays 0
self.levelcombo = SF.ComboBox()
self.levelcombo.Name = "levelcombo"
self.levelcombo.DropDownStyle = SF.ComboBoxStyle.DropDownList
self.levelcombo.DataSource = mycomboboxitem
self.levelcombo.DisplayMember = "key"
self.levelcombo.ValueMember = "value"
print(self.levelcombo.Items.Count)
self.levelcombo.Location = SD.Point(270, 45)
self.levelcombo.Size = SD.Size(150, 20)
self.Controls.Add(self.levelcombo)
but if I put the self.Controls.Add(self.levelcombo) before the datasource, it works well!!
self.levelcombo = SF.ComboBox()
self.Controls.Add(self.levelcombo)
self.levelcombo.Name = "levelcombo"
self.levelcombo.DropDownStyle = SF.ComboBoxStyle.DropDownList
self.levelcombo.DataSource = mycomboboxitem
self.levelcombo.DisplayMember = "key"
self.levelcombo.ValueMember = "value"
print(self.levelcombo.Items.Count)
self.levelcombo.Location = SD.Point(270, 45)
self.levelcombo.Size = SD.Size(150, 20)
now I can set the default key to display when script is run
import clr
import sys
import System
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
from collections import namedtuple
class MyItem():
def __init__(self, key, value):
self.Key = key
self.Value = value
keys = ["a", "b", "c"]
values = [10,20,30]
list_of_items = [MyItem(i,j) for i,j in zip(keys, values)]
class MainForm(Form):
def __init__(self):
self.choice = None
self.InitializeComponent()
def InitializeComponent(self):
#
self._comboBoxType = System.Windows.Forms.ComboBox()
self.Controls.Add(self._comboBoxType) # move to here
self._comboBoxType.Location = System.Drawing.Point(40, 50)
self._comboBoxType.Size = System.Drawing.Size(221, 21)
self._comboBoxType.DataSource = list_of_items
self._comboBoxType.DisplayMember = "Key"
self._comboBoxType.ValueMember = "Value"
self._comboBoxType.SelectedIndex = 2 # added
self._comboBoxType.SelectedIndexChanged += self.ComboBox1SelectedIndexChanged
#
self.ClientSize = System.Drawing.Size(300, 200)
self.Name = "MainForm"
self.Text = "MainForm"
self.ResumeLayout(False)
def ComboBox1SelectedIndexChanged(self, sender, e):
self.choice = sender.SelectedItem.Value
self.Close()
objForm = MainForm()
objForm.ShowDialog()
OUT = objForm.choice
datasource seems does not work if I switch the script to cpython… strange
Objects under IronPython are .Net Native, this allows to use properties/attributes of Python objects as Net properties.
Python.Net (CPython engine) does not allow this, in this case you have to use .Net objects (not python objects)
1 Like
c.poupin:
.Net objects
that’s mean I need to use something like PyQt5?
No, not necessarily, you just have to make sure that you are using Object .Net with CPython3
an example
you can also use a DataTable (see example in my previous post)
If you are a beginner it may be better to start with IronPython (which makes a lot of things easier)
3 Likes
thx, but can I use ironpython 3.4 in pyrevit?
except incident, the final version of Ironpython 3.4 should arrive soon, then Ipy 3.4 can be implemented or updated in software(s)
3 Likes