PropertyGrid - 根据另一个 属性 值更改下拉列表 属性 编辑器的项目
PropertyGrid - Change items of a dropdown property editor based on another property value
我正在尝试在自定义组件中实现下拉菜单 属性,我使用 and 作为指导。
到目前为止,我设法让它工作,下拉列表中有预定义的项目。
但我仍然需要弄清楚如何更改下拉列表中的项目?
这是我目前的代码(从上面提到的 link 构建)
[TypeConverter(typeof(TableNameConverter))]
public TableName gttTableName
{ get; set; }
...
public class TableName
{
public string Name { get; set; }
public override string ToString()
{
return $"{Name}";
}
}
public class TableNameService
{
List<TableName> list = new List<TableName>() {
new TableName() {Name = "naam 1" },
new TableName() {Name = "naam 2" },
};
public IEnumerable<TableName> GetAll()
{
return list;
}
}
public class TableNameConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
var svc = new TableNameService();
return new StandardValuesCollection(svc.GetAll().ToList());
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
//var id = int.Parse(v.Split('-')[0].Trim());
var name = v.ToString();
var svc = new TableNameService();
//return svc.GetAll().Where(x => x.Id == id).FirstOrDefault();
return svc.GetAll().Where(x => x.Name == name).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}
这在 VS 中看起来像这样 属性 window
现在的问题是,当某个属性发生变化时,下拉菜单属性中的项目必须发生变化。这应该在方法UpdateTableNames
(代码如下)中完成。
换句话说,在另一个 属性 的 setter 中,项目 naam 1
、naam 2
可以更改为一个完整的新集合,具有更多或更少的项目以及不同的值。
我还想不通的是如何更改这些项目?
让我解释一下情况。
- 用户将自定义组件放在表单上
- 当他查看 属性 gttTableName 时,它现在将显示
naam 1
和 naam 2
- 现在用户更改了另一个 属性 (gttDataModule) 并且在此 属性 的 setter 中 gttTableName 属性 的项目可以更改。
- 因此,如果他再次查看 属性 gttTableName,它现在应该会显示一个完整的其他值列表。
属性 gttDataModule 的代码是这样的
public gttDataModule gttDataModule
{
get { return _gttDataModule; }
set
{
_gttDataModule = value;
UpdateTableNames();
}
}
private void UpdateTableNames()
{
List<string> tableNames = new List<string>();
if (_gttDataModule != null)
{
foreach (gttDataTable table in _gttDataModule.gttDataTables)
{
tableNames.Add(table.Table.TableName);
}
}
// at this point the list tableNames will be populated with values.
// What I need is to replace the list in TableNameService from 'naam 1', 'naam 2'
// to the values in this list.
// so they won't be 'naam 1' and 'naam 2' anymore
// It could be more or less items or even none
// They could have different values
// for example the list could be 'tblBox', 'tblUser', tblClient', tblOrders'
// or it could be empty
// or it could be 'vwCars', 'tblSettings'
}
如何更改 TableNameService
的 list
中的项目?
我设法做到了,但不确定它是否是一个好的和稳定的解决方案。
我所做的是创建一个静态 class 来保存列表
public static class NameList
{
public static List<TableName> list = new List<TableName>();
public static void UpdateItems(List<string> tableNames)
{
list.Clear();
foreach (string item in tableNames)
{
list.Add(new TableName() { Name = item });
}
}
}
然后在 class TableNameService
中使用该列表
public class TableNameService
{
List<TableName> list = NameList.list;
public IEnumerable<TableName> GetAll()
{
return list;
}
}
现在我可以像这样
从另一个属性更新setter的列表
private void UpdateTableNames()
{
List<string> tableNames = new List<string>();
if (_gttDataModule != null)
{
foreach (gttDataTable table in _gttDataModule.gttDataTables)
{
tableNames.Add(table.Table.TableName);
}
}
NameList.UpdateItems(tableNames);
}
我试过了,它确实有效,列表正在更改正确。
但我想对这个解决方案提出一些意见,是否有任何我没有预见到的问题。这样做有什么缺点?
我会根据 post 中的答案创建一个示例:。
基本思想是使用自定义类型转换器并将 GetStandardValues 覆盖为 return 编辑支持值的列表 属性。
这里的重点是使用 context.Instance 获取正在编辑的对象的实例,并尝试根据编辑对象的其他属性过滤列表。
在下面的示例中,我将编辑一个 Product
,它有一个 Category
和一个 SubCategory
属性,并且类别和子类别之间存在关系.例如,如果您选择类别 1,则子类别列表应显示类别 1 的子类别,但如果您选择类别 2,则列表应显示不同的子类别组,如下所示:
这是示例代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
private Category category;
[TypeConverter(typeof(CategoryConverter))]
public Category Category
{
get { return category; }
set
{
if (category?.Id != value?.Id)
{
category = value;
SubCategory = null;
}
}
}
[TypeConverter(typeof(SubCategoryConverter))]
public SubCategory SubCategory { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{Id} - {Name}";
}
}
public class SubCategory
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{Id} - {Name}";
}
}
public class CategoryService
{
List<Category> categories = new List<Category> {
new Category() { Id = 1, Name = "Category 1" },
new Category() { Id = 2, Name = "Category 2" },
};
List<SubCategory> subCategories = new List<SubCategory> {
new SubCategory() { Id = 11, Name = "Sub Category 1-1", CategoryId= 1 },
new SubCategory() { Id = 12, Name = "Sub Category 1-2", CategoryId= 1 },
new SubCategory() { Id = 13, Name = "Sub Category 1-3", CategoryId= 1 },
new SubCategory() { Id = 21, Name = "Sub Category 2-1", CategoryId= 2 },
new SubCategory() { Id = 22, Name = "Sub Category 2-2", CategoryId= 2 },
};
public IEnumerable<Category> GetCategories()
{
return categories;
}
public IEnumerable<SubCategory> GetSubCategories()
{
return subCategories.ToList();
}
}
public class CategoryConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
var svc = new CategoryService();
return new StandardValuesCollection(svc.GetCategories().ToList());
}
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
var id = int.Parse(v.Split('-')[0].Trim());
var svc = new CategoryService();
return svc.GetCategories()
.Where(x => x.Id == id).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}
public class SubCategoryConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
var svc = new CategoryService();
var categoryId = ((Product)context.Instance).Category.Id;
return new StandardValuesCollection(svc.GetSubCategories()
.Where(x => x.CategoryId == categoryId).ToList());
}
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
var id = int.Parse(v.Split('-')[0].Trim());
var svc = new CategoryService();
return svc.GetSubCategories()
.Where(x => x.Id == id).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}
我正在尝试在自定义组件中实现下拉菜单 属性,我使用
到目前为止,我设法让它工作,下拉列表中有预定义的项目。
但我仍然需要弄清楚如何更改下拉列表中的项目?
这是我目前的代码(从上面提到的 link 构建)
[TypeConverter(typeof(TableNameConverter))]
public TableName gttTableName
{ get; set; }
...
public class TableName
{
public string Name { get; set; }
public override string ToString()
{
return $"{Name}";
}
}
public class TableNameService
{
List<TableName> list = new List<TableName>() {
new TableName() {Name = "naam 1" },
new TableName() {Name = "naam 2" },
};
public IEnumerable<TableName> GetAll()
{
return list;
}
}
public class TableNameConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
var svc = new TableNameService();
return new StandardValuesCollection(svc.GetAll().ToList());
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
//var id = int.Parse(v.Split('-')[0].Trim());
var name = v.ToString();
var svc = new TableNameService();
//return svc.GetAll().Where(x => x.Id == id).FirstOrDefault();
return svc.GetAll().Where(x => x.Name == name).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}
这在 VS 中看起来像这样 属性 window
现在的问题是,当某个属性发生变化时,下拉菜单属性中的项目必须发生变化。这应该在方法UpdateTableNames
(代码如下)中完成。
换句话说,在另一个 属性 的 setter 中,项目 naam 1
、naam 2
可以更改为一个完整的新集合,具有更多或更少的项目以及不同的值。
我还想不通的是如何更改这些项目?
让我解释一下情况。
- 用户将自定义组件放在表单上
- 当他查看 属性 gttTableName 时,它现在将显示
naam 1
和naam 2
- 现在用户更改了另一个 属性 (gttDataModule) 并且在此 属性 的 setter 中 gttTableName 属性 的项目可以更改。
- 因此,如果他再次查看 属性 gttTableName,它现在应该会显示一个完整的其他值列表。
属性 gttDataModule 的代码是这样的
public gttDataModule gttDataModule
{
get { return _gttDataModule; }
set
{
_gttDataModule = value;
UpdateTableNames();
}
}
private void UpdateTableNames()
{
List<string> tableNames = new List<string>();
if (_gttDataModule != null)
{
foreach (gttDataTable table in _gttDataModule.gttDataTables)
{
tableNames.Add(table.Table.TableName);
}
}
// at this point the list tableNames will be populated with values.
// What I need is to replace the list in TableNameService from 'naam 1', 'naam 2'
// to the values in this list.
// so they won't be 'naam 1' and 'naam 2' anymore
// It could be more or less items or even none
// They could have different values
// for example the list could be 'tblBox', 'tblUser', tblClient', tblOrders'
// or it could be empty
// or it could be 'vwCars', 'tblSettings'
}
如何更改 TableNameService
的 list
中的项目?
我设法做到了,但不确定它是否是一个好的和稳定的解决方案。
我所做的是创建一个静态 class 来保存列表
public static class NameList
{
public static List<TableName> list = new List<TableName>();
public static void UpdateItems(List<string> tableNames)
{
list.Clear();
foreach (string item in tableNames)
{
list.Add(new TableName() { Name = item });
}
}
}
然后在 class TableNameService
public class TableNameService
{
List<TableName> list = NameList.list;
public IEnumerable<TableName> GetAll()
{
return list;
}
}
现在我可以像这样
从另一个属性更新setter的列表 private void UpdateTableNames()
{
List<string> tableNames = new List<string>();
if (_gttDataModule != null)
{
foreach (gttDataTable table in _gttDataModule.gttDataTables)
{
tableNames.Add(table.Table.TableName);
}
}
NameList.UpdateItems(tableNames);
}
我试过了,它确实有效,列表正在更改正确。
但我想对这个解决方案提出一些意见,是否有任何我没有预见到的问题。这样做有什么缺点?
我会根据 post 中的答案创建一个示例:
基本思想是使用自定义类型转换器并将 GetStandardValues 覆盖为 return 编辑支持值的列表 属性。
这里的重点是使用 context.Instance 获取正在编辑的对象的实例,并尝试根据编辑对象的其他属性过滤列表。
在下面的示例中,我将编辑一个 Product
,它有一个 Category
和一个 SubCategory
属性,并且类别和子类别之间存在关系.例如,如果您选择类别 1,则子类别列表应显示类别 1 的子类别,但如果您选择类别 2,则列表应显示不同的子类别组,如下所示:
这是示例代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
private Category category;
[TypeConverter(typeof(CategoryConverter))]
public Category Category
{
get { return category; }
set
{
if (category?.Id != value?.Id)
{
category = value;
SubCategory = null;
}
}
}
[TypeConverter(typeof(SubCategoryConverter))]
public SubCategory SubCategory { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{Id} - {Name}";
}
}
public class SubCategory
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{Id} - {Name}";
}
}
public class CategoryService
{
List<Category> categories = new List<Category> {
new Category() { Id = 1, Name = "Category 1" },
new Category() { Id = 2, Name = "Category 2" },
};
List<SubCategory> subCategories = new List<SubCategory> {
new SubCategory() { Id = 11, Name = "Sub Category 1-1", CategoryId= 1 },
new SubCategory() { Id = 12, Name = "Sub Category 1-2", CategoryId= 1 },
new SubCategory() { Id = 13, Name = "Sub Category 1-3", CategoryId= 1 },
new SubCategory() { Id = 21, Name = "Sub Category 2-1", CategoryId= 2 },
new SubCategory() { Id = 22, Name = "Sub Category 2-2", CategoryId= 2 },
};
public IEnumerable<Category> GetCategories()
{
return categories;
}
public IEnumerable<SubCategory> GetSubCategories()
{
return subCategories.ToList();
}
}
public class CategoryConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
var svc = new CategoryService();
return new StandardValuesCollection(svc.GetCategories().ToList());
}
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
var id = int.Parse(v.Split('-')[0].Trim());
var svc = new CategoryService();
return svc.GetCategories()
.Where(x => x.Id == id).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}
public class SubCategoryConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
var svc = new CategoryService();
var categoryId = ((Product)context.Instance).Category.Id;
return new StandardValuesCollection(svc.GetSubCategories()
.Where(x => x.CategoryId == categoryId).ToList());
}
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
return true;
}
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value != null && value.GetType() == typeof(string))
{
var v = $"{value}";
var id = int.Parse(v.Split('-')[0].Trim());
var svc = new CategoryService();
return svc.GetSubCategories()
.Where(x => x.Id == id).FirstOrDefault();
}
return base.ConvertFrom(context, culture, value);
}
}