我正在尝试设置一种配置数据库连接的方法。我在GitHub上找到了一个简单的属性网格,将其添加到我的项目中,并将DbConnectionStringBuilder
后代绑定到它,它立即破裂。它找到了所有属性的名称和类型,但它似乎实际上并没有链接到对象实例,因此所有属性都显示空值,并且试图编辑它们会导致各种问题。
WPF数据绑定对DbConnectionStringBuilder
有什么特别奇怪的地方吗?
如果有人想尝试,请复制案例。CSV数据库配置类:
using System;
using System.ComponentModel;
using System.Data.Common;
namespace Repro
{
public class CsvConfigurator : DbConnectionStringBuilder
{
public CsvConfigurator() { }
public CsvConfigurator(string conf)
{
ConnectionString = conf;
}
public string Delimiter
{
get => GetString(nameof(Delimiter));
set => this[nameof(Delimiter)] = value;
}
public bool AutoDetectDelimiter
{
get => GetBool(nameof(AutoDetectDelimiter));
set => this[nameof(AutoDetectDelimiter)] = value;
}
public bool UsesHeader
{
get => GetBool(nameof(UsesHeader));
set => this[nameof(UsesHeader)] = value;
}
public bool UsesQuotes
{
get => GetBool(nameof(UsesQuotes));
set => this[nameof(UsesQuotes)] = value;
}
public char QuoteChar
{
get => GetChar(nameof(QuoteChar), '"');
set => this[nameof(QuoteChar)] = value;
}
public char EscapeChar
{
get => GetChar(nameof(EscapeChar), '\');
set => this[nameof(EscapeChar)] = value;
}
protected string GetString(string key) => TryGetValue(key, out var value) ? (string)value : null;
protected bool GetBool(string key) => TryGetValue(key, out var value) ? Convert.ToBoolean(value) : false;
protected char GetChar(string key, char defaultValue)
{
var result = GetString(key);
return string.IsNullOrEmpty(result) ? defaultValue : result[0];
}
}
}
- 创建一个带有属性网格(任何属性网格)的项目。
- 实例化上面的类
- 将其作为要检查的对象绑定到网格。
- 在getter和setter上设置断点,这样你就可以看到数据绑定实际发生的时间。
- 看所有没有被束缚的东西。(查看复选框控件以及它们如何处于空状态,尽管所讨论的属性不是
bool?
类型。) - 编辑一下
- 注意断点不被命中。
是的,从DbConnectionStringBuilder派生的类在与属性网格一起使用时具有特殊行为是有原因的。
因为它实现了ICustomTypeDescriptor接口。通常,属性网格使用TypeDescriptor。GetProperties方法,如果实现,默认情况下将推迟到ICustomTypeDescriptor。
它的意思是属性网格将而不是使用编译后的。net/c#属性来表示实例,而不是使用ICustomTypeDescriptor
接口的属性,以及自定义的PropertyDescriptor
实例。
所以编译后的。net/c#属性根本不会被属性网格使用,只会被"虚拟"。属性由内部DbConnectionStringBuilder
代码组成(您可以在这里查看其代码https://github.com/microsoft/referencesource/blob/master/System.Data/System/Data/Common/DbConnectionStringBuilder.cs#L335)。这些"virtual"属性将使用。net编译的属性来构造,但它们的代码不会用于获取或设置它们。
这在某种程度上类似于WPF依赖属性特性,其中。net代码只使用。net类的编译属性,而不使用WPF绑定/XAML引擎(除了WPF使用DependencyProperty.Register
代码来定义依赖属性,而不是编译的属性)。
如果你想支持WPF绑定引擎,你可以像这样在你的类中实现INotifyPropertyChanged
,例如:
public event PropertyChangedEventHandler PropertyChanged;
// thanks to how DbConnectionStringBuilder is designed,
// we can override this central method
public override object this[string keyword]
{
get => base[keyword];
set
{
object existing = null;
try
{
existing = base[keyword];
}
catch
{
// do nothing
}
if (existing == null)
{
if (value == null)
return;
}
else if (existing.Equals(value))
return;
base[keyword] = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(keyword));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ConnectionString)));
}
}
对于现有的类,比如MySqlConnectionStringBuilder,你什么也做不了(除非用另一个实现ICustomTypeDescriptor
的类,用类似于这个DynamicTypeDescriptor的方法包装它们)。并不是每个。net类都可以很好地使用WPF绑定,甚至是标准的Winforms绑定。它是密封的…