这个博文是纯原创的,转载一定要说明作者是 Nukepayload2!!
在.Net Core 中,很多地方被精简了,有个重灾区就是vb语言库。从当初的囊括vb6库函数并且附带后期绑定到现在的几个函数加上后期绑定,连End和Mid语句对应的库函数都被删掉了。
其中有些函数是不该删掉的。那么要用的话就得手动还原一下了。
首先是各种Hello world里面喜闻乐见的 MsgBox 和 InputBox 函数。
它们在Microsoft.VisualBasic的Interaction里面。
新建个模块,叫Interaction。
先分析一下 MsgBox 。这个函数作用是弹窗,显示标题,内容和选项按钮。
按钮比较常用的是两个按钮和一个按钮的情况。帮助不常用,三个按钮也不常用。
那么就实现一个或两个按钮的好了。返回值是True就按了确定,False是按了取消,如果没有值就说明对话框被强行关闭了。
由于是uwp,同步版本的很难实现。那就写个异步版本的好了。
Public Async Function MsgBoxAsync(Prompt$, HasCancel As Boolean, Title$, Optional OK$ = "确定", Optional Cancel$ = "取消") As Task(Of Boolean?) Dim dlg As New MessageDialog(Prompt, Title) Dim Result As Boolean? If HasCancel Then Dim msg As New MessageDialog(Prompt, Title) msg.Commands.Add(New UICommand(OK, Sub(command) Result = True)) msg.Commands.Add(New UICommand(Cancel, Sub(command) Result = False)) msg.DefaultCommandIndex = 0 msg.CancelCommandIndex = 1 Dim tsk = msg.ShowAsync Await tsk Return Result Else Await New MessageDialog(Prompt, Title).ShowAsync Return True End If End Function
接下来轮到 InputBox 了。
这个是用来收集输入的,一个文本框,一句提示语,两个常用的按钮,也附带了不常用的帮助功能按钮。
为了图省事我还是不写帮助按钮了。取消按钮用处也不大,不写了,因为UWP的文本框自带清除按钮。
首先新建个对话框,在Xaml代码写这个:
<ContentDialog x:Class="Nukepayload2.VisualBasicExtensions.UWP.InputBox" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <ContentDialog.Content> <Grid MinWidth="100" MinHeight="100"> <Grid.RowDefinitions> <RowDefinition Height="13*"/> <RowDefinition Height="7*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="71*"/> <ColumnDefinition Width="19*"/> </Grid.ColumnDefinitions> <TextBlock x:Name="TxtPrompt"></TextBlock> <TextBox x:Name="TxtOutput" Grid.Row="1"></TextBox> <Button Grid.Row="1" Grid.Column="1" Click="BtnOk_Click">确定</Button> </Grid> </ContentDialog.Content> </ContentDialog>
后面的vb代码(考虑了虚拟键盘):
Public NotInheritable Class InputBox Inherits ContentDialog Public Overloads Async Function ShowAsync(Prompt As String, Title As String, Optional InputScope As InputScopeNameValue = InputScopeNameValue.Text) As Task(Of String) Me.Title = Title TxtPrompt.Text = Prompt TxtOutput.Text = "" TxtOutput.InputScope = New InputScope TxtOutput.InputScope.Names.Add(New InputScopeName(InputScope)) Await ShowAsync() Return TxtOutput.Text End Function Private Sub BtnOk_Click(sender As Object, e As RoutedEventArgs) Hide() End Sub End Class
我的控件命名方式有点奇葩,大家按照自己的习惯写就好。
最后在Interaction模块写上这个函数的代码
Dim inputbox As New InputBox() Public Async Function InputBoxAsync(Prompt$, Title$, Optional InputScope As InputScopeNameValue = InputScopeNameValue.Text) As Task(Of String) Return Await inputbox.ShowAsync(Prompt, Title, InputScope) End Function
还有些比较常用的,比如Rnd和Randomize
这两个随机数函数比直接用Random类方便一些,尤其是做单精度浮点数计算的时候。
这里我没有用BitConverter转换Single和Integer,而是用联合体。读者自己实现的时候可以尝试一下BitConverter。
还有,默认的0作为种子这个限制可以顺手去除。
Imports System.Runtime.InteropServices Public Module VBMath Dim rand As New RandomPublic Sub Randomize() rand = New Random End Sub Public Sub Randomize(Number As Single) rand = New Random(New SingleInt32(Number).Int32Value) End Sub Public Function Rnd() As Single Return rand.NextDouble End Function Public Function Rnd(Number As Single) As Single rand = New Random(New SingleInt32(Number).Int32Value) Return Rnd End Function Private Structure SingleInt32 Sub New(SingleValue!) Me.SingleValue = SingleValue End Sub Sub New(Int32Value%) Me.Int32Value = Int32Value End Sub <FieldOffset(0)> Dim SingleValue! <FieldOffset(0)> Dim Int32Value% End Structure End Module
接下来要重新实现的函数在游戏角色名称处理,敏感词处理等场合用得比较多。
多功能的字符串转换器 StrConv
不管是简体繁体转换,还是全角半角转换,或者是特殊的大小写格式转换,它都能搞定。
我写之前查了msdn,上面只能找到一个本机的函数能替代它。那么我就利用它实现StrConv。
声明是这个,写之前要看看。这个函数在Windows.h里面声明了。
int LCMapStringEx( _In_opt_ LPCWSTR lpLocaleName, _In_ DWORD dwMapFlags, _In_ LPCWSTR lpSrcStr, _In_ int cchSrc, _Out_opt_ LPWSTR lpDestStr, _In_ int cchDest, _In_opt_ LPNLSVERSIONINFO lpVersionInformation, _In_opt_ LPVOID lpReserved, _In_opt_ LPARAM sortHandle );
看完之后就开始折腾!
新建个windows 运行时组件,然后新建给类,把这个写进去:
static String^ LCMapString(String^ LocaleName, int MapFlags, String^ Source) { int len = Source->Length(); auto str = ref new String(new wchar_t[len + 1], len); LCMapStringEx(LocaleName->Begin(), MapFlags, Source->Begin(), len, const_cast<wchar_t*>(str->Begin()), len, NULL, NULL, NULL); return str; }
上面和下面这段c++/cx代码只是传达一下意思,并没有经过测试。
static String^ LCMapString(int MapFlags, String^ Source) { len = Source->Length(); auto str = ref new String(new wchar_t[len + 1], len); LCMapStringEx(LOCALE_NAME_USER_DEFAULT, MapFlags, Source->Begin(), len, const_cast<wchar_t*>(str->Begin()), len, NULL, NULL, NULL); return str; }
注意看参数,里面的MapFlags还跟本机的LCMapStringEx一样,需要把vb的常量转换为这种标记值。
首先定义一下VbStrConv枚举。这个部分也可以用vb完成。
public enum class VbStrConv { Hiragana = 0x20, Katakana = 0x10, LinguisticCasing = 0x400, Lowercase = 2, Narrow = 8, None = 0, ProperCase = 3, SimplifiedChinese = 0x100, TraditionalChinese = 0x200, Uppercase = 1, Wide = 4 };
然后写转换函数。如果那个枚举是在vb实现的,转换函数也在vb写。
static int VbStrConvToMapFlags(VbStrConv value) { int val = static_cast<int>(value); int flag = 0; //宽窄 if (val & VbStrConv::Wide == VbStrConv::Wide) { flag &= LCMAP_FULLWIDTH; } else if (val & VbStrConv::Narrow == VbStrConv::Narrow) { flag &= LCMAP_HALFWIDTH; } //大小写 if (val & VbStrConv::ProperCase == VbStrConv::ProperCase) { flag &= LCMAP_TITLECASE; } else if (val & VbStrConv::Uppercase == VbStrConv::Uppercase) { flag &= LCMAP_UPPERCASE; } else if (val & VbStrConv::Lowercase == VbStrConv::Lowercase) { flag &= LCMAP_LOWERCASE; } else if (val & VbStrConv::LinguisticCasing == VbStrConv::LinguisticCasing) { flag &= LCMAP_LINGUISTIC_CASING; } //日语 if (val & VbStrConv::Hiragana == VbStrConv::Hiragana) { flag &= LCMAP_HIRAGANA; } else if (val & VbStrConv::Katakana == VbStrConv::Katakana) { flag &= LCMAP_KATAKANA; } //汉语 if (val & VbStrConv::SimplifiedChinese == VbStrConv::SimplifiedChinese) { flag &= LCMAP_SIMPLIFIED_CHINESE; } else if (val & VbStrConv::TraditionalChinese == VbStrConv::TraditionalChinese) { flag &= LCMAP_TRADITIONAL_CHINESE; } return flag; }
剩下的工作就交给vb了。Locale名称与之前的库函数不太一样,之前用的是LCID。重新实现后的用的是字符串,更加容易与已经存在的本地化API共同使用。
Imports Nukepayload2.VisualBasicExtensions.UWP.Native.Strings Public Module Strings Public Function StrConv$(Source$, Conversion As VbStrConv) Return LCMapString(VbStrConvToMapFlags(Conversion), Source) End Function Public Function StrConv$(Source$, Conversion As VbStrConv, LocaleName$) Return LCMapString(LocaleName, VbStrConvToMapFlags(Conversion), Source) End Function End Module