一直在使用dtapp大神写的STK在工作时间看股票,简单大方,非常方便。
由于单位使用的是日文的OS,stk的menu显示乱码,最近行情大好,导致使用频率增高,乱码的menu也无法忍受了。
所以下决心自己做一个,这个东东用了大约3天的闲暇时间初步功能完成,现一步一步的说明一下。
先上图
先简单说说实现的功能吧
1.定时刷新股票行情(HttpWebRequest取得新浪行情数据)
2.自定义添加股票,保存到ini文件(ini读写)
3.最小化时隐藏到任务栏,不在taskbar表示,双击任务栏图标启动程序(wpf使用NotifyIcon)
4.热键表示/隐藏(hotkey)
5.保存上次关闭时的位置,存到ini文件中
----------------------------------------------------------------------------
接下来分别说明一下:
1.定时刷新股票行情(HttpWebRequest取得新浪行情数据)
1 Dim request As WebRequest = HttpWebRequest.Create(String.Format(dataURL, _stockCodes)) 2 Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse) 3 Dim reader As New StreamReader(response.GetResponseStream, Encoding.GetEncoding(response.CharacterSet)) 4 Dim html = reader.ReadToEnd()
说明:dataURL是去行情的网址,这里使用的是http://hq.sinajs.cn/list={0}。定时器就是使用的timer代码就不贴了,很简单。
2.自定义添加股票,保存到ini文件(ini读写)
1 Public Class INIHelper 2 3 Private inipath As String = Environment.CurrentDirectory & "DtStock.ini" 4 5 <DllImport("kernel32")> _ 6 Private Shared Function WritePrivateProfileString(section As String, key As String, val As String, filePath As String) As Long 7 End Function 8 9 <DllImport("kernel32")> _ 10 Private Shared Function GetPrivateProfileString(section As String, key As String, def As String, retVal As StringBuilder, size As Integer, filePath As String) As Integer 11 End Function 12 13 Public Sub IniWriteValue(Section As String, Key As String, Value As String) 14 WritePrivateProfileString(Section, Key, Value, Me.inipath) 15 End Sub 16 17 Public Function IniReadValue(Section As String, Key As String) As String 18 Dim temp As New StringBuilder(500) 19 Dim i As Integer = GetPrivateProfileString(Section, Key, "", temp, 500, Me.inipath) 20 Return temp.ToString() 21 End Function 22 23 Public Function ExistINIFile() As Boolean 24 Return File.Exists(inipath) 25 End Function 26 End Class
说明:这里是把ini读写专门做了一个helper,方便使用,调用的时候如下
'读 iniHelper.IniReadValue("Stock", "Code") '写 iniHelper.IniWriteValue("Stock", "Code", String.Empty)
3.最小化时隐藏到任务栏,不在taskbar表示(wpf使用NotifyIcon)
1 Private Sub ShowNotifyIcon() 2 _notifyIcon = New NotifyIcon() 3 _notifyIcon.Text = "DeskTopStock v1.0" 4 _notifyIcon.Icon = ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath) 5 _notifyIcon.Visible = True 6 7 Dim openMenu As New MenuItem("Add or Setting", AddressOf AddStock) 8 Dim aboutMenu As New MenuItem("About us", AddressOf About) 9 Dim exitMenu As New MenuItem("Exit me", AddressOf CloseMe) 10 11 _notifyIcon.ContextMenu = New ContextMenu({openMenu, aboutMenu, exitMenu}) 12 13 AddHandler _notifyIcon.MouseDoubleClick, AddressOf OnNotifyIconDoubleClick 14 End Sub
说明:引入System.Windows.Forms,绑定上需要用的event就可以了
4.热键表示/隐藏(hotkey)
Imports System.Windows.Forms Imports System.Windows.Interop ''' <summary> ''' 直接构造类实例即可注册 ''' 自动完成注销 ''' 注意注册时会抛出异常 ''' </summary> Class HotKey '注册系统热键类 '热键会随着程序结束自动解除,不会写入注册表 #Region "Member" Private KeyId As Integer '热键编号 Private Handle As IntPtr '窗体句柄 Private window As Window '热键所在窗体 Private Controlkey As UInteger '热键控制键 Private Key As UInteger '热键主键 Public Delegate Sub OnHotkeyEventHandeler() '热键事件委托 Public Event OnHotKey As OnHotkeyEventHandeler '热键事件 Shared KeyPair As New Hashtable() '热键哈希表 Private Const WM_HOTKEY As Integer = &H312 ' 热键消息编号 Public Enum KeyFlags '控制键编码 MOD_ALT = &H1 MOD_CONTROL = &H2 MOD_SHIFT = &H4 MOD_WIN = &H8 End Enum #End Region ''' <summary> ''' 构造函数 ''' </summary> ''' <param name="win">注册窗体</param> ''' <param name="control">控制键</param> ''' <param name="key__1">主键</param> Public Sub New(win As Window, control As HotKey.KeyFlags, key__1 As Keys) '构造函数,注册热键 Handle = New WindowInteropHelper(win).Handle window = win Controlkey = CUInt(control) Key = CUInt(key__1) KeyId = CInt(Controlkey) + CInt(Key) * 10 If HotKey.KeyPair.ContainsKey(KeyId) Then Throw New Exception("热键已经被注册!") End If '注册热键 If False = HotKey.RegisterHotKey(Handle, KeyId, Controlkey, Key) Then Throw New Exception("热键注册失败!") End If If HotKey.KeyPair.Count = 0 Then '消息挂钩只能连接一次!! If False = InstallHotKeyHook(Me) Then Throw New Exception("消息挂钩连接失败!") End If End If '添加这个热键索引 HotKey.KeyPair.Add(KeyId, Me) End Sub #Region "core" <System.Runtime.InteropServices.DllImport("user32")> _ Private Shared Function RegisterHotKey(hWnd As IntPtr, id As Integer, controlKey As UInteger, virtualKey As UInteger) As Boolean End Function <System.Runtime.InteropServices.DllImport("user32")> _ Private Shared Function UnregisterHotKey(hWnd As IntPtr, id As Integer) As Boolean End Function Private Shared Function InstallHotKeyHook(hk As HotKey) As Boolean '安装热键处理挂钩 If hk.window Is Nothing OrElse hk.Handle = IntPtr.Zero Then Return False End If '获得消息源 Dim source As System.Windows.Interop.HwndSource = System.Windows.Interop.HwndSource.FromHwnd(hk.Handle) If source Is Nothing Then Return False End If '挂接事件 source.AddHook(AddressOf HotKey.HotKeyHook) Return True End Function Private Shared _status As Boolean = False Private Shared Function HotKeyHook(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr '热键处理过程 If msg = WM_HOTKEY Then Dim hk As HotKey = DirectCast(HotKey.KeyPair(CInt(wParam)), HotKey) _status = hk.window.IsVisible If _status Then hk.window.Hide() _status = False Else hk.window.Show() _status = True End If End If Return IntPtr.Zero End Function Protected Overrides Sub Finalize() Try '析构函数,解除热键 HotKey.UnregisterHotKey(Handle, KeyId) Finally MyBase.Finalize() End Try End Sub #End Region End Class
说明:这个是网上找到的一个类,作者写的很好,直接使用了(忘记作者的原贴地址了,抱歉!!)
需要在Loaded之后调用,直接初期化一下传入热键就OK了,不写入注册表,程序关掉即释放。
5.保存上次关闭时的位置,存到ini文件中
Private Sub MainWindow_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.Closing _iniHelper.IniWriteValue("WinLocation", "Left", CStr(Me.Left)) _iniHelper.IniWriteValue("WinLocation", "Top", CStr(Me.Top)) End Sub Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded If String.IsNullOrEmpty(_iniHelper.IniReadValue("WinLocation", "Left")) Then Me.WindowStartupLocation = Windows.WindowStartupLocation.CenterScreen Exit Sub End If Me.Left = Double.Parse(_iniHelper.IniReadValue("WinLocation", "Left")) Me.Top = Double.Parse(_iniHelper.IniReadValue("WinLocation", "Top")) Dim HotKey As New HotKey(Me, HotKey.KeyFlags.MOD_WIN, Keys.A) End Sub
说明:窗口关闭的时候把位置保存起来,程序启动的时候再读出来,第一次启动程序的时候设定位置为屏幕中央。
最后把XAML贴出来
1 <Window x:Class="MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="DeskTopStock" SizeToContent="WidthAndHeight" 5 xmlns:my="clr-namespace:DeskTopStock" ResizeMode="CanMinimize" WindowStyle="None" 6 Opacity="0.8" AllowsTransparency ="True" ShowInTaskbar="False" Topmost="True" 7 FontSize="11.5" FontFamily="Microsoft YaHei" MouseMove="TitleBar_MouseMove"> 8 9 <Window.Resources> 10 <my:BackgroundConverter x:Key="myConverter"/> 11 12 <ToolTip x:Key="InforTip"> 13 <StackPanel Orientation="Vertical"> 14 <StackPanel Orientation="Horizontal"> 15 <TextBlock Text="{Binding Name}" /> 16 <TextBlock Text=" " /> 17 <TextBlock Text="{Binding Code}"/> 18 </StackPanel> 19 <StackPanel Orientation="Horizontal"> 20 <TextBlock Text="昨收盘:" /> 21 <TextBlock Text="{Binding YesterdayClose}"/> 22 </StackPanel> 23 <StackPanel Orientation="Horizontal"> 24 <TextBlock Text="今开盘:" /> 25 <TextBlock Text="{Binding TodayOpen}"/> 26 </StackPanel> 27 <StackPanel Orientation="Horizontal"> 28 <TextBlock Text="最 高:" /> 29 <TextBlock Text="{Binding High}"/> 30 </StackPanel> 31 <StackPanel Orientation="Horizontal"> 32 <TextBlock Text="最 低:" /> 33 <TextBlock Text="{Binding Low}"/> 34 </StackPanel> 35 36 </StackPanel> 37 </ToolTip> 38 39 <Style x:Key="myItemStyle" TargetType="{x:Type ListViewItem}"> 40 <Setter Property="Foreground"> 41 <Setter.Value> 42 <Binding RelativeSource="{RelativeSource Self}" Converter="{StaticResource myConverter}" ConverterParameter="{RelativeSource Self}" /> 43 </Setter.Value> 44 </Setter> 45 <Setter Property="ToolTip" Value="{Binding Source={StaticResource InforTip}}" /> 46 </Style> 47 48 </Window.Resources> 49 <Grid> 50 <Grid.RowDefinitions> 51 <RowDefinition Height="22" /> 52 <RowDefinition Height="*" /> 53 <RowDefinition Height="20" /> 54 </Grid.RowDefinitions> 55 <DockPanel Background="AntiqueWhite" > 56 <Button Name="C" ToolTip="关闭" Width="30" Height="20" Content="X" /> 57 <Button Width="30" ToolTip="隐藏" Height="20" Content="—" Name="Button1" /> 58 <Button Name="Add" ToolTip="添加股票" Width="30" Height="20" Content="十" Margin="5,0,0,0" Command="{Binding AddCommand}" HorizontalAlignment="Left" 59 CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window, AncestorLevel=1}}" /> 60 </DockPanel> 61 62 <ListView HorizontalAlignment="Stretch" Name="ListView1" ItemsSource="{Binding StockCollection}" VerticalAlignment="Stretch" 63 ItemContainerStyle="{StaticResource myItemStyle}" Grid.Row="1" > 64 <ListView.View> 65 <GridView> 66 <GridViewColumn Width="60" Header="简称" DisplayMemberBinding="{Binding Name}" /> 67 <GridViewColumn Width="75" Header="最新价" DisplayMemberBinding="{Binding Current}"/> 68 <GridViewColumn Width="65" Header="涨跌额" DisplayMemberBinding="{Binding ZDF}"/> 69 <GridViewColumn Width="60" Header="涨跌幅" DisplayMemberBinding="{Binding ZDF2}"/> 70 </GridView> 71 </ListView.View> 72 </ListView> 73 <TextBlock Text="数据更新中,请稍候.." Visibility="{Binding IsUpdateVisilility}" HorizontalAlignment="Left" Grid.Row="2"/> 74 <TextBlock Grid.Row="2" HorizontalAlignment="Right" Text="{Binding DateString}"/> 75 </Grid> 76 </Window>
说明:1.BackgroundConverter是控制ListView的行颜色的,上涨的时候是红色,跌的时候绿色 2.InforTip是Tooltip的显示内容
如有想需要代码或者程序的同学给我地址即可。