ComboBox for List of Strings

Grids for WPF Forum

Posted 8 years ago by Matt Gates
Version: 11.1.0543
Avatar
New to this grid so here goes: Could someone please direct me to an example which displays a combobox for a list of strings on a view model to which the property grid is bound?

    public class MyViewModel
    {
        public MyViewModel()
        {
            FavoriteActivity = new List<string>() { "Run", "Bike", "Swim"};
            SelectedActvity = FavoriteActivity[0];
        }

        [Category("Info")]
        [Description("Your Name")]
        [DisplayName("Your Name")]
        public string   Name { get; set; }

        [Category("Info")]
        [Description("Your Age")]
        [DisplayName("Your Age")]
        public int      Age { get; set; }

        [Category("Other")]
        [Description("Favorite Activity")]
        [DisplayName("Favorite Activity")]
        public IList<string> FavoriteActivity //Need these in a combo box
        {
            get;
            set;
        }

        [Browsable(false)]
        public string SelectedActvity { get; set; } //This is the selected
    }


                <propgrid:PropertyGrid  SelectedObject="{Binding MyViewModel}" 
                                        MinHeight="200" MinWidth="300"
                                        SummaryCanAutoSize="True" 
                                        SummaryHeight="Auto">                    
                </propgrid:PropertyGrid>

Comments (7)

Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

By default, the PropertyGrid uses the TypeConverter associated with the property to determine the "standard values" for a given property. So your best bet would probably be to create a custom TypeConverter for your property and override the GetStandardValues method, the one that has an ITypeDescriptorContext parameter. The ITypeDescriptorContext has an Instance property, which will refer to your object. So you can cast that to your object to access the list of possible values.

There are a couple other ways to specify the standard values, but the method above allows you to keep your View-Models separated from the View.


Actipro Software Support

Posted 8 years ago by Matt Gates
Avatar
Thanks, that was very helpful. I have a type converter implemented, but still have a problem:

The selected item is not coming across from the combo box to the bound property on the view model. In my case I always get a struct with the default values (0 for the 'Id' and null for 'Name'). Below is the exception being thrown and my test code/xaml.

A first chance exception of type 'System.NullReferenceException' occurred in WpfTestApp.exe
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
A first chance exception of type 'System.NullReferenceException' occurred in System.dll
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=ValueAsString; DataItem='PropertyGridDataAccessorItem' (Name=''); target element is 'ComboBox' (Name='comboBox'); target property is 'SelectedItem' (type 'Object') TargetInvocationException:'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.ComponentModel.ReflectPropertyDescriptor.SetValue(Object component, Object value)
at ActiproSoftware.Windows.Controls.PropertyGrid.Primitives.PropertyDescriptorDataAccessor.set_ValueInternal(Object value)
at ActiproSoftware.Windows.Controls.PropertyGrid.Primitives.CachedPropertyDataAccessorBase.set_ValueAsStringInternal(String value)
at ActiproSoftware.Windows.Controls.PropertyGrid.Primitives.CachedPropertyDataAccessorBase.set_ValueAsString(String value)
at ActiproSoftware.Windows.Controls.PropertyGrid.Primitives.PropertyGridDataAccessorItem.set_ValueAsString(String value)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
at System.Windows.Data.BindingExpression.UpdateSource(Object value)'

                <propgrid:PropertyGrid SelectedObject="{Binding MyViewModel}" 
                                        MinHeight="200" 
                                        MinWidth="300"
                                        SummaryCanAutoSize="True" 
                                        SummaryHeight="Auto">

                    <propgrid:PropertyGrid.PropertyEditors>

                        <propgrid:PropertyEditor ValueTemplateKey="{x:Static propgrid:BuiltinEditors.DynamicStringValueTemplateKey}" >
                            <propgrid:PropertyEditor.ValueStyles>
                                <propgrid:PropertyEditorStyle Key="{x:Type TextBox}">
                                </propgrid:PropertyEditorStyle>
                            </propgrid:PropertyEditor.ValueStyles>
                        </propgrid:PropertyEditor>

                    </propgrid:PropertyGrid.PropertyEditors>

                </propgrid:PropertyGrid>

    [TypeConverter(typeof(ActivityTypeConverter))]
    public struct Activity
    {
        public string Name;
        public int    Id;
    }

    public class MyViewModel : ViewModelBase
    {
        public MyViewModel()
        {
            Activity act1 = new Activity();
            act1.Name     = "Swim";
            act1.Id       = 1;

            Activity act2 = new Activity();
            act2.Name     = "Bike";
            act2.Id       = 2;

            Activity act3 = new Activity();
            act3.Name     = "Run";
            act3.Id       = 3;

            FavoriteActivityList = new List<Activity>(){act1, act2, act3};
            SelectedActvity      = FavoriteActivityList[0];
        }

        [Browsable(false)]
        public IList<Activity> FavoriteActivityList
        {
            get;
            set;
        }

        Activity _selectedActivityObj;

        public Activity SelectedActvity 
        { 
            get { return _selectedActivityObj; } 
            set 
            { 
                _selectedActivityObj = value; 
                RaisePropertyChanged("SelectedActvity"); 
                Debug.WriteLine("SelectedActvity = " + _selectedActivityObj.Name.ToString()); 
            } 
        }

    }

    public class ActivityTypeConverter : TypeConverter
    {
        #region TypeConverter Members

        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            MyViewModel  vm   = context.Instance as MyViewModel;
            List<string> list = new List<string>();
            if (vm != null)
            {         
                foreach (Activity act in vm.FavoriteActivityList)
                {
                    list.Add(act.Name);
                }
                
            }
            return new StandardValuesCollection(list);
        }

        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }

        #endregion
    }

Posted 8 years ago by Matt Gates
Avatar
Strike that last post. I needed to override ConvertFrom:

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            MyViewModel vm = context.Instance as MyViewModel;
            object obj = null;
            if (vm != null)
            {
                obj = vm.FavoriteActivityList.Where(a => String.Compare(a.Name, value.ToString(), true) == 0).FirstOrDefault();
            }
            return obj;
        }
Posted 4 years ago by matt2012
Avatar

Hello,

I have also a combobox inside the propertygrid with own objects inside.
I've made a type-converter and I have overridden the necessary methods.
If I change the values inside the combobox, the property-value is refreshed.
Everything works ok. But there is one problem.
The pre selection of the combobox does not work.

Do you have a tip what I doing wrong?

Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Matt,

It's hard to say without debugging it in a very simple example project.  Either your selection binding isn't taking place at the right time initially or something with the type converter isn't catching a case.  Maybe you could try debugging the type converter to see what value it's called with initially.


Actipro Software Support

Posted 4 years ago by matt2012
Avatar

Hello,
thank you for your answer.
I use this thread because the example of Matt Gates is very similar to my example.

Here is my simple example:

    [TypeConverter(typeof(MyDesignTypeConverter))]
    public class MyDesign
    {
        public string DesignStr;
        public int DesignId;
    }

    public class MyDesignTypeConverter : TypeConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }

        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            var userData = context.Instance as UserData;
            List<string> list = new List<string>();
            if (userData != null)
            {
                foreach (MyDesign myDesign in userData.DesignList)
                {
                    list.Add(myDesign.DesignStr);
                }
                //
            }
            //
            return new StandardValuesCollection(list);
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return true;
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var userData = context.Instance as UserData;
            if (userData != null && value != null)
            {
                object obj = userData.DesignList.FirstOrDefault(a => System.String.CompareOrdinal(a.DesignStr, value.ToString()) == 0);
                return obj;
            }
            //
            return base.ConvertFrom(context, culture, value);
        }
    }
 
    //View Model
    public class UserData : ViewModelBase
    {
        
        [Browsable(false)]
        public IList<MyDesign> DesignList
        { get; set; }

        private MyDesign _selectedMyDesign;
        [LocalizedCategoryAttributes("MasterCat_MainSettings\\Cat_Appearance")]
        [LocalizedDisplayNameAttributes]
        [LocalizedDescriptionAttributes]
        [SortOrder(1)]
        [TypeConverter(typeof(MyDesignTypeConverter))]
        public MyDesign SelectedMyDesign
        {
            get { return _selectedMyDesign; }
            set
            {
                if (Equals(value, _selectedMyDesign)) return;
                _selectedMyDesign = value;
                //
                OnPropertyChanged();
            }
        }

        public UserData (MyObject parent)
            : base(parent, true)
        {
            //
            DesignList= new List<MyDesign>();
            DesignList.Add(new MyDesign(){DesignId = 1, DesignStr = "Design1"});
            DesignList.Add(new MyDesign() { DesignId = 2, DesignStr = "Design2" });
            DesignList.Add(new MyDesign() { DesignId = 3, DesignStr = "Design3" });
	    SelectedMyDesign=DesignList.Add[0];
        }

    }

    XAML:

.
.
.
    <propgrid:PropertyGrid.PropertyEditors>
         <propgrid:PropertyEditor ValueTemplateKey="{x:Static propgrid:BuiltinEditors.DynamicStringValueTemplateKey}" />
    </propgrid:PropertyGrid.PropertyEditors>
.
.
.	

[Modified 4 years ago]

Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

It's hard to help from looking at code in the forum.  Per our Support Requests Policy, if you'd like us to debug anything, please make a new simple sample project that shows the problem and email the ZIP over to our support address in a renamed .zip file extension so it doesn't get spam blocked.  Reference this thread in your email.  Thanks!


Actipro Software Support

The latest build of this product (v2019.1 build 0681) was released 18 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.