如何正确使用在 C++/CLI 中实现的 class 作为 .net 中数据绑定的数据源?
How to correctly use a class implemented in C++/CLI as a data source for data binding in .net?
我在 VC++ 中定义了一个抽象 class,如下所示:
public ref class AbstractFoo abstract {
public:
virtual property String^ Bar {
virtual String^ get() = 0;
}
// More stuff
};
我有多个具体的 class 是这样的:
public ref class ConcreteFoo : AbstractFoo {
public:
property String^ Bar {
virtual String^ get() override;
}
// more
}
我现在想 select 在 C# 应用程序的 ComboBox 中实现几个 AbstractFoo
之一。因此,我单击组合框上的小箭头并选中 "Use Data Bound items"。然后我尝试创建一个 AbstractFoo
类型的新数据源。然后单击 Finish
时,我得到:
Error using the dropdown: Object reference not set to an instance of an object.
当我关闭对话框时,VS 退出。当我使用 ConcreteFoo
.
时也会发生这种情况
目前,我能想到的唯一可行的解决方案是在 C# 中定义一个 class,如下所示:
internal sealed class FooWrapper {
private readonly AbstractFoo _foo;
internal FooWrapper(AbstractFoo foo) {
_foo = foo;
}
public String DisplayName { get { return _foo.Bar; } }
public static implicit operator AbstractFoo(FooWrapper w) {
return w._foo;
}
}
然后添加这些 FooWrappers
并使用隐式转换取回 AbstractFoo
。
这真的是唯一的方法吗,还是我在 VS 卡住的地方声明了错误?
编辑:创建 GUI 时,ConcreteFoo
实现已经存在。
我对此进行了更多调查,并找到了导致此问题的原因。据我目前所知,除了上述解决方法外,没有其他解决方案。
它与托管/非托管互操作有关。我在这个项目中仍然绑定到 VS2010,所以我不知道这只是那里的一个错误还是我配置错误。
这是我重现该行为的方式:
首先,我创建了一个 C++/CLI class 库,其中包含非常简单的抽象和具体 class(我们暂时称它们为 AbstractThing
和 ConcreteThing
)。然后,我创建了一个简单的 C# Windows Forms 应用程序,其中仅包含一个组合框,该组合框绑定到具有 AbstractThing
类型的绑定源。这有效。
然后我像往常一样将依赖项添加到非托管库:将非托管库的包含目录指定为附加包含目录,该目录包含 unmanaged.lib
和 unmanaged.dll
两者调试和发布模式,并添加 unmanaged.lib
作为附加库。然后,我在 C# 应用程序中添加了一个 post-build 命令,该命令在成功构建后将适当的 unmanaged.dll
复制到构建目录中。
重新创建绑定仍然有效。
现在,进入游戏破坏者:
当我向非托管库添加一个包含 #include <unmanaged.h>
时,我得到了上述错误并且 VS 退出了。
为了解决问题,我然后创建了一个如上所述的包装器,它起初有效,但导致了下一个奇怪的错误:当我重新进入 GUI 设计器时,我得到一个错误页面找不到我的 managed/unmanaged 互操作 DLL(包含 AbstractThing
和 ConcreteThing
的 DLL)。案例是:.net 的数据绑定分析 classes 的 public 属性,如果它们中的任何一个恰好属于与非托管内容互操作的类型,事情就会变得疯狂。我相信 GUI 设计器和 VS 在错误的目录中查找依赖的非托管 DLL。
因此,唯一可行的方法是编写包装器并通过使用内部 属性 或方法显式提取来隐藏数据绑定中的互操作内容。
因此,示例 AbstractThing
class 的包装器现在可以工作了:
internal sealed class ThingWrapper {
private readonly AbstractThing _thing;
internal ThingWrapper(AbstractThing thing) {
_thing = thing;
}
public String DisplayName { get { return _thing.SomeStringProperty; } }
internal AbstractThing Thing { get { return _thing; } }
}
制作internal AbstractThing Thing
属性public
导致设计器无法正确加载,所以无法使用绑定源的ValueMember
属性。自 AbstractThing theThing = ((ThingWrapper) theComboBox.SelectedItem).Thing;
起必须手动完成提取
我在 VC++ 中定义了一个抽象 class,如下所示:
public ref class AbstractFoo abstract {
public:
virtual property String^ Bar {
virtual String^ get() = 0;
}
// More stuff
};
我有多个具体的 class 是这样的:
public ref class ConcreteFoo : AbstractFoo {
public:
property String^ Bar {
virtual String^ get() override;
}
// more
}
我现在想 select 在 C# 应用程序的 ComboBox 中实现几个 AbstractFoo
之一。因此,我单击组合框上的小箭头并选中 "Use Data Bound items"。然后我尝试创建一个 AbstractFoo
类型的新数据源。然后单击 Finish
时,我得到:
Error using the dropdown: Object reference not set to an instance of an object.
当我关闭对话框时,VS 退出。当我使用 ConcreteFoo
.
目前,我能想到的唯一可行的解决方案是在 C# 中定义一个 class,如下所示:
internal sealed class FooWrapper {
private readonly AbstractFoo _foo;
internal FooWrapper(AbstractFoo foo) {
_foo = foo;
}
public String DisplayName { get { return _foo.Bar; } }
public static implicit operator AbstractFoo(FooWrapper w) {
return w._foo;
}
}
然后添加这些 FooWrappers
并使用隐式转换取回 AbstractFoo
。
这真的是唯一的方法吗,还是我在 VS 卡住的地方声明了错误?
编辑:创建 GUI 时,ConcreteFoo
实现已经存在。
我对此进行了更多调查,并找到了导致此问题的原因。据我目前所知,除了上述解决方法外,没有其他解决方案。
它与托管/非托管互操作有关。我在这个项目中仍然绑定到 VS2010,所以我不知道这只是那里的一个错误还是我配置错误。
这是我重现该行为的方式:
首先,我创建了一个 C++/CLI class 库,其中包含非常简单的抽象和具体 class(我们暂时称它们为 AbstractThing
和 ConcreteThing
)。然后,我创建了一个简单的 C# Windows Forms 应用程序,其中仅包含一个组合框,该组合框绑定到具有 AbstractThing
类型的绑定源。这有效。
然后我像往常一样将依赖项添加到非托管库:将非托管库的包含目录指定为附加包含目录,该目录包含 unmanaged.lib
和 unmanaged.dll
两者调试和发布模式,并添加 unmanaged.lib
作为附加库。然后,我在 C# 应用程序中添加了一个 post-build 命令,该命令在成功构建后将适当的 unmanaged.dll
复制到构建目录中。
重新创建绑定仍然有效。
现在,进入游戏破坏者:
当我向非托管库添加一个包含 #include <unmanaged.h>
时,我得到了上述错误并且 VS 退出了。
为了解决问题,我然后创建了一个如上所述的包装器,它起初有效,但导致了下一个奇怪的错误:当我重新进入 GUI 设计器时,我得到一个错误页面找不到我的 managed/unmanaged 互操作 DLL(包含 AbstractThing
和 ConcreteThing
的 DLL)。案例是:.net 的数据绑定分析 classes 的 public 属性,如果它们中的任何一个恰好属于与非托管内容互操作的类型,事情就会变得疯狂。我相信 GUI 设计器和 VS 在错误的目录中查找依赖的非托管 DLL。
因此,唯一可行的方法是编写包装器并通过使用内部 属性 或方法显式提取来隐藏数据绑定中的互操作内容。
因此,示例 AbstractThing
class 的包装器现在可以工作了:
internal sealed class ThingWrapper {
private readonly AbstractThing _thing;
internal ThingWrapper(AbstractThing thing) {
_thing = thing;
}
public String DisplayName { get { return _thing.SomeStringProperty; } }
internal AbstractThing Thing { get { return _thing; } }
}
制作internal AbstractThing Thing
属性public
导致设计器无法正确加载,所以无法使用绑定源的ValueMember
属性。自 AbstractThing theThing = ((ThingWrapper) theComboBox.SelectedItem).Thing;