在WPF开发中,经常需要使用泛型来创建灵活且可重用的组件。然而,泛型在XAML中的使用并不像在C#代码中那样直观。幸运的是,WPF团队提供了正确的机制,使得开发者能够在XAML中完全支持泛型。本文将通过一个简单的示例,展示如何在XAML中实现泛型支持。
首先,需要了解一个典型的WPF窗口由两个部分构成:代码后置(code behind)和XAML。在编译时,这两个部分会合并成一个生成的文件。为了在由代码后置和XAML组成的文件中正确支持泛型,必须确保这两个部分都使用了泛型类型参数。下面通过一个示例来说明这一点。
假设有一个通用的窗口基类,它是泛型的。然后继承这个基类,并在代码后置和XAML中提供泛型参数,使其成为泛型类型。为了演示这一点,构建了一个小型演示应用程序,其中包括以下内容:
首先,定义了一个名为LoggingStrategy
的抽象基类,如下所示:
using System;
namespace GenericViews
{
public abstract class LoggingStrategy
{
public abstract void LogText(string textToLog);
}
}
然后,定义了一个具体的类,它重写了LogText(…)
方法,如下所示:
using System;
using System.Windows;
namespace GenericViews
{
public class MessageBoxLogger : LoggingStrategy
{
public override void LogText(string textToLog)
{
MessageBox.Show(textToLog);
}
}
}
接下来,在WPF应用程序的主App
类中,创建了一个简单的Dictionary
查找,用于根据Type
获取正确的日志策略。这个Type
将来自视图提供的泛型参数,稍后会看到。
using System;
using System.Collections.Generic;
using System.Windows;
namespace GenericViews
{
public partial class App : Application
{
public static Dictionary Loggers = new Dictionary();
static App()
{
Loggers.Add(typeof(MessageBoxLogger), new MessageBoxLogger());
}
}
}
现在已经涵盖了所有演示内容,接下来来处理实际的泛型部分。在附带的演示中,提供了一个小型的视图基类,这个基类是泛型的,并将从static App Dictionary
中查找正确的LoggingStrategy
继承对象。
using System;
using System.Collections.Generic;
using System.Windows;
namespace GenericViews
{
public class ViewBase : Window where T : LoggingStrategy
{
public LoggingStrategy CurrentLogger { get; private set; }
public ViewBase()
{
CurrentLogger = App.Loggers[typeof(T)];
}
}
}
可以看到,这个基类期望一个泛型参数T
,以便工作。T
必须继承自LoggingStrategy
。然后,泛型参数用于从static App Dictionary
中获取正确的LoggingStrategy
继承对象。
接下来,需要创建一个自定义窗口,它继承自ViewBase
,并传递一个泛型参数。让从简单的代码后置部分开始。这部分非常简单,只需使用标准的泛型,就像使用任何其他泛型类一样:
using System;
using System.Collections.Generic;
using System.Windows;
namespace GenericViews
{
public partial class MessageLoggingWindow : ViewBase
{
public MessageLoggingWindow() : base()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
base.CurrentLogger.LogText(String.Format("Some text which was logged at {0}", DateTime.Now.ToShortTimeString()));
}
}
}
现在简单的部分已经完成,让专注于XAML部分。
<local:ViewBase
x:Class="GenericViews.MessageLoggingWindow"
x:TypeArguments="local:MessageBoxLogger"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GenericViews"
>
…
</local:ViewBase>
这里发生了什么?实际上有一些事情,让逐一了解。
x:Class
:这是类的名称,它与代码后置的命名空间和类名匹配。
xmlns:local
:这是一个命名空间别名,允许在XAML中使用local:
指向GenericViews
命名空间中的类。
<local:ViewBase…/>
:意味着使用GenericViews.ViewBase<T>
作为这个新窗口的基类,但是等等,泛型参数在哪里?这就是第四步。
x:TypeArguments
:这是泛型值插入的地方。所以现在在XAML中声明了一个窗口,它实际上是GenericViews.ViewBase<MessageBoxLogger>
。
就是这样……知道例子并不完全符合现实世界,但那不是本文的重点,只是想向展示如何在XAML中创建泛型,从这个角度来看,这篇文章应该能够做到。
这是一个小型演示应用程序,它创建了一个带有MessageBoxLogger
的泛型窗口,运行时看起来像这样:
希望喜欢!