DevExpress WPF PropertyGridControl:在绑定对象的另一部分引用对象
DevExpress WPF PropertyGridControl: Referencing objects in another part of the bound object
我有一个复杂的对象,我试图在 DevExpress WPF PropertyGridControl 中显示它。该对象中嵌套了其他对象。一些嵌套对象引用其他嵌套对象,我想维护它们之间的引用 link(即我不想求助于字符串或 ids 到 link 对象)。这是我正在处理的对象层次结构的人为示例:
public class OrderSystem
{
public List<Customer> Customers { get; set; }
public List<Order> Orders { get; set; }
}
public class Customer
{
public string Name { get; set; }
public override string ToString() => Name;
}
public class Order
{
public Customer Buyer { get; set; }
}
我解决这个问题的方法是用自定义类型转换器装饰订单的 Buyer
属性,该转换器决定对象的其他部分的可用客户:
public class Order
{
[TypeConverter(typeof(AvailableCustomersTypeConverter))]
public Customer Buyer { get; set; }
}
自定义类型转换器代码如下所示:
public class AvailableCustomersTypeConverter : TypeConverter
{
public static List<Customer> AvailableCustomers { get; set; }
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
var customers = AvailableCustomers ?? new List<Customer>();
var availableNames = AvailableCustomers.Select(c => c.Name).ToList();
return new StandardValuesCollection(availableNames);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) => DoConvert(value);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) => DoConvert(value);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string) || destinationType == typeof(Customer);
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) || sourceType == typeof(Customer);
private object DoConvert(object value)
{
if (value is string name)
{
if (AvailableCustomers != null)
{
return AvailableCustomers.FirstOrDefault(c => c.Name == name);
}
else
{
return string.Empty;
}
}
else if (value is Customer customer)
{
return customer.Name;
}
else
{
throw new NotSupportedException($"{value.GetType().FullName}");
}
}
}
静态 List<Customer> AvailableCustomers
属性 在加载新的 OrderSystem 时设置,或者在 OrderSystem 的 Customers
集合更改时设置。我必须将其设为静态,以便 AvailableCustomersTypeConverter 可以访问它,因为它包含在一个属性中。
这很好用,因为它确实将客户限制为对象其他部分可用的客户。
但是,一旦内容失去焦点,选择就会变成空白:
请注意,我已确认实际对象引用仍然存在。引用没有损坏,它只是停止在 UI.
中正常显示
我想弄清楚如何让选定的客户在内容单元格失去焦点后正确显示。有人有什么想法吗?
我发现了这个问题,这源于我的错误想法,即客户对象需要从字符串(它们的名称)来回转换,因为我认为这就是下拉框将显示的内容。但是,由于我在 Customer 对象(显示名称)上创建了 ToString() 覆盖,因此没有必要这样做。
当我(或者说我的 co-worker)将 AvailableCustomersTypeConverter 中的 GetStandardValues 方法更改为 return 实际客户对象的列表而不仅仅是它们的名称时,事情突然开始起作用了。
此外,因为我直接 return 访问 Customer 对象,所以我不需要所有这些转换方法。更新后的 AvailableCustomersTypeConverter 代码如下所示:
public class AvailableCustomersTypeConverter : TypeConverter
{
public static List<Customer> AvailableCustomers { get; set; }
public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(AvailableCustomers ?? new List<Customer>());
}
}
我有一个复杂的对象,我试图在 DevExpress WPF PropertyGridControl 中显示它。该对象中嵌套了其他对象。一些嵌套对象引用其他嵌套对象,我想维护它们之间的引用 link(即我不想求助于字符串或 ids 到 link 对象)。这是我正在处理的对象层次结构的人为示例:
public class OrderSystem
{
public List<Customer> Customers { get; set; }
public List<Order> Orders { get; set; }
}
public class Customer
{
public string Name { get; set; }
public override string ToString() => Name;
}
public class Order
{
public Customer Buyer { get; set; }
}
我解决这个问题的方法是用自定义类型转换器装饰订单的 Buyer
属性,该转换器决定对象的其他部分的可用客户:
public class Order
{
[TypeConverter(typeof(AvailableCustomersTypeConverter))]
public Customer Buyer { get; set; }
}
自定义类型转换器代码如下所示:
public class AvailableCustomersTypeConverter : TypeConverter
{
public static List<Customer> AvailableCustomers { get; set; }
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
var customers = AvailableCustomers ?? new List<Customer>();
var availableNames = AvailableCustomers.Select(c => c.Name).ToList();
return new StandardValuesCollection(availableNames);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) => DoConvert(value);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) => DoConvert(value);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string) || destinationType == typeof(Customer);
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) || sourceType == typeof(Customer);
private object DoConvert(object value)
{
if (value is string name)
{
if (AvailableCustomers != null)
{
return AvailableCustomers.FirstOrDefault(c => c.Name == name);
}
else
{
return string.Empty;
}
}
else if (value is Customer customer)
{
return customer.Name;
}
else
{
throw new NotSupportedException($"{value.GetType().FullName}");
}
}
}
静态 List<Customer> AvailableCustomers
属性 在加载新的 OrderSystem 时设置,或者在 OrderSystem 的 Customers
集合更改时设置。我必须将其设为静态,以便 AvailableCustomersTypeConverter 可以访问它,因为它包含在一个属性中。
这很好用,因为它确实将客户限制为对象其他部分可用的客户。
但是,一旦内容失去焦点,选择就会变成空白:
请注意,我已确认实际对象引用仍然存在。引用没有损坏,它只是停止在 UI.
中正常显示我想弄清楚如何让选定的客户在内容单元格失去焦点后正确显示。有人有什么想法吗?
我发现了这个问题,这源于我的错误想法,即客户对象需要从字符串(它们的名称)来回转换,因为我认为这就是下拉框将显示的内容。但是,由于我在 Customer 对象(显示名称)上创建了 ToString() 覆盖,因此没有必要这样做。
当我(或者说我的 co-worker)将 AvailableCustomersTypeConverter 中的 GetStandardValues 方法更改为 return 实际客户对象的列表而不仅仅是它们的名称时,事情突然开始起作用了。
此外,因为我直接 return 访问 Customer 对象,所以我不需要所有这些转换方法。更新后的 AvailableCustomersTypeConverter 代码如下所示:
public class AvailableCustomersTypeConverter : TypeConverter
{
public static List<Customer> AvailableCustomers { get; set; }
public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(AvailableCustomers ?? new List<Customer>());
}
}