CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4
  1. #1
    Join Date
    Sep 2013
    Posts
    2

    Dictionary as a Combobox Datasource...

    Hi, I'm new on VC++ and I'm trying to do a basic Combobox populate.
    What I need is that Combobox shows a description with an integer value behind.

    So my first try was using a Dictionary but I wasn't able to bind it to the Combobox
    This is my code:

    Code:
    	Dictionary<long, String^> time = gcnew Dictionary<long, String^>();
    
    	time.Add(1, "One");
    	time.Add(5, "Five");
    	time.Add(10, "Ten");
    
    	cbTime->DisplayMember = "Value";
    	cbTime->ValueMember = "Key";
    	cbTime->DataSource = time;
    In C# to acomplish this I must use a BindingSource to adapt the Dictionary, but in VC++ I don't know how to use it.

    Well hope you can help me.
    Thank you in advance!

  2. #2
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Dictionary as a Combobox Datasource...

    So what is the C# way you would do that? I can read C# to some extent, I just can't really write it. Maybe I can translate it to C++/CLI or at least give a few hints regarding the translation.

    I, personally, am not really a friend of the data binding mechanism. It feels quite "database-ish" to me, and that's not really my natural codotope. Once I attempted to implement an IList for a collection of my objects, which is the prerequisite to successfully assigning it to the ComboBox::DataSource property. However, I abandoned that idea way before finishing, since the better adherence to some formalisms didn't seem to outweigh the required effort.

    Currently I'm favoring a more low-level approach based on wrapper classes for the classes of objects to be represented in the combo box. The below example deals with enums, but the principle can be easily adapted to practically any class, including the key/value pairs that comprise the contents of a dictionary.

    Code:
    // ValueTriggerEditor.cpp
    
    #include "StdAfx.h"
    
    #include "ValueTriggerEditor.h"
    
    #ifdef SUPPORT_TRIGGERS
    
    using namespace System::Text;
    
    using namespace EPM;
    
    namespace EPM
    {
    
    generic<class T>
    where T : Enum
    private ref class EnumCoboWrapper
    {
    public:
      EnumCoboWrapper(T value) : Value(value)
      {}
    
    protected:
      EnumCoboWrapper(String ^strValue) : Value(static_cast<T>(Enum::Parse(T::typeid, strValue)))
      {}
    
    public:
      static array<EnumCoboWrapper<T> ^> ^CreateAll()
      {
        array<String ^> ^astrNames = Enum::GetNames(T::typeid);
        array<EnumCoboWrapper<T> ^> ^wrappers = gcnew array<EnumCoboWrapper<T> ^>(astrNames->Length);
        for (int i = 0; i < astrNames->Length; ++i)
          wrappers[i] = gcnew EnumCoboWrapper<T>(astrNames[i]);
        return wrappers;
      }
    
      virtual String ^ToString() override
      {
        StringBuilder ^sb = gcnew StringBuilder;
        String ^strEnumItemName = Enum::GetName(T::typeid, Value);
        for (int i = 0; i < strEnumItemName->Length; ++i) {
          if (Char::IsUpper(strEnumItemName[i])) {
            if (sb->Length)
              sb->Append(L' ');
            sb->Append(Char::ToLower(strEnumItemName[i]));
          } else
            sb->Append(strEnumItemName[i]);
        }
        return sb->ToString();
      }
    
    public:
      T Value;
    };
    
    private ref class ValueRelationCoboWrapper : public EnumCoboWrapper<ValueRelation>
    {
    public:
      ValueRelationCoboWrapper(ValueRelation value) : EnumCoboWrapper(value)
      {}
    
    protected:
      ValueRelationCoboWrapper(String ^strValue) : EnumCoboWrapper(strValue)
      {}
    
    public:
      static array<ValueRelationCoboWrapper ^> ^CreateAll()
      {
        array<String ^> ^astrNames = Enum::GetNames(ValueRelation::typeid);
        array<ValueRelationCoboWrapper ^> ^wrappers = gcnew array<ValueRelationCoboWrapper ^>(astrNames->Length);
        for (int i = 0; i < astrNames->Length; ++i)
          wrappers[i] = gcnew ValueRelationCoboWrapper(astrNames[i]);
        return wrappers;
      }
    
    public:
      virtual String ^ToString() override
      {
        String ^str = __super::ToString();
        return str->EndsWith("equal") ? str + " to" : str;
      }
    };
    
    }
    
    ValueTriggerEditor::ValueTriggerEditor()
    {
      InitializeComponent();
    
      cbRelation->Items->AddRange(ValueRelationCoboWrapper::CreateAll());
      cbRelation->SelectedIndex = 0;
      cbTimestampMode->Items->AddRange(EnumCoboWrapper<TimestampAggregationMode>::CreateAll());
      cbTimestampMode->SelectedIndex = 0;
    }
    
    System::Void ValueTriggerEditor::ValueTriggerEditor_Load(System::Object^  sender, System::EventArgs^  e)
    {
      if (cbSource->Items->Count)
        cbSource->SelectedIndex = 0;
    }
    
    #endif
    This is an authentic implementation file from one of my real-life programs that demonstrates the wrapper architecture as well as how to use it. However, the dialog it implements isn't finished yet, so there are some important parts still missing (but these are rather unrelated to our discussion). This wrapper represents the concrete item value by a public field, so access to the selected item must be coded like safe_cast<DesiredTypeWrapper ^>(cb->SelectedItem)->Value rather than safe_cast<DesiredType>(cb->SelectedValue), but I don't think that's really painful. I don't think it would be a big problem either to wrap the wrapper class value field into a property and assign its name to cb->ValueMember so access over cb->SelectedValue becomes possible, yet I never saw a concrete need to take that effort in my programs.

    I'd almost bet there's some LINQ involved in the way you do that in C#, isn't it? I ruminated a bit about a possible LINQ way of achieving your goal, but couldn't imagine any that would be more convenient and/or elegant than my usual low-level approach. Using LINQ in C++/CLI can be surprisingly tideous, since there's no support for it at the language level, so quite some times it's just simpler to take another approach.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  3. #3
    Join Date
    Sep 2013
    Posts
    2

    Re: Dictionary as a Combobox Datasource...

    Hi Eri523, and thanks so much for reply.

    I'm totally agree with you, the binding model was evolving with every release of .NET Framework, I'm new on VC++ but I'm coding C# since Framework v1, today with Framework v4+ and technologies like Silverlight or WPF, practycally the only way you have to manage data on a frontend is the binding model with all the problems it has!.
    Sometimes when you need to do things not that simple, databindings can turn in a real nightmare.

    What I'm trying to do with VC++ it's a very simple project, with no layers, with no database, just a form, some controls and a basic logic behind.
    Mybe my mistake was starting it thinking in my C# experience, but the main idea was keep the code as simple as i could, that's why i used a binding. Later, when I realized that I wasn't able to populate the combobox with a key/value list I started to think on make a class wrapper with some methods and properties but I said "wait!" must be a simple way to do this, I mean is just a combobox filled with a key/value list, very very basic stuff, so I decided to ask here.

    I will show you how to populate a Combobox with a key/value list of items in C#:

    Code:
    	var time = new Dictionary<int, string>
    	{
    		{1, "One"},
    		{5, "Five"},
    		{10, "Ten"}
    	};
    
    	cbTime.DisplayMember = "Value";
    	cbTime.ValueMember = "Key";
    	cbTime.DataSource = new BindingSource(time, null);
    Quite simple. Isn't it?
    The magic is the overloaded constructor of the BindingSource class. This class is an adapter for binding sources, like I needed in this case. I see this class exists in VC++ but the magic overloaded constructor doesn't

    About LINQ you're right, I can resolve this code using LINQ converting the dictionary to an array of anonymous class type (with two properties) but why? if can do it with just a few lines like above.

    You're wrapper is very useful, I will keep it for a future needing.

    Well Eri, thanks again and if I can help you with something just let me know.
    Regards.
    K.

  4. #4
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Dictionary as a Combobox Datasource...

    Quote Originally Posted by kakamokoloko View Post
    I will show you how to populate a Combobox with a key/value list of items in C#:

    [...]
    Actually, that can be applied to your original C++/CLI sample much the same way. Apparently your problem merely was of syntactical nature, rather than being related to unavailable constructor overloads. This works:

    Code:
    	Dictionary<long, String^> ^time = gcnew Dictionary<long, String^>();
    
    	time->Add(1, "One");
    	time->Add(5, "Five");
    	time->Add(10, "Ten");
    
    	cbTime->DisplayMember = "Value";
    	cbTime->ValueMember = "Key";
    	cbTime->DataSource = gcnew BindingSource(time, nullptr);
    You originally made your time what is called an implicitly dereferenced variable. That's not fundamentally wrong, but it's syntactically a bit tricky when you need to get hands on the enclosed tracking handle, like here in order to pass it to the BindingSource constructor. I generally recommend to beginners (of C++/CLI, not necessarily programming in total) to avoid them, since they tend to be confusing at times. In fact, I don't use them really much myself either. The fixed sample above uses the usual C++/CLI reference type syntax and semantics.

    Well Eri, thanks again and if I can help you with something just let me know.
    You're welcome. When I have a question of my own, you most probably will find a thread I started here.

    Encouraged by the simplicity of using data binding here, I again made a few experiments to replace my wrapper approach shown above with a custom enum fomatter assigned to the ComboBox::FormatInfo property. But no matter what I tried, my functions implementing IFormatProvider and ICustomFormatter never even got called. That was sort of a déjÃ* vu: I recall that problems of this sort were one of the main reasons to introduce the wrapper class approach in the first place. Looks like I'm going to stick with it for the next time.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured