65.9K
CodeProject 正在变化。 阅读更多。
Home

构建 Silverlight 企业应用程序的冒险经历 - 第 25 部分

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2009年10月8日

CPOL

4分钟阅读

viewsIcon

9482

构建 Silverlight 企业应用程序的冒险经历 - 第 25 部分

在前一集中,我讨论了如何使用 enum 来封装数据库中的 char 值,这里以 Gender enum 为例。虽然这很酷,但它确实给 Silverlight 中的数据绑定带来了一个问题,我想与您分享。部分解释将我们带入反射领域(这将是一个很酷的博客标题:)),然后进入一些很酷的绑定技巧,最终以一点失望告终。所以如果你已经感到沮丧,现在就停止阅读吧 :-P。

故事

首先有一个 BussinessObjectBase。这个类处理很多关于在服务和客户端之间传输数据的事情。它还有助于跟踪属性更改并通知它们。这是一个非常通用的类,它作为我们所有“业务”对象的基类。

在这种情况下,假设有一个 PersonBase 类,它派生自 BusinessObjectBasePersonBase 是基于一些元数据生成的,所以我们不想修改这个源代码。PersonBase 类有一个名为 Gender 的属性,其类型为 string

然后是 Person 类,它是生成的,但旨在用于自定义代码,所以我们将在那里编写一些代码,使我们的 Gender 可用作 Gender 而不是 string

我们可以简单地在 Person 类中编写如下内容

private char GetGender()
{
string gender = base.Gender;
if (gender.Length == 0)
{
return DefaultGender;
}
return gender[0];
}

public Gender GenderValue
{
get
{
return (Gender)GetGender();
}
set
{
base.Gender = ((char)value).ToString();
}
}

我们的意图是隐藏 Gender 属性的基本实现,并拥有我们自己的实现,该实现返回实际的 Gender enum。到目前为止没有什么特别的。

下一步是在数据绑定场景中使用这个新属性。这是 XAML 中的绑定语句的样子

{Binding Path=Gender, Mode=TwoWay}

现在,如果您尝试使用像这样的绑定语句并运行代码,会发生什么?您会得到一个 AmbiguousMatchException,原因是绑定引擎无法区分 PersonBase.GenderPerson.Gender

什么?但我们想要隐藏 PersonBase.Gender,对吗? 绝对是的,但这两个属性都是 public,所以实际上都是可用的。

反思“新”属性

为了更好地理解为什么会发生这种情况,我决定编写一些反射代码

object person = newPerson();
object personBase = newPersonBase();

Type personType = person.GetType();
PropertyInfo personGenderProperty = personType.GetProperty("Gender");
MessageBox.Show(personGenderProperty.GetValue(person, null).ToString());

实际上尝试通过反射获取 Gender 属性的值会抛出 AmbiguousMatchException,就像绑定到它时一样。 实际上从 Visual Studio 的即时窗口调用 personType.GetProperties 会返回两个属性。 然后这个异常突然变得有意义了。

可能看起来有点尴尬的是两个属性都存在。如果我尝试编写 string gender = somePerson.Gender,其中 somePerson 的类型是 Person,那么它将无法编译,因为我无法隐式转换它,这证明了基本属性实际上是隐藏的。 尽管如此,仍然需要提供该属性,因为派生类需要能够调用基类中的属性。

仍然尝试绑定到它

尽管如此,我还是需要找到一种方法来绑定到这个属性。我想出了一些简单的解决方案

  1. 使基本属性受到保护
  2. 重命名其中一个属性以完全消除问题
  3. 找到一种方法来区分数据绑定中的两个属性

第一个解决方案对我不起作用,因为我无法修改此代码(记住,它是生成的?)。

第二个解决方案意味着我必须在派生类中重命名该属性,这似乎不太好。

第三个解决方案希望渺茫,但我必须尝试一下。 我实际上找到了用于这种情况的语法,但是它的文档记录不太完善,所以我做了一些实验以更好地理解它。 我开始在 XAML 中编写此内容

{Binding Path=(local:Person.Gender)}

这失败了,并出现一个异常,告诉我这是属性的无效值。 我感到惊讶和困惑,因为我读到其他人正在使用它,并且实际上在文档中是这样的。 进一步的调查告诉我,这仅适用于依赖属性。 我为此构建了一个小例子,它实际上运行得很好,但是......

...拥有一个包含所有管道的依赖属性最好通过从 DependencyObject 派生您的类来完成(其中包含诸如 GetValueSetValue 之类的东西)。 我显然不能从 DependencyObject 派生 PersonPersonBase,因为我已经从另一个类派生了它们。 这意味着我现在必须在派生类中重命名该属性 :(。

您可以在此处找到绑定到新属性的示例。 希望对您有所帮助。 总的来说,这是一次很棒的学习经历。 只是很遗憾它没有为我带来更好的解决方案。

© . All rights reserved.