Cast object to KeyValuePair with "generic value"?
Asked Answered
B

5

7

I have a ComboBox filled with mixed items of two different types. The types are either

KeyValuePair<Int32, FontFamily>

or

KeyValuePair<Int32, String>

Now there are occasions where I am only interested in the Key of the selected item, which is always an Int32.

What would be the easiest way to access the Key of the selcted item? I am thinking of something like

Int32 key = ((KeyValuepair<Int32, object/T/var/IdontCare>)combobox.SelectedItem).Key;

but that doesn´t work.

So all I have is

    Int32 key;
    if(combobox.SelectedItem.GetType().Equals(typeof(KeyValuePair<Int32, FontFamily)))
    {
        key = ((KeyValuePair<Int32, FontFamily)combobox.SelectedItem).Key;
    }
    else if(combobox.SelectedItem.GetType().Equals(typeof(KeyValuePair<Int32, String)))
    {
        key = ((KeyValuePair<Int32, String)combobox.SelectedItem).Key;
    }

which works, but I wonder if there is a more elegant way?

Bunting answered 25/9, 2015 at 12:6 Comment(4)
use object like KeyValuePair<Int32, Object>Audio
You could use dynamic: (int)((dyamic)selectedItem).Key)Reseta
Ehsan: Tried before, causes InvalidCastException.Bunting
Lee: worked, thanks! (see accepted answer))Bunting
V
10

Casting to dynamic (poor man's reflection) can do the trick

var key = (int) ((dynamic) comboxbox.SelectedItem).Key);
Veto answered 25/9, 2015 at 12:14 Comment(3)
That´s what Lee suggested in the comments, but I get a compiler error (CS0656) saying that member "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create" is missing.Bunting
Include the Microsoft.CSharp assembly in your project.Vinous
Patrick: thanks, now it really works. Richard: Looks a little fragile, but it´s just what I was looking for. To be honest, I never used dynamic before. Will have to read about it.Bunting
K
3

You certainly don't need to use GetType(). You could use:

int key;
var item = combobox.SelectedItem;
if (item is KeyValuePair<int, FontFamily>)
{
    key = ((KeyValuePair<int, FontFamily>) item).Key;
}
else if (item is KeyValuePair<int, string>)
{
    key = ((KeyValuePair<int, string>) item).Key;
}

I don't think there's really a better way without using reflection or dynamic typing, assuming you can't change the type of the selected items to your own equivalent to KeyValuePair with some non-generic base type or interface.

Kahle answered 25/9, 2015 at 12:11 Comment(0)
A
3

I guess it's bound in WPF, in that case I would suggest to not use KeyValuePair<TKey,TValue> but instead an own VM class. E.g.

class MyComboItem
{
    private String _stringValue;
    private FontFamiliy _fontFamilyValue;

    public Int32 Key {get;set;}
    public object Value => (_fontFamilyValue!=null)?_fontFamilyValue:_stringValue;
}

or you could have an interface like

interface IMyComboItem
{
    Int32 Key {get;}
    object Value {get;}
}

and implement two VM classes that implement it storing the proper value type. With proper constructors and so on. Casting as you want achieve isn't possible with generics, and your solution case isn't elegant.

Abecedarian answered 25/9, 2015 at 12:11 Comment(2)
That really looks elegant, but since I get the KeyValuePairs from outside the class and later will give away KeyValuePairs, I would try to avoid casting.Bunting
Not casting but mapping, eventually your ComboItem VM class can be only a wrapper around the KeyValuePair class handling it properlyAbecedarian
P
2

You can create your own class hierarchy like this

public interface IComboBoxItem
{
    public int Key { get; }
}

public class ComboBoxItem<T> : IComboBoxItem
{
    public T Value { get; set; }

    public int Key { get; set; }
}

and your cast will look like this:

key = ((IComboBoxItem)combobox.SelectedItem).Key;
Powerless answered 25/9, 2015 at 12:11 Comment(1)
That really looks elegant, but since I get the KeyValuePairs from outside the class and later will give away KeyValuePairs, I would try to avoid casting.Bunting
C
-1

Building on Rich's answer, I used Dynamic successfully. I knew the dictionary type I was binding to (and realistically could have used the dictionary itself, since that was still referenced in my form), but I wanted to create a method to search by the displayname. This will eventually check if the binding source is a datatable too, but for now, this worked well for a <string,?> dictionary.

private void SetComboBoxSelection(ComboBox cmb, string ItemText)
        {
            if (cmb.DisplayMember.ToLower() == "key" && cmb.ValueMember.ToLower() == "value")
            {
                foreach (dynamic item in cmb.Items)
                    if (item.Key == ItemText)
                        cmb.SelectedItem = item.Value;
            }
        }
Cloakroom answered 18/11, 2021 at 18:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.