其实吧

其实吧,我并没有像别人想象的那般。

这我从签企鹅实习Offer的一开始就在反复提醒自己的一句话,我一直觉得自己在某些程度上是依靠运气,或者仅仅是外因占主导的原因,才会有今天这样的成绩。就如我昨天晚上和同学在天台聊天时说的,如果不是企鹅这两年扩张,如果不是因为我们组去年初人手不够,如果不是我大二去混了个ACM奖,如果不是我们部门早期就用了这非主流的.Net框架,根本不会有我这个岗位的需求。或者早一年,晚一年,我都没机会进入这种想都没想过的地方。所以我一直庆幸,自己的运气如此之好,就这样混进去了。

我一直很怕别人和我说,zkd,听说你算法很厉害。

我每次听到这样的话简直不知所措,面红耳赤,羞愧得无地自容。初二到高二,玩了四年NOIP,打了四年酱油,当了四年分母。高三暑假无聊买了一本《算法导论》,3年之后因为看不懂于是把书送人了。大二参加本部ACM比赛,依靠Cosmos的提点混进了复赛,又靠着两个队友的发挥才勉强没有再次成为分母,混了一个末位安慰奖。

而我这四年写了些什么?toy toy toy,还是toy。只是组装了别人生产的零件而已,而且还是照着图纸的那种。按图索骥罢了。

还有什么?还有很多很多。其实我就是那种什么都会一点,但是真正发现我什么都不会的那种。我不知道有多少人会像我这样,一定不少,但是不一定如我这样严重。

总之吧,最近实在提不起精神来,毕业论文也还没动笔,心里想的很多很多想法,也仅仅是些想法。

生活继续浑浑噩噩地过吧,每天努力着活着便是,其它都是无谓的。

豆瓣电台 for Windows Phone 7.5开发笔记 Ex

之前在博客园写过一篇《豆瓣电台 for Windows Phone 7.5开发笔记》,当时草草而谈,并没有太深入研究,现在在这里对之前的开发过程做一个总结。

Windows Phone上的.Net并不是完整的.Net 2、3、3.5、4版本或者其子集,而是一套针对移动设备开发的,类似于.Net Framework for Silverlight,与Windows 8上Metro App使用的WinRT API极其相似的一个开发框架。所以之前有报道说Silverlight已死,死的只是Silverlight这个产品而已,而Silverlight的开发框架将会在Windows Phone和Windows 8中继续发扬光大。另一方面,Sliverlight和WPF一脉相承,抛开执行效率的问题,这本身就是对DirectUI的一次扩展,而DirectUI又是微软最先在XP中使用的独门技术。所以说没有永恒的技术,只有永恒的思想。从Linux到Windows,从C到C++到Java再到C#莫不如此。

扯远了。上面说到.Net的版本问题,就决定了之前在Windows上写的API不能直接拿来用。经过对比,影响最大的部分在网络模块。Windows Phone中大部分的网络操作均只提供异步API,而不提供同步API。这样做的原因是可以达到更好的响应速度,不至于让UI卡死而达到更流畅的用户体验。其实之前在Windows版本上也可以直接用HttpRequest的异步操作,但是当时我的做法是在异步线程上执行HttpRequest的同步操作。效果相同,所以推荐直接用HttpRequest异步方法。

其实入手Windows Phone也快一年了,遇到很多应用最大的部分就是UI不够流畅,而且很明显可以看出来是在进行网络操作的时候整个UI都卡死了。微软的建议是,任何可能超过50毫秒的操作都使用异步执行,麻烦一点,但是带来的体验改善是相当巨大的。

Ok,说完基本的框架,再说说做音乐类应用最重要的AudioPlayerAgent核心。

AudioPlayerAgent和另外两个后台任务类AudioStreamingAgent、ScheduledTaskAgent都是继承自抽象类BackgroundAgent。BackgroundAgent像极了WinProc函数。所有继承自BackgroundAgent的对象都将托管给系统,由系统在某些特定的情况下(如按下下一首、每30分钟),由系统调用托管给系统对象的特定方法,最终执行方法内的操作。为了更好地了解这一过程,可以看下图的函数堆栈,这是我从App Hub里的崩溃记录里找到的(是的,通过App Hub发布的所有应用崩溃都会向微软发送错误的堆栈信息):

从上面可以看到,我们写的AudioPlayerAgent其实是被一个名为Microsoft.Phone.BackgroundAgentDispatcher.InvokationTread的方法调用的。微软通过这样的方法让应用程序有一定“后台执行”的能力,但是所有执行的操作又能掌控在系统手上,不至于让应用随意执行,不但不能随意执行,每次执行的时间也是有所限制的。例如上图中的OnUserAction方法,由CallOnUserAction调用后,如果在规定时间内没能执行BackgroundAgent.NotifyComplete方法,系统就会认定这次方法执行失败。而一旦BackgroundAgent.NotifyComplete被调用,本次操作就会全部结束,不管你还有没有正在执行的线程。这里就引出了一个很重要的问题,如果在BackgroundAgent中用HttpRequest网络请求操作使用异步,那么在网络响应结束之前,NotifyComplete被触发,整个BackgroundAgent所在的线程就会被挂起,而网络请求自然也不能成功。解决这个问题的时候我是采用一个简单的线程锁标记来解决的,在网络请求结束以前,设定一个标记将当前线程锁定,直接到网络操作结束再释放。

说到AudioPlayerAgent还有一个不得不说的地方,就是AudioPlayerAgent和主程序之前通信问题。由于在执行的时候AudioPlayerAgent和主程序不属于同一个进程,因此用独立存储的时候经常遇到主程序里的IsolatedStorageSettings更新了,但是AudioPlayerAgent里的IsolatedStorageSettings读取出来还是老的数据(IsolatedStorageFile应该没有这个问题)。而类似像豆瓣电台这样的应用,我需要在主程序和AudioPlayerAgent交换音乐属性(毕竟AudioTrack提供的那几个不够我用的)。在不使用IsolatedStorageFile的情况下,我把额外的交换信息写成了一个ExchangeClass,然后用json序列化放在AudioTrack.Tag中。如果条件许可的情况下,这里还是建议使用IsolatedStorageFile。

关于多语言支持,一开始是没有考虑到这层的,一旦使用了以后就会发现微软在设计框架时是多么精巧。Windows Phone里的多语言支持和XAML的数据绑定是相通的,直接设定一个关键字,然后针对这个关键字编写不同语言版本的资源文件,再然后编译一下就搞定了。唯一需要提的一点是应用在Applications里名称的全球化支持,需要一个标准的Win32 DLL作为资源文件(MSDN),然后根据不同的语言编码(16进制)写不同的资源文件,放在xap根目录中。Windows会根据当前系统语言自动寻找对应的资源文件然后显示,从这点也可以说明微软自己在开发Windows Phone的时候应该更主要用的是C++。对于官方的应用,Native C++是最有可能的选择了。

关于Silverlight Toolkit for Windows Phone,我只能说这是个好工具,但是使用的时候要适可而止。我一直不明白微软为什么不直接在系统里支持Toolkit里的功能,而要单独再创建一个开源项目来实现它。臃肿就不说了,效率也是Toolkit的一大问题。类似像TiltEffect.IsTiltEnabled的特性,如果不是特殊必要,应该考虑舍弃。因为Toolkit里的TileEffect的效率实在是太差了,如果页面上图片太多,启用TileEffect就会导致拖动页面的时候经常性的卡死,ListBox尤其如此,这也是为什么很多包括ListBox应用拖动列表时卡顿明显的原因。

在开发过程中大致遇到的问题就这些了。Windows Phone入门实在是太低太低了,下载安装个SDK,新建个项目随便拖拖就能出个应用,但是想真正做一个精品应用,UI美观大方,运行流畅,对开发者的要求却又过高。另外再吐槽下微软的产品,博大而又精深。

安装Windows SDK 7.1时提示发生致命错误

很早之前就尝试过安装Windows SDK 7.1,但是一直失败。今天下午为了编译Cosmos发来的一个Sample,又尝试了一次,又是显而易见的失败。心中多有不甘,所以上网搜索了一下解决方法。在MSDN Forms里有过类似的问题,但是回答多是安装包损坏,请重新尝试一次。

上个月底的时候重装系统,在没有安装Visual Studio 2010 SP1之前就先安装了SDK 7.1,可以完全成功,后来分析应该是SP1里更新了Complier,而且版本比SDK 7.1的高,所以在安装的时候出现了冲突。所以如果再出现发生致命错误的时候可以尝试不安装SDK里的Complier,另外再次鄙视一下微软的文档帮助系统。。。

COM入门笔记

从年初的时候断断续续开始看COM技术,之前只是知道COM技术广泛用在Windows平台上,包括DX、Visual Studio、Office等都是基于COM的项目,今天小试牛刀,边看《COM技术内幕》边Code,实现了一个简单的动态链接COM应用。
下午的Demo是实现了第5章:动态链接的第一个Demo。然后向师傅咨询了一下DLL相关的一些知识点,比如dll的导入导出函数,DEF文件的作用以及__export描述符等等。简单地总结就是,在dll里自带了一个导出函数表以及导入函数表,lib文件中也有一张与之对应的导出函数表,但是没有函数的实现方法,一个dll的导出函数是这个dll实现函数的子集,def文件能够创建dll的导出函数表,加了__export修饰以后不需要创建def表也可以生成相应的导出函数表。
其实看了这么久的COM,对COM的整个框架还不是很熟悉,但是对它的大致功能还是了解了。COM能够实现的不需要知道“函数名”,不需要知道“入口地址”,只需要把相关dll加载进来,然后给它一个接口ID,它就能返回函数的指针,然后就尽情地使用。这对于大型项目来说,确实相当方便了很多。唔,说不定可以在公司的分布式系统上考虑用这种思想。

hello world

好吧,按照惯例,任何一个新地儿的第一次都是以hello world开始的。

so…. I started.

Windows Phone 7中带题头的TextBox和PasswordBox

之前在做豆瓣电台的时候想实现一个登录界面,起初是把TextBlock和TextBox放在一起(效果如下),但是这样实现出来的效果既浪费空间,又不美观。如下图:

因为之前写WPF对自定义控件有一些基础,所以就对TextBox和Password的Template做了些手脚,让题头融合到控件里。这样既美观又节省空间,同时还可以保证不会推动系统控件风格和功能,如下图:

实现上面的效果非常简单,只需要在Blend中创建一个新的TextBox,然后右键-Edit Template-Edit a Copy。在弹出的对话框中的Name设定一个名字,如“LabelTextBox”,然后在Define in中选择Application以在整个程序范围内都可以使用它。

操作完这些以后Blend会自动在App.xaml里创建一个新的Style资源,我们在里面搜索一下ContentControl,这就是TextBox中显示文字的元素了。剩下的就是对这个元素进行改造,如调整位置,添加新元素等等。下面是我写的TextBox和PasswordBox的资源:

<ControlTemplate x:Key="PhoneDisabledTextBoxTemplate" TargetType="TextBox">
<ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/>
</ControlTemplate>
<Style x:Key="LabelTextBox" TargetType="TextBox">
    <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
    <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
    <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource PhoneTextBoxBrush}"/>
    <Setter Property="SelectionBackground" Value="{StaticResource PhoneAccentBrush}"/>
    <Setter Property="SelectionForeground" Value="{StaticResource PhoneTextBoxSelectionForegroundBrush}"/>
    <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
    <Setter Property="Padding" Value="2"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid Background="Transparent">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver"/>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledOrReadonlyBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="ReadOnly">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledOrReadonlyBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="DisabledOrReadonlyBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="DisabledOrReadonlyBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DisabledOrReadonlyContent">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxReadOnlyBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBackgroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBorderBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Margin="10, 0" Text="{TemplateBinding Tag}" VerticalAlignment="Center" />
                            <ContentControl Grid.Column="1" x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="1,2,1,2" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch" VerticalAlignment="Center"/>
                        </Grid>
                    </Border>
                    <Border x:Name="DisabledOrReadonlyBorder" BorderBrush="{StaticResource PhoneDisabledBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Margin="{StaticResource PhoneTouchTargetOverhang}" Visibility="Collapsed">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Margin="10, 0" Text="{TemplateBinding Tag}" VerticalAlignment="Center" Foreground="{StaticResource PhoneDisabledBrush}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" />
                            <TextBox Grid.Column="1" x:Name="DisabledOrReadonlyContent" Background="Transparent" Foreground="{StaticResource PhoneDisabledBrush}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" IsReadOnly="True" SelectionForeground="{TemplateBinding SelectionForeground}" SelectionBackground="{TemplateBinding SelectionBackground}" TextAlignment="{TemplateBinding TextAlignment}" TextWrapping="{TemplateBinding TextWrapping}" Text="{TemplateBinding Text}" Template="{StaticResource PhoneDisabledTextBoxTemplate}"/>
                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<ControlTemplate x:Key="PhoneDisabledPasswordBoxTemplate" TargetType="PasswordBox">
    <Border x:Name="ContentElement" BorderThickness="0" Margin="{StaticResource PhonePasswordBoxInnerMargin}" Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
<Style x:Key="LabelPassword" TargetType="PasswordBox">
    <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
    <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
    <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource PhoneTextBoxBrush}"/>
    <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
    <Setter Property="SelectionBackground" Value="{StaticResource PhoneAccentBrush}"/>
    <Setter Property="SelectionForeground" Value="{StaticResource PhoneContrastBackgroundBrush}"/>
    <Setter Property="Padding" Value="2"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="PasswordBox">
                <Grid Background="Transparent">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver"/>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBackgroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="EnabledBorder">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBorderBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Margin="10, 0" Text="{TemplateBinding Tag}" VerticalAlignment="Center" />
                            <Border Grid.Column="1" x:Name="ContentElement" BorderThickness="0" Margin="{StaticResource PhonePasswordBoxInnerMargin}" Padding="{TemplateBinding Padding}"/>
                        </Grid>
                    </Border>
                    <Border x:Name="DisabledBorder" BorderBrush="{StaticResource PhoneDisabledBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Margin="{StaticResource PhoneTouchTargetOverhang}" Visibility="Collapsed">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Margin="10, 0" Text="{TemplateBinding Tag}" VerticalAlignment="Center" Foreground="{StaticResource PhoneDisabledBrush}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" />
                            <PasswordBox Grid.Column="1" x:Name="DisabledContent" Background="Transparent" Foreground="{StaticResource PhoneDisabledBrush}" Password="{TemplateBinding Password}" PasswordChar="{TemplateBinding PasswordChar}" Template="{StaticResource PhoneDisabledPasswordBoxTemplate}"/>
                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Ok,样式设定完毕,要使用的时候就非常简单了,在项目里的任何一个TextBox或者PasswordBox中像这样使用它就可以了:

<TextBox Tag="帐号:" Style="{StaticResource LabelTextBox}" Name="Username" />
<PasswordBox Tag="密码:" Style="{StaticResource LabelPassword}" Name="Password" />

大功告成!其中Tag是控件前面题头里的文字,而TextBox和PasswordBox的功能不会受到任何影响~

NP完全问题的一个特例

昨天晚上一个技术性群里某同学问题了一个问题,原问题比较复杂,所以我做了抽象:

给定一个100以下的自然数集Q,是否存在某个非空子集T,使得子集中数字之和等于100,要求集合T中元素最少。

显然当Q集中数字超过100的情况下,无论怎样取都存在只有两个元素的子集,子集之和能够等于100。

但是当Q集中数字只有20个、甚至10个的情况,就需要对方案进行搜索。

昨天晚上遇到这个问题的时候第一想法是枚举,后一秒马上PIA了自己一耳光,too young too naive。这种问题和动态规划中的入门级问题最大子串和有一定的相似性,大问题的最优解都需要依赖小问题已经求出的最优解,而这种问题求解的核心在于推 导出递归公式。

@!#¥%#!@%

中间的废话不说,直接推出公式为Best(k)=min{Best(i)+Best(k-i), i∈[0, k)}。Best(k)函数的定义为Q集中数字之和为k的方案中需要数字最少的方案数。。。@.@很饶舌,木办法,语文没学好。

有了递归公式一切就好办了,之后就是代码,昨天用C#尝试写出了方案的解。昨天的方案中考虑的是Q集中存在重复数字,当然这在数学集合的定义里是不允许的。

代码应该还有很多优化的地方,子循环中其实很多地方可以考虑用break和continue跳过,虽然这样,但是时间复杂度依然是o(n^2),n在这里等于100。不知道各位有没有更优的一些方法,可以把复杂度降低到o(n) ^.^

static void Main(string[] args)
{
    List<byte> N = new List<byte>();
    Random rnd = new Random();
    byte[] minSum = new byte[101];
    string[] result = new string[101];
    byte[] Times = new byte[100];

    for (int i = 0; i < 100; ++i)
    {
        Times[i] = 0;
    }

    for (int i = 0; i < 20; i++)
    {
        byte t = (byte)rnd.Next(100);
        N.Add(t);
        ++Times[t];
    }

    for (byte i = 0; i <= 100; ++i)
    {
        if (N.Contains(i))
        {
            minSum[i] = 1;
            result[i] = i.ToString();
        }
        else
        {
            bool IsFirst = true;

            int minIndex1 = -1;
            int minIndex2 = -1;

            for (byte j = 0; j < i; ++j)
            {
                byte k = (byte)(i – j);
                if (minSum[j] != 0 && minSum[k] != 0)
                {
                    if (IsFirst)
                    {
                        minIndex1 = j;
                        minIndex2 = k;
                        IsFirst = false;
                    }
                    else if (minSum[j] + minSum[j] < minSum[minIndex1] + minSum[minIndex2])
                    {
                        minIndex1 = j;
                        minIndex2 = k;
                    }
                }
            }

            if (minIndex1 == -1)
            {
                minSum[i] = 0;
                result[i] = string.Empty;
            }
            else
            {
                string[] Member1 = result[minIndex1].Split(‘+’);
                string[] Member2 = result[minIndex2].Split(‘+’);

                int[] UsedTimes = new int[100];

                for (byte x = 0; x < 100; ++x)
                {
                    UsedTimes[x] = 0;
                }

                bool IsContinue = false;

                foreach (string t in Member1)
                {
                    byte y = byte.Parse(t);
                    ++UsedTimes[y];
                    if (UsedTimes[y] > Times[y])
                        IsContinue = true;
                }

                if (IsContinue)
                    continue;

                foreach (string t in Member2)
                {
                    byte y = byte.Parse(t);
                    ++UsedTimes[y];
                    if (UsedTimes[y] > Times[y])
                        IsContinue = true;
                }

                if (IsContinue)
                    continue;

                minSum[i] = (byte)(minSum[minIndex1] + minSum[minIndex2]);
                result[i] = result[minIndex1] + "+" + result[minIndex2];
            }

        }
    }

    for (byte i = 0; i < N.Count; ++i)
    {
        Console.Write("{0} ", N[i].ToString());
        if (i % 10 == 9)
            Console.WriteLine();
    }

    Console.WriteLine();
    Console.Write(result[100].Trim() == string.Empty ? "No Result" : result[100]);

    int wait = Console.Read();

}

C-语言词法分析器

囧,编译原理大作业,正好最近要写LRC歌词解析,貌似要用到LEX语法分析,顺便复习一下。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 255    // 字符串缓冲;亦是关键字、标识符、注释的最大有效长度

bool IsNumber(char CurrentChar);
bool IsUpperLetter(char CurrentChar);
bool IsLowerLetter(char CurrentChar);
bool IsVerifiedSymbol(char CurrentChar);
char CheckNonsignificantChar(FILE* in, char CurrentChar);
bool IsKeywords(char Word[]);
bool IsSingleSymbol(char CurrentChar);
void IsDoubleSymbol(FILE* in, FILE* out, char CurrentChar, char FirstChar, char SecondChar);

int main()
{
    FILE *in, *out;
    char arr[MAX];
    char CurrentChar=' ';
    int i=0;

    if(!(in=fopen("input.txt","r")))
    {
        printf("Cannot Open Input File(input.txt).\n");
        system("pause");
        return 0;
    }
    if(!(out=fopen("output.txt","w")))
    {
        printf("Cannot Open Output File(output.txt).\n");
        system("pause");
        return 0;
    }

    while(CurrentChar!=-1)
    {
        CurrentChar=fgetc(in);
        if(IsNumber(CurrentChar)||IsUpperLetter(CurrentChar)||IsLowerLetter(CurrentChar)||IsVerifiedSymbol(CurrentChar))
        {
            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            i=0;
            if(IsNumber(CurrentChar))
            {
                arr[i++]=CurrentChar;
                CurrentChar=fgetc(in);
                while(IsNumber(CurrentChar))
                {
                    arr[i++]=CurrentChar;
                    CurrentChar=fgetc(in);
                }
                fseek(in,-1,SEEK_CUR);
                arr[i++]='\0';
                fprintf(out,"%s%s\n","Number    : ",arr);
                continue;
            }

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            i=0;
            if(IsLowerLetter(CurrentChar)||IsUpperLetter(CurrentChar))
            {
                while(IsLowerLetter(CurrentChar)||IsUpperLetter(CurrentChar)||IsNumber(CurrentChar))
                {
                    arr[i++]=CurrentChar;
                    CurrentChar=fgetc(in);
                }
                fseek(in,-1,SEEK_CUR);
                arr[i++]='\0';
                if(IsKeywords(arr))
                    fprintf(out,"%s%s\n","Keyword   : ",arr);
                else
                    fprintf(out,"%s%s\n","Identifier: ",arr);
                continue;
            }

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            if(IsSingleSymbol(CurrentChar))
            {
                fprintf(out,"%s%c\n","Symbol    : ",CurrentChar);
            }

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            IsDoubleSymbol(in, out, CurrentChar, '=', '=');

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            IsDoubleSymbol(in, out, CurrentChar, '>', '=');

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            IsDoubleSymbol(in, out, CurrentChar, '<', '=');

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            IsDoubleSymbol(in, out, CurrentChar, '!', '=');

            CurrentChar=CheckNonsignificantChar(in, CurrentChar);
            i=0;
            if(CurrentChar=='/')
            {
                arr[i++]=CurrentChar;
                CurrentChar=fgetc(in);
                if(CurrentChar=='*')
                {
                    do
                    {
                        arr[i++]=CurrentChar;
                        CurrentChar=fgetc(in);
                    } while (!(CurrentChar=='*'&&fgetc(in)=='/'));
                    arr[i++]='*';
                    arr[i++]='/';
                    arr[i++]='\0';
                    fprintf(out,"%s%s\n","Annotation: ",arr);
                }
                else
                {
                    fseek(in,-1,SEEK_CUR);
                    fprintf(out,"%s%c\n","Symbol    : ",'/');
                }
            }
        }
        else
        {
            fprintf(out,"%s%c\n","error     : ",CurrentChar);
            CurrentChar=fgetc(in);
        }
    }
    fclose(in);
    fclose(out);
    return 0;
}

bool IsNumber(char CurrentChar)
{
    if(CurrentChar>='0'&&CurrentChar<='9')
        return true;
    else
        return false;
}
bool IsUpperLetter(char CurrentChar)
{
    if(CurrentChar>='A'&&CurrentChar<='Z')
        return true;
    else
        return false;
}
bool IsLowerLetter(char CurrentChar)
{
    if(CurrentChar>='a'&&CurrentChar<='z')
        return true;
    else
        return false;
}
bool IsVerifiedSymbol(char CurrentChar)
{
    if(CurrentChar=='+'||
        CurrentChar=='-'||
        CurrentChar=='*'||
        CurrentChar=='/'||
        CurrentChar=='<'||
        CurrentChar=='>'||
        CurrentChar=='='||
        CurrentChar=='!'||
        CurrentChar==';'||
        CurrentChar==','||
        CurrentChar=='('||
        CurrentChar==')'||
        CurrentChar=='['||
        CurrentChar==']'||
        CurrentChar=='{'||
        CurrentChar=='}'||
        CurrentChar==' '||
        CurrentChar=='\t'||
        CurrentChar=='\n')
        return true;
    else
        return false;
}
char CheckNonsignificantChar(FILE* in, char CurrentChar)
{
    while(CurrentChar==' '||CurrentChar=='\t'||CurrentChar=='\n')
    {
        CurrentChar=fgetc(in);
    }
    return CurrentChar;
}
bool IsKeywords(char Word[])
{
    if(!strcmp(Word,"else")||
        !strcmp(Word,"if")||
        !strcmp(Word,"int")||
        !strcmp(Word,"return")||
        !strcmp(Word,"void")||
        !strcmp(Word,"while"))
        return true;
    else
        return false;
}
bool IsSingleSymbol(char CurrentChar)
{
    if(CurrentChar=='+'||
        CurrentChar=='-'||
        CurrentChar=='*'||
        CurrentChar==';'||
        CurrentChar==','||
        CurrentChar=='('||
        CurrentChar==')'||
        CurrentChar=='['||
        CurrentChar==']'||
        CurrentChar=='{'||
        CurrentChar=='}')
        return true;
    else
        return false;
}
void IsDoubleSymbol(FILE* in, FILE* out, char CurrentChar, char FirstChar, char SecondChar)
{
    if(CurrentChar==FirstChar)
    {
        if(fgetc(in)==SecondChar)
        {
            fprintf(out,"%s%c%c\n","Symbol    : ",FirstChar,SecondChar);
        }
        else
        {
            fseek(in,-1,SEEK_CUR);
            if(FirstChar=='!')
            {
                fprintf(out,"%s%c\n","ErrorChar : ",CurrentChar);
            }
            else
            {
                fprintf(out,"%s%c\n","Symbol    : ",FirstChar);
            }
        }
    }
}

语音控制程序操作

昨天晚上睡觉前在床上突然想到的,给豆瓣电台加一个语音控制的功能,这样没事躺床上就可以动动嘴巴来操作程序了,电脑放在床下也没有关系,安逸哇!!

今天起床就找了些资料,Windows平台上的必须就是Microsoft Speech Library,这是一个COM组件,从微软的网站可以下载到SDK。不过从.Net 3.0开始在.Net Framework里也加了相应的映射:System.Speech,不过一般项目是没有添加这个引用的,自己手动加一下。

下面是一个简单的Sample,演示了如何向语音识别对象注册语音命令。

using System.Speech.Recognition;

namespace Douban
{
    public partial class MainWindow : Window
    {
        SpeechRecognizer speechRecognizer;
        public MainWindow()
        {
            InitializeComponent();
            // if (Properties.Settings.Default.EnableSpeech)
                speechRecognizer = new SpeechRecognizer();
        }

        private void window_Loaded(object sender, RoutedEventArgs e)
        {
            // if(Properties.Settings.Default.EnableSpeech)
                VoiceCommand();
        }

        private void VoiceCommand()
        {
            RegisterVoiceCommand(VoiceCommand_Close, "退出");
            RegisterVoiceCommand(VoiceCommand_Next, "下一首");
            RegisterVoiceCommand(VoiceCommand_Minimize, "最小化");
            RegisterVoiceCommand(VoiceCommand_Restore, "恢复");

            RegisterVoiceCommand(VoiceCommand_Pause, "暂停");
            RegisterVoiceCommand(VoiceCommand_Resume, "播放");
        }

        private void VoiceCommand_Resume(object sender, EventArgs e)
        {
            if (mediaPlayer.MediaState != MediaState.Play)
            {
                if (mediaPlayer.MediaUri != null)
                {
                    mediaPlayer.Play();
                    pnlPlayer.SetPlayerStatus(true);
                }
            }
        }

        private void VoiceCommand_Pause(object sender, EventArgs e)
        {
            if (mediaPlayer.MediaState == MediaState.Play)
            {
                mediaPlayer.Pause();
                pnlPlayer.SetPlayerStatus(false);
            }
        }

        private void VoiceCommand_Close(object sender, EventArgs e)
        {
            this.Close();
        }

        private void VoiceCommand_Next(object sender, EventArgs e)
        {

        }

        private void VoiceCommand_Minimize(object sender, EventArgs e)
        {
            this.WindowState = WindowState.Minimized;
        }

        private void VoiceCommand_Restore(object sender, EventArgs e)
        {
            this.WindowState = WindowState.Normal;
        }

        private void RegisterVoiceCommand(EventHandler Callback, params string[] Args)
        {
            Choices choices = new Choices();
            foreach (string arg in Args)
            {
                choices.Add(arg);
            }
            Grammar grammar = new Grammar(new GrammarBuilder(choices));

            grammar.SpeechRecognized += delegate(object sender, SpeechRecognizedEventArgs e)
            {
                EventHandler eventHandler = new EventHandler(Callback);
                Dispatcher.BeginInvoke(eventHandler, sender, e);
            };

            speechRecognizer.LoadGrammarAsync(grammar);
        }

豆瓣电台API 2.0 笔记

最近无聊自己用WPF写了一个豆瓣电台,之前的版本API一直有些问题,昨天晚上无聊把API这部分重写了,解决了以前的一些Bug,记个笔记,怕自己以后忘记了。

把Network文件夹里的东西无视掉基本上可以跨平台。

1、验证身份

验证身份需要向http://www.douban.com/j/app/login这个地址Post数据,返回一个json格式的结果。

Post的数据参数如下:

email=豆瓣帐号

password=密码

app_name=”radio_desktop_win”

version=”100″

返回的json数据对象User结构:

返回r为false表示登录失败,此时err表示一段说明登录失败的解释,其余成员为null;若登录成功,r返回true,其余成员亦有意义。

2、获取歌曲列表

获取歌曲列表时有两种情况:长报告和短报告。长报告适用于需要添加音乐池队列,短报告适用于向服务器返回本地操作。报告的正确发送与否直接影响服务器对该用户的歌曲匹配程度。

发送报告需要向http://www.douban.com/j/app/radio/people这个地址Get数据,返回json格式的结果。

Get数据参数如下:

app_name=”radio_desktop_win”

version=”100″

user_id=user_id

expire=expire

token=token

sid=当前音乐的sid属性

h=最近播放的音乐

channel=频道id

type=报告类型

说明:

1、红色标记的三个参数为非必要参数,适用于未登录的情况。user_id、expire、token三个参数分别来自于验证身份里User结构的三个成员。

2、最近播放的音乐一般为20个记录,格式为“|Song.sid:报告类型”,如“|1386894:s|444482:p|460268:s|48180:s|1027376:s|188257:s”。

3、频道id为枚举数字,目前已知的频道与id对应如下:

4、报告类型为一个字符,说明如下:

b: bye(?),不再播放,短报告;

e: end,当前歌曲播放完毕,但是歌曲队列中还有歌曲,短报告;

n: new,没有歌曲播放,歌曲队列也没有任何歌曲,需要返回新播放列表,长报告;

p: playing(?),歌曲正在播放,但是歌曲队列中已经没有歌曲,需要返回新播放列表,长报告;

s: skip,歌曲正在播放,歌曲队列中还有歌曲,适用于用户点击“下一首”,短报告;

r: rate(?),歌曲正在播放,标记喜欢当前歌曲,短报告;

u: unrate(?),歌曲正在播放,标记取消喜欢当前歌曲,短报告;

尤其注意在发出长报告以后更新歌曲列表,发送短报告后亦会返回歌曲列表,但是认为没有意义。

返回的json数据对象中song[]成员结构(注:返回的结构song[]只是一个成员,并非返回的就是song[]):

尤其注意subtype这个成员。默认情况subtype为”",但是最近发现一些广告音频的subtype属性为”T”,不除非还有其它可能性。