Windows Phone 7 中的阿拉伯控件






4.31/5 (9投票s)
使您的 Windows Phone 7 应用程序支持从右到左的双向显示。
介绍
当微软十多年前推出 Windows CE 时,软件市场为新型应用程序打开了大门,我们都希望编写新应用程序或将已为 Win32 编写的应用程序移植到 Windows CE。不幸的是,对于阿拉伯市场,WinCE 2.11 和 WinCE 3.0 没有阿拉伯语支持,Windows Phone 7 也没有。
据我所知,微软会谈论带有阿拉伯语支持的新设备,但我并不这么认为,我们能做什么?
我们可以对此提出异议,或者我们可以采取变通方法来忽略这种情况。
我个人更喜欢第二种方法(做些事情或采取变通方法)。但 Windows Phone 7 仍然存在问题:主要问题是 Windows Phone 7 不支持双向 (BiDi) 和从右到左 (RTL) 的窗口控件。我不知道为什么。而且我们无法将我们的代码注入到短信、收件箱或 Word 等系统中。
背景
Windows Mobile 7 开发使用 .NET Framework。NET Framework 是微软为创建 Windows 应用程序而创建的软件框架。程序员使用 .NET Framework 支持的几种语言之一(如 C#)编写应用程序,然后应用程序在称为公共语言运行时 (Common Language Runtime) 的运行时环境中执行。对于 Windows Phone 7,您在创建应用程序时可以采取两种不同的开发方法。
第一种方法是使用 Silverlight for Windows Phone。Silverlight 最初设想是作为开发人员创建富 Internet 应用程序的一种方式。近年来,它的市场份额急剧增加,这主要归因于 Netflix 使用 Silverlight 流式传输视频,NBC 使用 Silverlight 在线转播奥运会。Silverlight 应用程序结合了声明式标记(称为 XAML)来构建用户界面,以及用 .NET Framework 语言编写的代码来控制应用程序的行为。如果您正在为 Windows Phone 7 开发数据驱动的应用程序,您可能应该使用 Silverlight。
Silver Light
Silverlight 是一个用于在移动、Web 和桌面设备上构建丰富控件和业务应用程序的平台,通常是代码和 XAML 的混合体。大多数情况下,您将使用 XAML 来定义应用程序视觉效果的布局,并使用代码来处理事件,包括所有用户输入事件以及控件因处理用户输入事件而生成的事件。
在 XAML 中执行的许多对象创建和初始化操作传统上会在页面或窗口类的构造函数中完成。这可能使 XAML 看起来只是应用程序的一小部分,但事实证明它远不止于此。顾名思义,XAML 完全符合 XML 标准,因此它是可工具化的——机器可写、机器可读,同时也可以人写、人读。
尽管 XAML 通常涉及对象创建和初始化,但 Silverlight 的某些功能提供的远不止对象初始化。其中一项功能是数据绑定,它涉及控件之间,或控件与底层数据之间的连接,以便在无需显式事件处理程序的情况下自动更新属性。整个动画也可以在 XAML 中定义。
虽然 XAML 有时被称为“声明性语言”,但它绝不是一门完整的编程语言。您无法在 XAML 中以任何通用方式执行算术运算,也无法在 XAML 中动态创建对象。
初次接触 XAML 的经验丰富的程序员有时会对其产生抵触情绪。我知道我曾经是。我们在 C# 等编程语言中珍视的所有内容——必需的声明、强类型、数组边界检查、用于调试的跟踪功能——当一切都简化为 XML 文本字符串时,大部分都消失了。然而,多年来,我已经非常适应 XAML,并且在将 XAML 用于应用程序的视觉效果时,我发现它非常自由。特别是,我喜欢窗口表面上控件的父子关系如何被 XML 中固有的父子结构所模仿。我也喜欢尝试 XAML 的能力——即使只是在 Visual Studio 设计器中。
您在 Silverlight 中需要做的所有事情都可以分配到以下三个类别:
• 可以在代码或 XAML 中完成的操作
• 只能在代码中完成的操作(例如,事件处理和方法)
• 只能在 XAML 中完成的操作(例如,模板)。
您将从本文中受益:
1-您对 Windows Phone 7 (WP7) 开发感兴趣
2-您想了解如何使用现有的 Silverlight 知识和工具来创建和部署 WP7 应用程序。
3-使您的应用程序支持阿拉伯语。
解决方案
首先,您无需担心 Windows 7 中的阿拉伯字体,它本身就带有阿拉伯字体,不像 Windows Mobile 和 Pocket PC。您需要将 2 个 DLL 复制到您的项目中,然后享受阿拉伯语支持的从右到左布局和阿拉伯键盘。
阿拉伯化涉及反转字符串,考虑到数字不应反转,并手动执行字形替换。

使用代码
现在让我们讨论我们代码中的一些部分。我想分享一些关于从右到左的代码。第二个步骤有点棘手,所以请做好准备。我们知道内存中的阿拉伯字符串包含 Unicode 字符,这些字符具有与字母的孤立字形相同的形式。我们必须将字符映射到要显示的正确字形,同时考虑字母的所有状态。
我设法构建了一个包含所有阿拉伯字母状态的数组。它不是按字母顺序排序的。该表的第一行是我收集到的关于一个字母的第一个数据,即字母 'thal'。
据我所知,字形替换的规则就在 TTF 文件中,但我发现它非常复杂。为了帮助构建字形替换的规则,我根据字符是否需要与前一个或后一个字符连接,制作了两组字符。
字符映射表
//
public ArabicShape()
{
//
// TODO: Add constructor logic here
theSet1 = new char[22]
{
(char)0x62c, (char)0x62d, (char)0x62e, (char)0x647, (char)0x639, (char)0x63a, (char)0x641, (char)0x642,
(char)0x62b, (char)0x635, (char)0x636, (char)0x637, (char)0x643, (char)0x645, (char)0x646, (char)0x62a,
(char)0x644, (char)0x628, (char)0x64a, (char)0x633, (char)0x634, (char)0x638
};
theSet2 = new char[13]
{
(char)0x627, (char)0x623, (char)0x625, (char)0x622, (char)0x62f, (char)0x630, (char)0x631, (char)0x632,
(char)0x648, (char)0x624, (char)0x629, (char)0x649,
(char)0x698,
};
a = new char[N_DISTINCT_CHARACTERS, 5]
{
{(char)0x630, (char)0xfeac, (char)0xfeab, (char)0xfeac, (char)0xfeab},
{(char)0x62f, (char)0xfeaa, (char)0xfea9, (char)0xfeaa, (char)0xfea9},
{(char)0x62c, (char)0xfe9e, (char)0xfe9f, (char)0xfea0, (char)0xfe9d},
{(char)0x62d, (char)0xfea2, (char)0xfea3, (char)0xfea4, (char)0xfea1},
{(char)0x62e, (char)0xfea6, (char)0xfea7, (char)0xfea8, (char)0xfea5},
{(char)0x647, (char)0xfeea, (char)0xfeeb, (char)0xfeec, (char)0xfee9},
{(char)0x639, (char)0xfeca, (char)0xfecb, (char)0xfecc, (char)0xfec9},
{(char)0x63a, (char)0xfece, (char)0xfecf, (char)0xfed0, (char)0xfecd},
{(char)0x641, (char)0xfed2, (char)0xfed3, (char)0xfed4, (char)0xfed1},
{(char)0x642, (char)0xfed6, (char)0xfed7, (char)0xfed8, (char)0xfed5},
{(char)0x62b, (char)0xfe9a, (char)0xfe9b, (char)0xfe9c, (char)0xfe99},
{(char)0x635, (char)0xfeba, (char)0xfebb, (char)0xfebc, (char)0xfeb9},
{(char)0x636, (char)0xfebe, (char)0xfebf, (char)0xfec0, (char)0xfebd},
{(char)0x637, (char)0xfec2, (char)0xfec3, (char)0xfec4, (char)0xfec1},
{(char)0x643, (char)0xfeda, (char)0xfedb, (char)0xfedc, (char)0xfed9},
{(char)0x645, (char)0xfee2, (char)0xfee3, (char)0xfee4, (char)0xfee1},
{(char)0x646, (char)0xfee6, (char)0xfee7, (char)0xfee8, (char)0xfee5},
{(char)0x62a, (char)0xfe96, (char)0xfe97, (char)0xfe98, (char)0xfe95},
{(char)0x627, (char)0xfe8e, (char)0xfe8d, (char)0xfe8e, (char)0xfe8d},
{(char)0x644, (char)0xfede, (char)0xfedf, (char)0xfee0, (char)0xfedd},
{(char)0x628, (char)0xfe90, (char)0xfe91, (char)0xfe92, (char)0xfe8f},
{(char)0x64a, (char)0xfef2, (char)0xfef3, (char)0xfef4, (char)0xfef1},
{(char)0x633, (char)0xfeb2, (char)0xfeb3, (char)0xfeb4, (char)0xfeb1},
{(char)0x634, (char)0xfeb6, (char)0xfeb7, (char)0xfeb8, (char)0xfeb5},
{(char)0x638, (char)0xfec6, (char)0xfec7, (char)0xfec8, (char)0xfec5},
{(char)0x632, (char)0xfeb0, (char)0xfeaf, (char)0xfeb0, (char)0xfeaf},
{(char)0x648, (char)0xfeee, (char)0xfeed, (char)0xfeee, (char)0xfeed},
{(char)0x629, (char)0xfe94, (char)0xfe93, (char)0xfe93, (char)0xfe93},
{(char)0x649, (char)0xfef0, (char)0xfeef, (char)0xfef0, (char)0xfeef},
{(char)0x631, (char)0xfeae, (char)0xfead, (char)0xfeae, (char)0xfead},
{(char)0x624, (char)0xfe86, (char)0xfe85, (char)0xfe86, (char)0xfe85},
{(char)0x621, (char)0xfe80, (char)0xfe80, (char)0xfe80, (char)0xfe7f},
{(char)0x626, (char)0xfe8a, (char)0xfe8b, (char)0xfe8c, (char)0xfe89},
{(char)0x623, (char)0xfe84, (char)0xfe83, (char)0xfe84, (char)0xfe83},
{(char)0x622, (char)0xfe82, (char)0xfe81, (char)0xfe82, (char)0xfe81},
{(char)0x625, (char)0xfe88, (char)0xfe87, (char)0xfe88, (char)0xfe87},
};
//
}
private void ArabiChar(ref string s)
{
string out1, rev;
out1 = "";
char[] dest = s.ToCharArray();
System.Array.Reverse(dest, 0, dest.Length);
s = new string(dest);
int i = 0;
while (i < s.Length)
{
if ((s[i] >= '0' && s[i] <= '9')) // isDigit(s[i]) ?
{
rev = "";
while ((s[i] >= '0' && s[i] <= '9')) // isDigit(s[i]) ?
{
rev = rev + s[i];
++i;
}
dest = rev.ToCharArray();
System.Array.Reverse(dest, 0, dest.Length);
rev = new string(dest);
//rev.MakeReverse();
out1 = out1 + rev;
}
else
{
out1 = out1 + s[i];
++i;
}
}
s = out1;
}
public string DisplayArabic(string s)
{
str = s;
bool linkBefore, linkAfter;
Pstr = str;
for (int i = 0; i < str.Length; i++)
{
char ch = str.ToCharArray()[i];
if ((ch >= 0x0621 && ch <= 0x064a) || (ch >= 0x067e && ch <= 0x06d5)) // is an Arabic character?
{
int idx = 0;
while (idx < N_DISTINCT_CHARACTERS)
{
if (a[idx, 0] == str.ToCharArray()[i])
break;
++idx;
}
if (i == str.Length - 1)
linkAfter = false;
else
linkAfter = (isFromTheSet1(str.ToCharArray()[i + 1]) ||
isFromTheSet2(str.ToCharArray()[i + 1]));
if (i == 0)
linkBefore = false;
else
linkBefore = isFromTheSet1(str.ToCharArray()[i - 1]);
if (linkBefore && linkAfter)
{
Pstr = Pstr.Remove(i, 1);
Pstr = Pstr.Insert(i, new string(a[idx, 3], 1));//mid
}
if (linkBefore && !linkAfter)
{
Pstr = Pstr.Remove(i, 1);
Pstr = Pstr.Insert(i, new string(a[idx, 1], 1));//end
}
if (!linkBefore && linkAfter)
{
Pstr = Pstr.Remove(i, 1);
Pstr = Pstr.Insert(i, new string(a[idx, 2], 1));//init
}
if (!linkBefore && !linkAfter)
{
Pstr = Pstr.Remove(i, 1);
Pstr = Pstr.Insert(i, new string(a[idx, 4], 1));//iso
}
}
}
ArabiChar(ref Pstr);
return Pstr;
}
//
在这段代码中,我检查字符是否可以从开头连接,或者是否可以从结尾连接,或者字符是否根本无法连接。您可以在 Windows 系统工具的字符映射表中查看阿拉伯 Unicode。
private bool isFromTheSet1(char ch)
{
int i = 0;
while (i < 22)
{
if (ch == theSet1[i])
return true;
++i;
}
return false;
}
private bool isFromTheSet2(char ch)
{
int i = 0;
while (i < 13)
{
if (ch == theSet2[i])
return true;
++i;
}
return false;
}
在这里,我们将代码放在 private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) 中。
/*
* do some extra initialize like keybd Coordinate and button size
* and some brushes info
*/
this.keyboard.InitializeKeyboard();
/*
* defined handle for key bosrd ,it's fired when keybd show or hide
*/
this.keyboard.KeyboardHide += new EventHandler(keyboard_KeyboardHide);
this.keyboard.KeyboardShow += new EventHandler(keyboard_KeyboardShow);
/*
* defined handler for edit control ,it's fired when we
* put the fniger inside edit (WM_SETFOCUS)
*/
this.textBlock.GotFocus += new RoutedEventHandler(textBlock1);
this.rTLTextBox1.GotFocus += new RoutedEventHandler(textBlock2);
这里有一段代码说明了如何初始化 ArabicShape 类,我们可以看到如何添加阿拉伯字符串,这非常容易。
//Intilize Arabic Shape Class
crAr = new Arabic.Controls.ArabicShape();
listBox1.Items.Add("إختبار عربي");
/*Here to make Control Display Arabic items corectly*/
listBox1.Items.Add(crAr.DisplayArabic("إختبار عربي")); //look to Display Arabic Function
btnClose.Content = crAr.DisplayArabic("دخول");
我们重写了 BackKey 函数来处理一些与 SIP(屏幕键盘)相关的问题。您知道这个键盘是我们开发的,所以当用户点击退出应用程序时,SIP 可能仍然显示,所以您肯定会担心。我建议当用户点击返回按钮时,我想隐藏 SIP,如果 SIP 已经显示,我想调用原始过程。
if (SIP.Instance.Visible)
{
e.Cancel = true;
this.keyboard.Dismiss();
}
else
{
NavigationService.GoBack(); }
历史记录:
我将继续更新本文,我将对 Windows Phone 7.1 进行一些更改。