Mercurial > silverbladetech
changeset 20:6109bc268b90
Latest
line wrap: on
line diff
--- a/MetroWpf/MetroWpf.sln Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf.sln Tue Mar 20 13:37:46 2012 +0000 @@ -9,6 +9,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetroWpf.Xaml", "MetroWpf.Xaml\MetroWpf.Xaml.csproj", "{A5D99423-4BAE-4FC0-A0CB-F7238EC2C402}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stocks.Service", "Stocks.Service\Stocks.Service.csproj", "{47F54122-5381-48D8-ACF7-72BBE0353511}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stocks.Common", "Stocks.Common\Stocks.Common.csproj", "{847365D2-E27B-44C3-8DF4-B749D9FA65D7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,6 +63,26 @@ {A5D99423-4BAE-4FC0-A0CB-F7238EC2C402}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {A5D99423-4BAE-4FC0-A0CB-F7238EC2C402}.Release|Mixed Platforms.Build.0 = Release|Any CPU {A5D99423-4BAE-4FC0-A0CB-F7238EC2C402}.Release|x86.ActiveCfg = Release|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Debug|x86.ActiveCfg = Debug|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Release|Any CPU.Build.0 = Release|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {47F54122-5381-48D8-ACF7-72BBE0353511}.Release|x86.ActiveCfg = Release|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Debug|x86.ActiveCfg = Debug|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Release|Any CPU.Build.0 = Release|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {847365D2-E27B-44C3-8DF4-B749D9FA65D7}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Converters/AbsoluteNumberConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace MetroWpf.Converters +{ + public class AbsoluteNumberConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return Math.Abs((decimal) value); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Converters/BoolToServiceRunningTextConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace MetroWpf.Converters +{ + public class BoolToServiceRunningTextConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return (bool) value ? "Service Running" : "Service Stopped"; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Converters/BoolToSubscribedTextConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace MetroWpf.Converters +{ + public class BoolToSubscribedTextConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return (bool) value ? "Subscribed" : "Unsubscribed"; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Converters/DateTimeToTimeConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,24 @@ +using System; +using System.Windows.Data; + +namespace MetroWpf.Converters +{ + public class DateTimeToTimeConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string dateTimeString = ((DateTime) value).ToString("HH:mm:ss.ffff"); + + return dateTimeString; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Converters/DeltaToIconConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,46 @@ +using System; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace MetroWpf.Converters +{ + public class DeltaToIconConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string uri; + BitmapImage image; + decimal delta; + string file = "UNK"; + + try + { + delta = (decimal)value; + { + if (delta > 0) + file = "UP"; + else if (delta < 0) + file = "DOWN"; + else + file = "LEVEL"; + } + } + finally + { + uri = string.Format("../Images/{0}.png", file); + image = new BitmapImage(new Uri(uri, UriKind.Relative)); + } + + return image; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- a/MetroWpf/MetroWpf/IoCConfiguration.cs Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/IoCConfiguration.cs Tue Mar 20 13:37:46 2012 +0000 @@ -5,34 +5,50 @@ using MetroWpf.Presentation.Settings; using MetroWpf.Presentation.Login; using MetroWpf.Presentation.About; +using MetroWpf.Presentation.Stocks; using MetroWpf.Presentation.UserProfile; using MetroWpf.Presentation.Menu; using MetroWpf.Presentation.Shell; +using Stocks.Common; +using Stocks.Service; namespace MetroWpf { - public class IoCConfiguration - { - private static void RegisterCoreServices() + public class IoCConfiguration { - SimpleIoc.Default.Register<IMessenger, Messenger>(); - SimpleIoc.Default.Register<IWpfApplication, WpfApplication>(); - SimpleIoc.Default.Register<MainWindowViewModel>(); - SimpleIoc.Default.Register<MenuViewModel>(); - SimpleIoc.Default.Register<LoginViewModel>(); - SimpleIoc.Default.Register<UserProfileViewModel>(); - SimpleIoc.Default.Register<SettingsViewModel>(); - SimpleIoc.Default.Register<AboutViewModel>(); + private static void RegisterCoreServices() + { + // view model services + + // STOCKS + SimpleIoc.Default.Register<Stocks.Common.IConfigurationService, Stocks.Common.ConfigurationService>(); + SimpleIoc.Default.Register<IWebClientShim, WebClientShim>(); + SimpleIoc.Default.Register<IStocksService, StocksService>(); + + // core services + SimpleIoc.Default.Register<IMessenger, Messenger>(); + SimpleIoc.Default.Register<IWpfApplication, WpfApplication>(); + + // view models + SimpleIoc.Default.Register<StocksViewModel>(); + + SimpleIoc.Default.Register<MainWindowViewModel>(); + SimpleIoc.Default.Register<MenuViewModel>(); + SimpleIoc.Default.Register<LoginViewModel>(); + SimpleIoc.Default.Register<UserProfileViewModel>(); + SimpleIoc.Default.Register<SettingsViewModel>(); + SimpleIoc.Default.Register<AboutViewModel>(); + + } + + public static void RegisterDesignTimeServices() + { + RegisterCoreServices(); + } + + public static void RegisterRuntimeServices() + { + RegisterCoreServices(); + } } - - public static void RegisterDesignTimeServices() - { - RegisterCoreServices(); - } - - public static void RegisterRuntimeServices() - { - RegisterCoreServices(); - } - } }
--- a/MetroWpf/MetroWpf/Locator.cs Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/Locator.cs Tue Mar 20 13:37:46 2012 +0000 @@ -3,6 +3,7 @@ using MetroWpf.Presentation.Shell; using MetroWpf.Presentation.Settings; using MetroWpf.Presentation.Login; +using MetroWpf.Presentation.Stocks; using MetroWpf.Presentation.UserProfile; using MetroWpf.Presentation.About; using MetroWpf.Presentation.Menu; @@ -21,6 +22,7 @@ else { // Create run time services and view models + IoCConfiguration.RegisterRuntimeServices(); } } @@ -53,5 +55,10 @@ { get { return SimpleIoc.Default.GetInstance<AboutViewModel>(); } } + + public StocksViewModel StocksViewModel + { + get { return SimpleIoc.Default.GetInstance<StocksViewModel>(); } + } } } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Messages/AuthenticatedUserMessage.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,9 @@ +using GalaSoft.MvvmLight.Messaging; + +namespace MetroWpf.Messages +{ + public class UserAuthenticatedMessage : MessageBase + { + public string UserId { get; set; } + } +}
--- a/MetroWpf/MetroWpf/MetroWpf.csproj Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/MetroWpf.csproj Tue Mar 20 13:37:46 2012 +0000 @@ -52,9 +52,15 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\Libs\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath> </Reference> + <Reference Include="Ninject"> + <HintPath>..\Libs\Ninject.2.2.1.4\lib\net40-Client\Ninject.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.ComponentModel.Composition" /> <Reference Include="System.Data" /> + <Reference Include="System.Reactive, Version=1.0.10621.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> + <HintPath>..\Libs\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll</HintPath> + </Reference> <Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <HintPath>..\packages\Caliburn.Micro.1.3.1\lib\net40\System.Windows.Interactivity.dll</HintPath> </Reference> @@ -79,7 +85,21 @@ <DependentUpon>App.xaml</DependentUpon> <SubType>Code</SubType> </Compile> + <Compile Include="Converters\AbsoluteNumberConverter.cs" /> + <Compile Include="Converters\BoolToServiceRunningTextConverter.cs" /> + <Compile Include="Converters\BoolToSubscribedTextConverter.cs" /> + <Compile Include="Converters\DateTimeToTimeConverter.cs" /> + <Compile Include="Converters\DeltaToIconConverter.cs" /> + <Compile Include="Messages\AuthenticatedUserMessage.cs" /> <Compile Include="Messages\NavigationMessage.cs" /> + <Compile Include="Presentation\Stocks\DisplayStockPrice.cs" /> + <Compile Include="Presentation\Stocks\StocksView.xaml.cs"> + <DependentUpon>StocksView.xaml</DependentUpon> + </Compile> + <Compile Include="Presentation\Stocks\StocksViewModel.cs" /> + <Compile Include="Presentation\Stocks\StockTest.xaml.cs"> + <DependentUpon>StockTest.xaml</DependentUpon> + </Compile> <Compile Include="Services\Interfaces\INavigationService.cs" /> <Compile Include="Services\NavigationService.cs" /> <Compile Include="Presentation\About\AboutView.xaml.cs"> @@ -166,6 +186,14 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Presentation\Stocks\StocksView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Page Include="Presentation\Stocks\StockTest.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Presentation\UserProfile\UserProfileView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -192,6 +220,26 @@ <Project>{A5D99423-4BAE-4FC0-A0CB-F7238EC2C402}</Project> <Name>MetroWpf.Xaml</Name> </ProjectReference> + <ProjectReference Include="..\Stocks.Common\Stocks.Common.csproj"> + <Project>{847365D2-E27B-44C3-8DF4-B749D9FA65D7}</Project> + <Name>Stocks.Common</Name> + </ProjectReference> + <ProjectReference Include="..\Stocks.Service\Stocks.Service.csproj"> + <Project>{47F54122-5381-48D8-ACF7-72BBE0353511}</Project> + <Name>Stocks.Service</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Resource Include="Presentation\Images\DOWN.png" /> + </ItemGroup> + <ItemGroup> + <Resource Include="Presentation\Images\LEVEL.png" /> + </ItemGroup> + <ItemGroup> + <Resource Include="Presentation\Images\UNK.png" /> + </ItemGroup> + <ItemGroup> + <Resource Include="Presentation\Images\UP.png" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
--- a/MetroWpf/MetroWpf/Presentation/Login/LoginViewModel.cs Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Login/LoginViewModel.cs Tue Mar 20 13:37:46 2012 +0000 @@ -1,153 +1,120 @@ -using System; -using System.Windows; -using System.ComponentModel; -using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; +using GalaSoft.MvvmLight.Messaging; +using MetroWpf.Messages; namespace MetroWpf.Presentation.Login { - /// <summary> - /// Login view view model class - /// </summary> - public sealed class LoginViewModel : - ViewModelBase - { - #region · Data Properties · - /// <summary> - /// Gets or sets whether the form is busy - /// </summary> - private bool isBusy; - public bool IsBusy - { - get { return isBusy; } - set - { - if (this.isBusy != value) - { - this.isBusy = value; - RaisePropertyChanged("IsBusy"); - //relay requery? - } - } - } - - /// <summary> - /// Gets or sets the user name + /// Login view view model class /// </summary> - private string userId; - public string UserId - { - get { return this.userId; } - set - { - if (this.userId != value) - { - this.userId = value; - RaisePropertyChanged("UserId"); - LoginCommand.RaiseCanExecuteChanged(); - } - } - } - - /// <summary> - /// Gets or sets the password - /// </summary> - private string password; - public string Password - { - get { return this.password; } - set - { - if (this.password != value) - { - this.password = value; - RaisePropertyChanged("Password"); - LoginCommand.RaiseCanExecuteChanged(); - } - } - } - - #endregion - - #region · Constructors · - - /// <summary> - /// Initializes a new instance of the <see cref="LoginViewModel"/> class - /// </summary> - public LoginViewModel() - : base() + public sealed class LoginViewModel : + ViewModelBase { - LoginCommand = new RelayCommand(LoginCommandExecute, CanLoginCommandExecute); - CloseCommand = new RelayCommand(CloseCommandExecute); - } - - #endregion - - #region · Command Actions · - - #region LoginCommand - - public RelayCommand LoginCommand { get; set; } - - private void LoginCommandExecute() - { - } + #region · Data Properties · - private bool CanLoginCommandExecute() - { - if (string.IsNullOrEmpty(UserId) || - string.IsNullOrEmpty(Password)) - return false; - - return true; - } + /// <summary> + /// Gets or sets whether the form is busy + /// </summary> + private bool isBusy; + public bool IsBusy + { + get { return isBusy; } + set + { + if (this.isBusy != value) + { + this.isBusy = value; + RaisePropertyChanged("IsBusy"); + //relay requery? + } + } + } - #endregion - - #region CloseCommand - public RelayCommand CloseCommand { get; set; } - - private void CloseCommandExecute() - { - } - #endregion - // should fields be disabled when busy? + /// <summary> + /// Gets or sets the user name + /// </summary> + private string userId; + public string UserId + { + get { return this.userId; } + set + { + if (this.userId != value) + { + this.userId = value; + RaisePropertyChanged("UserId"); + LoginCommand.RaiseCanExecuteChanged(); + } + } + } - //protected override bool CanInquiryData() - //{ - // return (!String.IsNullOrEmpty(this.UserId) && - // !String.IsNullOrEmpty(this.Password) && - // this.ViewMode != ViewModeType.Busy); - //} + /// <summary> + /// Gets or sets the password + /// </summary> + private string password; + public string Password + { + get { return this.password; } + set + { + if (this.password != value) + { + this.password = value; + RaisePropertyChanged("Password"); + LoginCommand.RaiseCanExecuteChanged(); + } + } + } - //protected override void OnInquiryAction(InquiryActionResult<UserLogin> result) - //{ - // result.Data = this.Entity; - // result.Result = InquiryActionResultType.DataFetched; - //} + #endregion + + #region · Constructors · + + /// <summary> + /// Initializes a new instance of the <see cref="LoginViewModel"/> class + /// </summary> + public LoginViewModel() + : base() + { + LoginCommand = new RelayCommand(LoginCommandExecute, CanLoginCommandExecute); + CloseCommand = new RelayCommand(CloseCommandExecute); + } + + #endregion - //protected override void OnInquiryActionComplete(InquiryActionResult<UserLogin> result) - //{ - // if (result.Result == InquiryActionResultType.DataFetched) - // { - // Channel<AuthenticationInfo>.Public.OnNext( - // new AuthenticationInfo - // { - // Action = AuthenticationAction.LoggedIn, - // UserId = this.UserId - // }, true); + #region · Command Actions · + + #region LoginCommand + + public RelayCommand LoginCommand { get; set; } + + private void LoginCommandExecute() + { + //successful login! + + Messenger.Default.Send(new UserAuthenticatedMessage() { UserId = userId }); + } - // ServiceLocator.GetService<IVirtualDesktopManager>().CloseDialog(); - // } - // else if (result.Result == InquiryActionResultType.DataNotFound) - // { - // this.NotificationMessage = "Username and password do not match."; + private bool CanLoginCommandExecute() + { + if (string.IsNullOrEmpty(UserId) || + string.IsNullOrEmpty(Password)) + return false; + + return true; + } + + #endregion - // this.ViewMode = ViewModeType.Default; - // } - //} + #region CloseCommand + public RelayCommand CloseCommand { get; set; } - #endregion - } -} + private void CloseCommandExecute() + { + } + #endregion + + #endregion + } +} \ No newline at end of file
--- a/MetroWpf/MetroWpf/Presentation/Menu/MenuView.xaml Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Menu/MenuView.xaml Tue Mar 20 13:37:46 2012 +0000 @@ -1,61 +1,62 @@ -<UserControl x:Class="MetroWpf.Presentation.Menu.MenuView" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:login="clr-namespace:MetroWpf.Presentation.Login" - xmlns:userprofile="clr-namespace:MetroWpf.Presentation.UserProfile" - xmlns:settings="clr-namespace:MetroWpf.Presentation.Settings" - xmlns:about="clr-namespace:MetroWpf.Presentation.About" - xmlns:converters="clr-namespace:MetroWpf.Xaml.Converters;assembly=MetroWpf.Xaml" - mc:Ignorable="d" - DataContext="{Binding MenuViewModel, Source={StaticResource Locator}}"> - <Grid> - <TabControl x:Name="tabHost" SelectedIndex="{Binding SelectedTabIndex, Converter={converters:EnumToIntConverter}}"> +<UserControl x:Class="MetroWpf.Presentation.Menu.MenuView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:about="clr-namespace:MetroWpf.Presentation.About" + xmlns:converters="clr-namespace:MetroWpf.Xaml.Converters;assembly=MetroWpf.Xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:login="clr-namespace:MetroWpf.Presentation.Login" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:settings="clr-namespace:MetroWpf.Presentation.Settings" + xmlns:userprofile="clr-namespace:MetroWpf.Presentation.UserProfile" + xmlns:Stocks="clr-namespace:MetroWpf.Presentation.Stocks" + DataContext="{Binding MenuViewModel, Source={StaticResource Locator}}" + mc:Ignorable="d"> + <Grid> + <TabControl x:Name="tabHost" SelectedIndex="{Binding SelectedTabIndex, Converter={converters:EnumToIntConverter}}"> - <TabItem Header="_Stocks" Visibility="{Binding ShowStocks, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - - </StackPanel> - </TabItem> + <TabItem Header="_Stocks" Visibility="{Binding ShowStocks, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10" HorizontalAlignment="Center"> + <Stocks:StockTest/> + </StackPanel> + </TabItem> - <TabItem Header="_Rates" Visibility="{Binding ShowFxRates, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> + <TabItem Header="_Rates" Visibility="{Binding ShowFxRates, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10"> - </StackPanel> - </TabItem> + </StackPanel> + </TabItem> - <!-- Start of hidden tabs--> - <TabItem Name="tabItemLogin" Visibility="{Binding ShowLogin, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - <login:LoginView/> - </StackPanel> - </TabItem> + <!-- Start of hidden tabs--> + <TabItem Name="tabItemLogin" Visibility="{Binding ShowLogin, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10" HorizontalAlignment="Center" VerticalAlignment="Center"> + <login:LoginView /> + </StackPanel> + </TabItem> - <TabItem Name="tabItemUserProfile" Visibility="{Binding ShowUserProfile, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - <userprofile:UserProfileView /> - </StackPanel> - </TabItem> + <TabItem Name="tabItemUserProfile" Visibility="{Binding ShowUserProfile, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10"> + <userprofile:UserProfileView /> + </StackPanel> + </TabItem> - <TabItem Name="tabItemSettings" Visibility="{Binding ShowSettings, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - <settings:SettingsView /> - </StackPanel> - </TabItem> + <TabItem Name="tabItemSettings" Visibility="{Binding ShowSettings, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10"> + <settings:SettingsView /> + </StackPanel> + </TabItem> - <TabItem Name="tabItemAbout" Visibility="{Binding ShowAbout, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - <about:AboutView /> - </StackPanel> - </TabItem> + <TabItem Name="tabItemAbout" Visibility="{Binding ShowAbout, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10"> + <about:AboutView /> + </StackPanel> + </TabItem> - <TabItem Name="tabItemHelp" Visibility="{Binding ShowHelp, Converter={converters:BooleanToVisibilityConverter}}"> - <StackPanel Margin="25,10"> - <about:AboutView /> - </StackPanel> - </TabItem> - <!-- End of hidden tabs--> - </TabControl> - </Grid> + <TabItem Name="tabItemHelp" Visibility="{Binding ShowHelp, Converter={converters:BooleanToVisibilityConverter}}"> + <StackPanel Margin="25,10"> + <about:AboutView /> + </StackPanel> + </TabItem> + <!-- End of hidden tabs--> + </TabControl> + </Grid> </UserControl> \ No newline at end of file
--- a/MetroWpf/MetroWpf/Presentation/Menu/MenuViewModel.cs Thu Mar 15 06:59:15 2012 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Menu/MenuViewModel.cs Tue Mar 20 13:37:46 2012 +0000 @@ -8,178 +8,207 @@ namespace MetroWpf.Presentation.Menu { - public class MenuViewModel : ViewModelBase - { - private Screen selectedTabIndex; - public Screen SelectedTabIndex - { - get { return selectedTabIndex; } - set - { - if (selectedTabIndex == value) - return; - - selectedTabIndex = value; - RaisePropertyChanged("SelectedTabIndex"); - } - } - - private bool showStocks; - public bool ShowStocks + public class MenuViewModel : ViewModelBase { - get { return showStocks; } - set - { - if (showStocks == value) - return; + private Screen selectedTabIndex; + public Screen SelectedTabIndex + { + get { return selectedTabIndex; } + set + { + if (selectedTabIndex == value) + return; + + selectedTabIndex = value; + RaisePropertyChanged("SelectedTabIndex"); + } + } - showStocks = value; - RaisePropertyChanged("ShowStocks"); - } - } + private bool showStocks; + public bool ShowStocks + { + get { return showStocks; } + set + { + if (showStocks == value) + return; + + showStocks = value; + RaisePropertyChanged("ShowStocks"); + } + } - private bool showFxRates; - public bool ShowFxRates - { - get { return showFxRates; } - set - { - if (showFxRates == value) - return; + private bool showFxRates; + public bool ShowFxRates + { + get { return showFxRates; } + set + { + if (showFxRates == value) + return; + + showFxRates = value; + RaisePropertyChanged("ShowFxRates"); + } + } - showFxRates = value; - RaisePropertyChanged("ShowFxRates"); - } - } + private bool showLogin; + public bool ShowLogin + { + get { return showLogin; } + set + { + if (showLogin == value) + return; + + showLogin = value; + RaisePropertyChanged("ShowLogin"); + } + } - private bool showLogin; - public bool ShowLogin - { - get { return showLogin; } - set - { - if (showLogin == value) - return; + private bool showUserProfile; + public bool ShowUserProfile + { + get { return showUserProfile; } + set + { + if (showUserProfile == value) + return; - showLogin = value; - RaisePropertyChanged("ShowLogin"); - } - } + showUserProfile = value; + RaisePropertyChanged("ShowUserProfile"); + } + } - private bool showUserProfile; - public bool ShowUserProfile - { - get { return showUserProfile; } - set - { - if (showUserProfile == value) - return; + private bool showSettings; + public bool ShowSettings + { + get { return showSettings; } + set + { + if (showSettings == value) + return; - showUserProfile = value; - RaisePropertyChanged("ShowUserProfile"); - } - } + showSettings = value; + RaisePropertyChanged("ShowSettings"); + } + } - private bool showSettings; - public bool ShowSettings - { - get { return showSettings; } - set - { - if (showSettings == value) - return; + private bool showAbout; + public bool ShowAbout + { + get { return showAbout; } + set + { + if (showAbout == value) + return; - showSettings = value; - RaisePropertyChanged("ShowSettings"); - } - } + showAbout = value; + RaisePropertyChanged("ShowAbout"); + } + } - private bool showAbout; - public bool ShowAbout - { - get { return showAbout; } - set - { - if (showAbout == value) - return; + private bool showHelp; + public bool ShowHelp + { + get { return showHelp; } + set + { + if (showHelp == value) + return; - showAbout = value; - RaisePropertyChanged("ShowAbout"); - } - } + showHelp = value; + RaisePropertyChanged("ShowHelp"); + } + } - private bool showHelp; - public bool ShowHelp - { - get { return showHelp; } - set - { - if (showHelp == value) - return; + public MenuViewModel() + { + Init(); + } - showHelp = value; - RaisePropertyChanged("ShowHelp"); - } - } + private void Init() + { + Messenger.Default.Register<NavigationMessage>( + this, + message => ChangeDisplayScreen(message.DisplayScreen)); - public MenuViewModel() - { - Init(); - } + Messenger.Default.Register<UserAuthenticatedMessage>( + this, + message => UserAuthenticated(message.UserId)); - private void Init() - { - Messenger.Default.Register<NavigationMessage>( - this, - message => ChangeDisplayScreen(message.DisplayScreen)); + ChangeDisplayScreen(Screen.Login); + } - ChangeDisplayScreen(Screen.Login); - } + private void UserAuthenticated(string userId) + { + Messenger.Default.Send(new NavigationMessage() { DisplayScreen= Screen.Stocks }); + } - private void ChangeDisplayScreen(Screen screen) - { - switch (screen) - { - case Screen.Login: - ShowStocks = false; - ShowFxRates = false; - ShowLogin = true; - ShowUserProfile = false; - ShowSettings = false; - ShowAbout = false; - ShowHelp = false; - break; - case Screen.UserProfile: - ShowLogin = false; - ShowUserProfile = true; - ShowSettings = false; - ShowAbout = false; - ShowHelp = false; - break; - case Screen.Settings: - ShowLogin = false; - ShowUserProfile = false; - ShowSettings = true; - ShowAbout = false; - ShowHelp = false; - break; - case Screen.About: - ShowLogin = false; - ShowUserProfile = false; - ShowSettings = false; - ShowAbout = true; - ShowHelp = false; - break; - case Screen.Help: - ShowLogin = false; - ShowUserProfile = false; - ShowSettings = false; - ShowAbout = false; - ShowHelp = true; - break; - } + private void ChangeDisplayScreen(Screen screen) + { + switch (screen) + { + case Screen.Login: + ShowLogin = true; + + ShowStocks = false; + ShowFxRates = false; + ShowUserProfile = false; + ShowSettings = false; + ShowAbout = false; + ShowHelp = false; + break; + case Screen.Stocks: + ShowStocks = true; + ShowFxRates = true; + + ShowLogin = false; + ShowUserProfile = false; + ShowSettings = false; + ShowAbout = false; + ShowHelp = false; + break; + case Screen.UserProfile: + ShowUserProfile = true; + + ShowStocks = false; + ShowFxRates = false; + ShowLogin = false; + ShowSettings = false; + ShowAbout = false; + ShowHelp = false; + break; + case Screen.Settings: + ShowSettings = true; + + ShowStocks = false; + ShowFxRates = false; + ShowLogin = false; + ShowUserProfile = false; + ShowAbout = false; + ShowHelp = false; + break; + case Screen.About: + ShowAbout = true; + + ShowStocks = false; + ShowFxRates = false; + ShowLogin = false; + ShowUserProfile = false; + ShowSettings = false; + ShowHelp = false; + break; + case Screen.Help: + ShowLogin = false; + ShowUserProfile = false; + ShowSettings = false; + ShowAbout = false; + ShowHelp = true; + break; + } - SelectedTabIndex = screen; + SelectedTabIndex = screen; + } } - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/DisplayStockPrice.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,90 @@ +using GalaSoft.MvvmLight; +using Stocks.Common.Models; + +namespace MetroWpf.Presentation.Stocks +{ + public class DisplayStockPrice : ObservableObject + { + public static DisplayStockPrice Create(Price price) + { + return new DisplayStockPrice() + { + Symbol = price.Symbol, + CurrentPrice = price.CurrentPrice, + PreviousPrice = price.PreviousPrice + }; + } + + public void Update(Price price) + { + Symbol = price.Symbol; + CurrentPrice = price.CurrentPrice; + PreviousPrice = price.PreviousPrice; + } + + public const string SymbolPropertyName = "Symbol"; + private string _symbol; + public string Symbol + { + get { return _symbol; } + private set + { + if (_symbol == value) return; + _symbol = value; + RaisePropertyChanged(SymbolPropertyName); + } + } + + public const string CompanyNamePropertyName = "CompanyName"; + private string _companyName; + public string CompanyName + { + get { return _companyName; } + private set + { + if (_companyName == value) return; + _companyName = value; + RaisePropertyChanged(CompanyNamePropertyName); + } + } + + public const string CurrentPricePropertyName = "CurrentPrice"; + private decimal _currentPrice = 0; + public decimal CurrentPrice + { + get { return _currentPrice; } + private set + { + if (_currentPrice == value) return; + _currentPrice = value; + RaisePropertyChanged(CurrentPricePropertyName); + } + } + + public const string PreviousPricePropertyName = "PreviousPrice"; + private decimal _previousPrice = 0; + public decimal PreviousPrice + { + get { return _previousPrice; } + private set + { + if (_previousPrice == value) return; + _previousPrice = value; + RaisePropertyChanged(PreviousPricePropertyName); + } + } + + public const string DeltaPropertyName = "Delta"; + private decimal _delta = 0; + public decimal Delta + { + get { return _delta; } + private set + { + if (_delta == value) return; + _delta = value; + RaisePropertyChanged(DeltaPropertyName); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/StockTest.xaml Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,12 @@ +<UserControl x:Class="MetroWpf.Presentation.Stocks.StockTest" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + mc:Ignorable="d" + d:DesignHeight="300" d:DesignWidth="300" + DataContext="{Binding StocksViewModel, Source={StaticResource Locator}}"> + <Grid> + + </Grid> +</UserControl>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/StockTest.xaml.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace MetroWpf.Presentation.Stocks +{ + /// <summary> + /// Interaction logic for StockTest.xaml + /// </summary> + public partial class StockTest : UserControl + { + public StockTest() + { + InitializeComponent(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/StocksView.xaml Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,68 @@ +<UserControl x:Class="MetroWpf.Presentation.Stocks.StocksView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:Converters="clr-namespace:MetroWpf.Converters" mc:Ignorable="d" + d:DesignHeight="100" d:DesignWidth="600" + DataContext="{Binding StocksViewModel, Source={StaticResource Locator}}"> + + <Grid x:Name="LayoutRoot"> + <Grid.RowDefinitions> + <RowDefinition Height="60" /> + <RowDefinition Height="20" /> + <RowDefinition Height="280" /> + <RowDefinition Height="180" /> + </Grid.RowDefinitions> + <Grid.Resources> + <Converters:BoolToServiceRunningTextConverter x:Key="BoolToServiceRunningTextConverter" /> + <Converters:BoolToSubscribedTextConverter x:Key="BoolToSubscribedTextConverter" /> + <Converters:DeltaToIconConverter x:Key="DeltaToIconConverter" /> + <Converters:AbsoluteNumberConverter x:Key="AbsoluteNumberConverter" /> + <!--<Converters:DateTimeToTimeConverter x:Key="DateTimeToTimeConverter" />--> + </Grid.Resources> + + <StackPanel Grid.Row="0" Orientation="Horizontal"> + <Button x:Name="btnServiceRunning" + Width="100" + Height="30" + HorizontalAlignment="Left" + Content="{Binding ServiceRunning, Converter={StaticResource BoolToServiceRunningTextConverter}}" + Command="{Binding ServiceCommand, Mode=TwoWay}" + Margin="5,0,0,0"/> + <Button x:Name="btnSubscribe" + Width="100" + Height="30" + Margin="10,0,0,0" + HorizontalAlignment="Left" + Content="{Binding Subscribed, Converter={StaticResource BoolToSubscribedTextConverter}}" + Command="{Binding SubscriptionCommand, Mode=TwoWay}" /> + </StackPanel> + + <StackPanel Grid.Row="1" Orientation="Horizontal"> + <TextBlock Text="Company" Width="170" Margin="5,0,0,0" /> + <TextBlock Text="Price" Width="100" /> + <TextBlock Text="Previous" Width="100" /> + <TextBlock Text="Change" Width="105" /> + <TextBlock Text="Time" Width="105" /> + </StackPanel> + <ListBox x:Name="lbStockPrices" + ItemsSource="{Binding Path=DisplayStockPrices}" + BorderThickness="0" FontFamily="Segoe UI" + Grid.Row="2"> + <ListBox.ItemTemplate> + <DataTemplate> + <StackPanel Orientation="Horizontal" Height="25"> + <TextBlock Text="{Binding CompanyName}" Width="125" FontSize="15" Margin="10,0,0,0"/> + <TextBlock Text="{Binding Symbol}" Width="45" FontSize="15" Margin="10,0,0,0"/> + <TextBlock Text="{Binding CurrentPrice}" Width="100" FontSize="15" /> + <TextBlock Text="{Binding PreviousPrice}" Width="100" FontSize="15" /> + <Image Source="{Binding Delta, Converter={StaticResource DeltaToIconConverter}}" Width="20" /> + <TextBlock Text="{Binding Delta, Converter={StaticResource AbsoluteNumberConverter}}" Width="85" FontSize="15" /> + <!--<TextBlock Text="{Binding Timestamp, Converter={StaticResource DateTimeToTimeConverter}}" Width="100" FontSize="15" />--> + </StackPanel> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> + </UserControl> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/StocksView.xaml.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,15 @@ +using System.Windows.Controls; + +namespace MetroWpf.Presentation.Stocks +{ + /// <summary> + /// Interaction logic for StocksView.xaml + /// </summary> + public partial class StocksView : UserControl + { + public StocksView() + { + InitializeComponent(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/MetroWpf/Presentation/Stocks/StocksViewModel.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,107 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Windows.Input; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using Stocks.Common; +using Stocks.Common.Events; + +namespace MetroWpf.Presentation.Stocks +{ + public class StocksViewModel : ViewModelBase + { + private readonly IStocksService _service; + + public BindingList<DisplayStockPrice> DisplayStockPrices { get; set; } + public ICommand ServiceCommand { get; set; } + public ICommand SubscriptionCommand { get; set; } + + public StocksViewModel(IStocksService service) + { + _service = service; + GetLatestPrices(); + + SubscriptionCommand = new RelayCommand(SubscriptionCommandExecute); + ServiceCommand = new RelayCommand(ServiceRunningCommandExecute); + + var priceUpdates = Observable.FromEventPattern<PriceChangedEventArgs>( + _service, "PriceChanged"); + + priceUpdates.Where(e => Subscribed) + //.Throttle(TimeSpan.FromSeconds(1)) + .Subscribe(PriceChanged); + } + + public void PriceChanged(EventPattern<PriceChangedEventArgs> e) + { + var displayRate = DisplayStockPrices.First( + rate => rate.Symbol == e.EventArgs.Price.Symbol); + + if (displayRate != null) + displayRate.Update(e.EventArgs.Price); + } + + + private void GetLatestPrices() + { + DisplayStockPrices = new BindingList<DisplayStockPrice>(); + var currentRates = _service.GetFullCurrentPrices(); + foreach (var latestRate in currentRates) + { + var displayRate = DisplayStockPrice.Create(latestRate); + DisplayStockPrices.Add(displayRate); + } + } + + private const string SubscribedPropertyName = "Subscribed"; + private bool _subscribed = false; + + public bool Subscribed + { + get { return _subscribed; } + set + { + if (_subscribed == value) return; + _subscribed = value; + RaisePropertyChanged(SubscribedPropertyName); + } + } + + private const string ServiceRunningPropertyName = "ServiceRunning"; + private bool _serviceRunning; + + public bool ServiceRunning + { + get { return _serviceRunning; } + set + { + if (_serviceRunning == value) return; + _serviceRunning = value; + RaisePropertyChanged(ServiceRunningPropertyName); + } + } + + private void ServiceRunningCommandExecute() + { + if (_service.IsRunning) + { + _service.Stop(); + ServiceRunning = false; + } + else + { + _service.Start(); + ServiceRunning = true; + } + } + + private void SubscriptionCommandExecute() + { + //toggle subscribed + Subscribed = !Subscribed; + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/ConfigurationService.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using Stocks.Common; +using Stocks.Common.Core; +using Stocks.Common.Models; +using Newtonsoft.Json; + +namespace Stocks.Common +{ + public class ConfigurationService : IConfigurationService + { + private string jsonFilename; + private string serviceUrlPrefix = "http://finance.yahoo.com/d/quotes.csv?s="; + private string serviceUrlSuffix = "&f=snl1"; + + public ConfigurationService(string filename) + { + jsonFilename = filename; + } + public List<Company> GetCompanies() + { + return new FileSerializer().DeserializeJson<Company>(jsonFilename); + } + + public string GetServiceUrl(string symbolCsv) + { + if (string.IsNullOrEmpty(symbolCsv)) + throw new ArgumentException(); + + return string.Concat(serviceUrlPrefix, symbolCsv, serviceUrlSuffix); + } + + public string GetServiceUrl(string[] symbols) + { + if (symbols == null || symbols.Length == 0) + throw new ArgumentException(); + + var symbolCsv = string.Join(",", symbols); + return GetServiceUrl(symbolCsv); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Core/ExtensionMethods.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,18 @@ +using System; + +namespace Stocks.Common.Core +{ + public static class StringHelper + { + /// <summary> + /// Receives string and returns the string with its letters reversed. + /// </summary> + public static string Reverse(this string s) + { + if (s == null) return null; + char[] arr = s.ToCharArray(); + Array.Reverse(arr); + return new string(arr); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Core/FileSerializer.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json; + +namespace Stocks.Common.Core +{ + public class FileSerializer + { + public void SerializeJson(string filename, object obj) + { + string json = JsonConvert.SerializeObject(obj); + Serialize(filename, json); + } + + public void Serialize(string filename, string text) + { + using (StreamWriter writer = new StreamWriter(filename)) + { writer.Write(text); } + } + + public void Serialize(string filename, IFormatter formatter, object objectToSerialize) + { + using (Stream stream = File.Open(filename, FileMode.Create)) + formatter.Serialize(stream, objectToSerialize); + } + + public string Deserialize(string filename) + { + StringBuilder sb = new StringBuilder(); + using (StreamReader reader = new StreamReader(filename)) + if (reader != null) + sb.AppendLine(reader.ReadToEnd()); + + return sb.ToString(); + } + + public List<T> DeserializeJson<T>(string filename) + { + var json = Deserialize(filename); + return JsonConvert.DeserializeObject<List<T>>(json); + } + + public T[] Deserialize<T>(string filename, IFormatter formatter, Type type) + { + using (Stream stream = File.Open(filename, FileMode.Open)) + { + var items = (T[])formatter.Deserialize(stream); + return items; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Core/TimedDelegates.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,20 @@ +using System; +using System.Diagnostics; + +namespace Stocks.Common.Core +{ + public class TimedDelegates + { + public static T Execute<T>( + Func<T, T> func, + T paramIn, + out Stopwatch stopwatch) + { + stopwatch = new Stopwatch(); + stopwatch.Start(); + T result = func(paramIn); + stopwatch.Stop(); + return result; + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Events/PriceChangedEventArgs.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,10 @@ +using System; +using Stocks.Common.Models; + +namespace Stocks.Common.Events +{ + public class PriceChangedEventArgs : EventArgs + { + public Price Price { get; set; } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Exceptions/InvalidWebPriceData.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Stocks.Common.Exceptions +{ + [Serializable] + public class InvalidWebPriceDataException : Exception + { + public string WebPriceData { get; set; } + public InvalidWebPriceDataException() : base () + { + + } + public InvalidWebPriceDataException(string webPriceData) : base(webPriceData) + { + WebPriceData = webPriceData; + } + + public InvalidWebPriceDataException(string webPriceData, Exception innerException) + : base("Unexpected web price data", innerException) + { + WebPriceData = webPriceData; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Factory.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,36 @@ +using NLog; +using Stocks.Common.Core; +using Stocks.Common.Models; +using System; +using Stocks.Common.Exceptions; + +namespace Stocks.Common +{ + public class Factory + { + public static Price CreatePrice(string webPriceData) + { + if (string.IsNullOrEmpty(webPriceData)) + throw new InvalidWebPriceDataException(webPriceData); + + try + { + var symbol = webPriceData.Substring(1, webPriceData.IndexOf('"', 2) - 1); + + decimal price = decimal.Parse(webPriceData.Substring( + webPriceData.Length - webPriceData.Reverse().IndexOf(","))); + + return new Price() + { + Symbol = symbol, + PreviousPrice = price, + CurrentPrice = price + }; + } + catch (Exception e) + { + throw new InvalidWebPriceDataException(webPriceData, e); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Fakes/FakeWebClientShim.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,23 @@ + +namespace Stocks.Common.Fakes +{ + public class FakeWebClientShim : IWebClientShim + { + private string _downloadString; + + public FakeWebClientShim(string downloadString = "") + { + _downloadString = downloadString; + } + + public string DownloadString(string address) + { + return _downloadString; + } + + public void Dispose() + { + // do nothing + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/IConfigurationService.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using Stocks.Common.Models; + +namespace Stocks.Common +{ + public interface IConfigurationService + { + List<Company> GetCompanies(); + string GetServiceUrl(string symbolCsv); + string GetServiceUrl(string[] symbols); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/IStocksService.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Stocks.Common.Models; + +namespace Stocks.Common +{ + public interface IStocksService + { + IList<Price> GetFullCurrentPrices(); + bool IsRunning { get; } + void Start(); + void Stop(); + } +} + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/IWebClientShim.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,8 @@ +using System; +namespace Stocks.Common +{ + public interface IWebClientShim : IDisposable + { + string DownloadString(string address); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Models/Company.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,9 @@ + +namespace Stocks.Common.Models +{ + public class Company + { + public string Symbol { get; set; } + public string Name { get; set; } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Models/Price.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,129 @@ +using Stocks.Common.Core; +using System.Collections; +using System; +using System.Collections.Generic; + +namespace Stocks.Common.Models +{ + public class Price : IEquatable<Price>, IComparable<Price>, IComparable + { + public string Symbol { get; set; } + public decimal CurrentPrice { get; set; } + public decimal PreviousPrice { get; set; } + + public Price() + { + } + + public Price(string symbol, decimal currentPrice, decimal previousPrice) + : this() + { + Symbol = symbol; + CurrentPrice = currentPrice; + PreviousPrice = previousPrice; + } + + public override bool Equals(object obj) + { + if (obj is Price) + return Equals(obj as Price); + else + return false; + } + + public bool Equals(Price other) + { + return (Symbol == other.Symbol + && CurrentPrice == other.CurrentPrice + && PreviousPrice == other.PreviousPrice); + } + + public int CompareTo(Price other) + { + return Symbol.CompareTo(other.Symbol); + } + + public int CompareTo(Price other, PriceComparisonType comparisonType) + { + switch (comparisonType) + { + case PriceComparisonType.NotSet: + case PriceComparisonType.Symbol: + return Symbol.CompareTo(other.Symbol); + case PriceComparisonType.CurrentPrice: + return CurrentPrice.CompareTo(other.CurrentPrice); + case PriceComparisonType.PreviousPrice: + return PreviousPrice.CompareTo(other.PreviousPrice); + default: + throw new Exception("Unknown comparison type"); + } + } + + public int CompareTo(object obj) + { + Price other; + if (obj is Price) + other = obj as Price; + else + throw new ArgumentException("obj is not a Price"); + + return CompareTo(other); + } + public override int GetHashCode() + { + int hash = 13; + hash = (hash * 7) + Symbol.GetHashCode(); + hash = (hash * 7) + CurrentPrice.GetHashCode(); + hash = (hash * 7) + PreviousPrice.GetHashCode(); + return hash; + } + + public static bool operator ==(Price lhs, Price rhs) + { + if (System.Object.ReferenceEquals(lhs, rhs)) + return true; + + if (((object)lhs == null) || ((object)rhs == null)) + return false; + + return lhs.Symbol == rhs.Symbol + && lhs.CurrentPrice == rhs.CurrentPrice + && lhs.PreviousPrice == rhs.PreviousPrice; + } + + public static bool operator !=(Price lhs, Price rhs) + { + return !(lhs == rhs); + } + + + public class PriceComparer : IComparer<Price>, IComparer + { + public PriceComparisonType ComparisonMethod { get; set; } + + public int Compare(Price x, Price y) + { + return x.CompareTo(y, ComparisonMethod); + } + + public int Compare(object x, object y) + { + Price lhs, rhs; + + if (x is Price) + lhs = x as Price; + else + throw new ArgumentException("x is not a Price"); + + if (y is Price) + rhs = y as Price; + else + throw new ArgumentException("y is not a Price"); + + return lhs.CompareTo(rhs, ComparisonMethod); + } + } + } + + public enum PriceComparisonType { NotSet = 0, Symbol, CurrentPrice, PreviousPrice } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Models/Quote.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,10 @@ + +namespace Stocks.Common.Models +{ + public class Quote + { + public string Symbol { get; set; } + public string Name { get; set; } + public double LastTrade { get; set; } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Models/SummaryStats.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,35 @@ +using System; +using System.Text; + +namespace Stocks.Common.Models +{ + public class SummaryStats + { + public WebRequestStats LastWebRequest { get; set; } + public int PriceChangeEvents { get; set; } + public int NumberOfRequests { get; set; } + public DateTime TimeServiceStarted { get; set; } + + public void Reset() + { + LastWebRequest = new WebRequestStats(); + PriceChangeEvents = 0; + NumberOfRequests = 0; + TimeServiceStarted = DateTime.Now; + } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendLine("Stocks Service Summary Stats:"); + sb.AppendLine(string.Format(" Time service started: {0}", TimeServiceStarted)); + sb.AppendLine(string.Format(" Number of requests: {0}", NumberOfRequests)); + sb.AppendLine(string.Format(" Number of price change events: {0}", PriceChangeEvents)); + sb.AppendLine(string.Format(" Number of symbols sent on last request: {0}", LastWebRequest.SymbolCount)); + sb.AppendLine(string.Format(" Number of prices in last response: {0}", LastWebRequest.PricesDownloaded)); + sb.AppendLine(string.Format(" Time taken for last request: {0}", LastWebRequest.Duration)); + //sb.AppendLine(string.Format(" Response string for last request: {0}", LastWebRequest.Response)); + return sb.ToString(); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Models/WebRequestStats.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,11 @@ +namespace Stocks.Common.Models +{ + public class WebRequestStats + { + public int Duration { get; set; } + public int PricesDownloaded { get; set; } + public string Response { get; set; } + public string Request { get; set; } + public int SymbolCount { get; set; } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Properties/AssemblyInfo.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Stocks.Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Stocks.Common")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("474b578e-a3b7-49d6-921d-f9c07c16ed92")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/Stocks.Common.csproj Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{847365D2-E27B-44C3-8DF4-B749D9FA65D7}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Stocks.Common</RootNamespace> + <AssemblyName>Stocks.Common</AssemblyName> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Newtonsoft.Json"> + <HintPath>..\Libs\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath> + </Reference> + <Reference Include="NLog"> + <HintPath>..\Libs\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="ConfigurationService.cs" /> + <Compile Include="Core\ExtensionMethods.cs" /> + <Compile Include="Core\FileSerializer.cs" /> + <Compile Include="Core\TimedDelegates.cs" /> + <Compile Include="Events\PriceChangedEventArgs.cs" /> + <Compile Include="Exceptions\InvalidWebPriceData.cs" /> + <Compile Include="Factory.cs" /> + <Compile Include="Fakes\FakeWebClientShim.cs" /> + <Compile Include="IConfigurationService.cs" /> + <Compile Include="IStocksService.cs" /> + <Compile Include="IWebClientShim.cs" /> + <Compile Include="Models\Company.cs" /> + <Compile Include="Models\Price.cs" /> + <Compile Include="Models\Quote.cs" /> + <Compile Include="Models\SummaryStats.cs" /> + <Compile Include="Models\WebRequestStats.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="WebClientShim.cs" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Common/WebClientShim.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,28 @@ +using System.Net; +using System; + +namespace Stocks.Common +{ + // Shim to wrap WebClient component to allow the shim to + // be mocked or stubbed for unit tests. + // Favours injection and composition over inheritence + public class WebClientShim : IWebClientShim + { + private WebClient _webClient; + + public WebClientShim(WebClient webClient) + { + _webClient = webClient; + } + + public string DownloadString(string address) + { + return _webClient.DownloadString(address).ToString(); + } + + public void Dispose() + { + _webClient.Dispose(); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Service/AssemblyInit.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,23 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using NLog; + +namespace Stocks.Service +{ + internal class AssemblyInit + { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public AssemblyInit() + { + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo( + Assembly.GetExecutingAssembly().Location); + + logger.Info("Init: {0} {1} ", + Assembly.GetExecutingAssembly().GetName(), + fvi.ProductVersion); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Service/Properties/AssemblyInfo.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Stocks.Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Stocks.Service")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e6a7b2c2-b543-498c-ad46-b33cc27b57fd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Service/Stocks.Service.csproj Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{47F54122-5381-48D8-ACF7-72BBE0353511}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Stocks.Service</RootNamespace> + <AssemblyName>Stocks.Service</AssemblyName> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="NLog"> + <HintPath>..\Libs\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AssemblyInit.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="StocksService.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Stocks.Common\Stocks.Common.csproj"> + <Project>{847365D2-E27B-44C3-8DF4-B749D9FA65D7}</Project> + <Name>Stocks.Common</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.Service/StocksService.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using NLog; +using Stocks.Common; +using Stocks.Common.Core; +using Stocks.Common.Events; +using Stocks.Common.Models; + +namespace Stocks.Service +{ + public class StocksService : IStocksService + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + private IList<Company> _companies; + private readonly IConfigurationService _configurationService; + private List<Price> _currentPrices; + private string _serviceUrl; + private readonly IWebClientShim _webClient; + + public bool IsRunning { get; private set; } + public SummaryStats Stats { get; private set; } + + public StocksService( + IConfigurationService configurationService, + IWebClientShim webClientShim) + { + new AssemblyInit(); + + _webClient = webClientShim; + _configurationService = configurationService; + Stats = new SummaryStats(); + + GetCompanyList(); + } + + private void GetCompanyList() + { + _companies = _configurationService.GetCompanies(); + + var symbolsCsv = _companies.Select( + c => c.Symbol).Aggregate((c, d) => c + "," + d); + + _serviceUrl = _configurationService.GetServiceUrl(symbolsCsv); + } + + public IList<Price> GetFullCurrentPrices() + { + return _companies.Select(company => new Price(company.Symbol, 0, 0)).ToList(); + } + + public event PriceChangedEventHandler PriceChanged; + public delegate void PriceChangedEventHandler(object sender, PriceChangedEventArgs e); + protected virtual void OnPriceChanged(Price price) + { + Stats.PriceChangeEvents++; + + if (PriceChanged != null) + PriceChanged(this, new PriceChangedEventArgs() { Price = price }); + } + + public void Start() + { + PrepareForServiceStarting(); + Task.Factory.StartNew(DownloadPrices); + } + + public void Stop() + { + IsRunning = false; + Log.Debug("StockService stopped"); + } + + private void PrepareForServiceStarting() + { + Log.Debug("StockService starting"); + IsRunning = true; + Stats.Reset(); + _currentPrices = new List<Price>(_companies.Count); + } + + private void DownloadPrices() + { + try + { + while (IsRunning) + { + Stopwatch timeToDownload; + var webResponse = TimedDelegates.Execute( + _webClient.DownloadString, + _serviceUrl, + out timeToDownload); + + PopulatePricesFromWebResponse(webResponse); + UpdateStats(timeToDownload, webResponse); + } + } + catch (Exception e) + { + Log.Error("Exception during DownloadPrices()"); + Log.Error("Stack Trace {0}: /r/nException Message: {1}", e.StackTrace, e.Message); + this.Stop(); + } + } + + private void PopulatePricesFromWebResponse(string webResponse) + { + var webPrices = webResponse.Split( + new[] { "\n", "\r\n" }, + StringSplitOptions.RemoveEmptyEntries); + + foreach (var webPriceData in webPrices) + { + var webPrice = Factory.CreatePrice(webPriceData); + var localPrice = _currentPrices.Find(x => x.Symbol == webPrice.Symbol); + + if (localPrice == null) + { + _currentPrices.Add(new Price(webPrice.Symbol, webPrice.CurrentPrice, webPrice.PreviousPrice)); + continue; + } + + if (!localPrice.Equals(webPrice)) + UpdateLocalPrice(webPrice, localPrice); + } + } + + private void UpdateLocalPrice(Price webPrice, Price localPrice) + { + localPrice.PreviousPrice = localPrice.CurrentPrice; + localPrice.CurrentPrice = webPrice.CurrentPrice; + OnPriceChanged(localPrice); + } + + private void UpdateStats(Stopwatch timeToDownload, string webResponse) + { + Stats.LastWebRequest.Duration = (int)timeToDownload.ElapsedMilliseconds; + Stats.LastWebRequest.PricesDownloaded = _currentPrices.Count; + Stats.LastWebRequest.Response = webResponse; + Stats.LastWebRequest.Request = _serviceUrl; + Stats.LastWebRequest.SymbolCount = _companies.Count; + Stats.NumberOfRequests++; + Log.Trace(Stats); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/App.xaml Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,15 @@ +<Application x:Class="Stocks.UI.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:Stocks.UI" + mc:Ignorable="d"> + <Application.Resources> + <ResourceDictionary> + <!--Global IoC Locator--> + <local:Locator x:Key="Locator" + d:IsDataSource="True" /> + </ResourceDictionary> + </Application.Resources> +</Application>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/App.xaml.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Windows; + +namespace Stocks.UI +{ + /// <summary> + /// Interaction logic for App.xaml + /// </summary> + public partial class App : Application + { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Converters/AbsoluteNumberConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace Stocks.UI.Converters +{ + public class AbsoluteNumberConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return Math.Abs((decimal) value); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Converters/BoolToServiceRunningTextConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace Stocks.UI.Converters +{ + public class BoolToServiceRunningTextConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return (bool) value ? "Service Running" : "Service Stopped"; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Converters/BoolToSubscribedTextConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; + +namespace Stocks.UI.Converters +{ + public class BoolToSubscribedTextConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return (bool) value ? "Subscribed" : "Unsubscribed"; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Converters/DateTimeToTimeConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,24 @@ +using System; +using System.Windows.Data; + +namespace Stocks.UI.Converters +{ + public class DateTimeToTimeConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string dateTimeString = ((DateTime) value).ToString("HH:mm:ss.ffff"); + + return dateTimeString; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Converters/DeltaToIconConverter.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,46 @@ +using System; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace Stocks.UI.Converters +{ + public class DeltaToIconConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string uri; + BitmapImage image; + decimal delta; + string file = "UNK"; + + try + { + delta = (decimal) value; + { + if (delta > 0) + file = "UP"; + else if (delta < 0) + file = "DOWN"; + else + file = "LEVEL"; + } + } + finally + { + uri = string.Format("../Images/{0}.png", file); + image = new BitmapImage(new Uri(uri, UriKind.Relative)); + } + + return image; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/DisplayStockPrice.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,90 @@ +using GalaSoft.MvvmLight; +using Stocks.Common.Models; + +namespace Stocks.UI +{ + public class DisplayStockPrice : ObservableObject + { + public static DisplayStockPrice Create(Price price) + { + return new DisplayStockPrice() + { + Symbol = price.Symbol, + CurrentPrice = price.CurrentPrice, + PreviousPrice = price.PreviousPrice + }; + } + + public void Update(Price price) + { + Symbol = price.Symbol; + CurrentPrice = price.CurrentPrice; + PreviousPrice = price.PreviousPrice; + } + + public const string SymbolPropertyName = "Symbol"; + private string _symbol; + public string Symbol + { + get { return _symbol; } + private set + { + if (_symbol == value) return; + _symbol = value; + RaisePropertyChanged(SymbolPropertyName); + } + } + + public const string CompanyNamePropertyName = "CompanyName"; + private string _companyName; + public string CompanyName + { + get { return _companyName; } + private set + { + if (_companyName == value) return; + _companyName = value; + RaisePropertyChanged(CompanyNamePropertyName); + } + } + + public const string CurrentPricePropertyName = "CurrentPrice"; + private decimal _currentPrice = 0; + public decimal CurrentPrice + { + get { return _currentPrice; } + private set + { + if (_currentPrice == value) return; + _currentPrice = value; + RaisePropertyChanged(CurrentPricePropertyName); + } + } + + public const string PreviousPricePropertyName = "PreviousPrice"; + private decimal _previousPrice = 0; + public decimal PreviousPrice + { + get { return _previousPrice; } + private set + { + if (_previousPrice == value) return; + _previousPrice = value; + RaisePropertyChanged(PreviousPricePropertyName); + } + } + + public const string DeltaPropertyName = "Delta"; + private decimal _delta = 0; + public decimal Delta + { + get { return _delta; } + private set + { + if (_delta == value) return; + _delta = value; + RaisePropertyChanged(DeltaPropertyName); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Locator.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,12 @@ +using GalaSoft.MvvmLight.Ioc; + +namespace Stocks.UI +{ + public class Locator + { + public StocksViewModel StocksViewModel + { + get { return SimpleIoc.Default.GetInstance<StocksViewModel>(); } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Properties/AssemblyInfo.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Stocks.UI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Stocks.UI")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file +//inside a <PropertyGroup>. For example, if you are using US english +//in your source files, set the <UICulture> to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Properties/Resources.Designer.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.261 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace Stocks.UI.Properties +{ + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Stocks.UI.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Properties/Resources.resx Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Properties/Settings.Designer.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.261 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace Stocks.UI.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Properties/Settings.settings Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8'?> +<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)"> + <Profiles> + <Profile Name="(Default)" /> + </Profiles> + <Settings /> +</SettingsFile> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/Stocks.UI.csproj Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{63E512BA-D038-4672-9C02-85DD49744E0F}</ProjectGuid> + <OutputType>WinExe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Stocks.UI</RootNamespace> + <AssemblyName>Stocks.UI</AssemblyName> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <TargetFrameworkProfile>Client</TargetFrameworkProfile> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PlatformTarget>x86</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <PlatformTarget>x86</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="GalaSoft.MvvmLight.Extras.WPF4"> + <HintPath>..\Libs\MvvmLight.Extras.4.0.0\GalaSoft.MvvmLight.Extras.WPF4.dll</HintPath> + </Reference> + <Reference Include="GalaSoft.MvvmLight.WPF4"> + <HintPath>..\Libs\MvvmLight.4.0.0\GalaSoft.MvvmLight.WPF4.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Practices.ServiceLocation"> + <HintPath>..\Libs\MvvmLight.Extras.4.0.0\Microsoft.Practices.ServiceLocation.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Reactive"> + <HintPath>..\Libs\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll</HintPath> + </Reference> + <Reference Include="System.Windows.Interactivity"> + <HintPath>..\Libs\System.Windows.Interactivity\System.Windows.Interactivity.dll</HintPath> + </Reference> + <Reference Include="System.Xml" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xaml"> + <RequiredTargetFramework>4.0</RequiredTargetFramework> + </Reference> + <Reference Include="WindowsBase" /> + <Reference Include="PresentationCore" /> + <Reference Include="PresentationFramework" /> + </ItemGroup> + <ItemGroup> + <ApplicationDefinition Include="App.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </ApplicationDefinition> + <Compile Include="Converters\AbsoluteNumberConverter.cs" /> + <Compile Include="Converters\BoolToServiceRunningTextConverter.cs" /> + <Compile Include="Converters\BoolToSubscribedTextConverter.cs" /> + <Compile Include="Converters\DateTimeToTimeConverter.cs" /> + <Compile Include="Converters\DeltaToIconConverter.cs" /> + <Compile Include="DisplayStockPrice.cs" /> + <Compile Include="Locator.cs" /> + <Compile Include="StocksViewModel.cs" /> + <Page Include="StocksView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Compile Include="App.xaml.cs"> + <DependentUpon>App.xaml</DependentUpon> + <SubType>Code</SubType> + </Compile> + <Compile Include="StocksView.xaml.cs"> + <DependentUpon>StocksView.xaml</DependentUpon> + <SubType>Code</SubType> + </Compile> + </ItemGroup> + <ItemGroup> + <Compile Include="Properties\AssemblyInfo.cs"> + <SubType>Code</SubType> + </Compile> + <Compile Include="Properties\Resources.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + <Compile Include="Properties\Settings.Designer.cs"> + <AutoGen>True</AutoGen> + <DependentUpon>Settings.settings</DependentUpon> + <DesignTimeSharedInput>True</DesignTimeSharedInput> + </Compile> + <EmbeddedResource Include="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + <None Include="Properties\Settings.settings"> + <Generator>SettingsSingleFileGenerator</Generator> + <LastGenOutput>Settings.Designer.cs</LastGenOutput> + </None> + <AppDesigner Include="Properties\" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Stocks.Common\Stocks.Common.csproj"> + <Project>{847365D2-E27B-44C3-8DF4-B749D9FA65D7}</Project> + <Name>Stocks.Common</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/StocksView.xaml Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,69 @@ +<UserControl x:Class="Stocks.UI.StocksView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:Converters="clr-namespace:Stocks.UI.Converters" + mc:Ignorable="d" + d:DesignHeight="100" d:DesignWidth="600" + d:DataContext="{Binding StocksViewModel, Source={StaticResource Locator}}"> + + <Grid x:Name="LayoutRoot"> + <Grid.RowDefinitions> + <RowDefinition Height="60" /> + <RowDefinition Height="20" /> + <RowDefinition Height="280" /> + <RowDefinition Height="180" /> + </Grid.RowDefinitions> + <Grid.Resources> + <Converters:BoolToServiceRunningTextConverter x:Key="BoolToServiceRunningTextConverter" /> + <Converters:BoolToSubscribedTextConverter x:Key="BoolToSubscribedTextConverter" /> + <Converters:DeltaToIconConverter x:Key="DeltaToIconConverter" /> + <Converters:AbsoluteNumberConverter x:Key="AbsoluteNumberConverter" /> + <!--<Converters:DateTimeToTimeConverter x:Key="DateTimeToTimeConverter" />--> + </Grid.Resources> + + <StackPanel Grid.Row="0" Orientation="Horizontal"> + <Button x:Name="btnServiceRunning" + Width="100" + Height="30" + HorizontalAlignment="Left" + Content="{Binding Path=ServiceRunning, Converter={StaticResource BoolToServiceRunningTextConverter}}" + Command="{Binding Path=ServiceCommand, Mode=TwoWay}" + Margin="5,0,0,0"/> + <Button x:Name="btnSubscribe" + Width="100" + Height="30" + Margin="10,0,0,0" + HorizontalAlignment="Left" + Content="{Binding Path=Subscribed, Converter={StaticResource BoolToSubscribedTextConverter}}" + Command="{Binding Path=SubscriptionCommand, Mode=TwoWay}" /> + </StackPanel> + + <StackPanel Grid.Row="1" Orientation="Horizontal"> + <TextBlock Text="Company" Width="170" Margin="5,0,0,0" /> + <TextBlock Text="Price" Width="100" /> + <TextBlock Text="Previous" Width="100" /> + <TextBlock Text="Change" Width="105" /> + <TextBlock Text="Time" Width="105" /> + </StackPanel> + <ListBox x:Name="lbStockPrices" + ItemsSource="{Binding Path=DisplayStockPrices}" + BorderThickness="0" FontFamily="Segoe UI" + Grid.Row="2"> + <ListBox.ItemTemplate> + <DataTemplate> + <StackPanel Orientation="Horizontal" Height="25"> + <TextBlock Text="{Binding CompanyName}" Width="125" FontSize="15" Margin="10,0,0,0"/> + <TextBlock Text="{Binding Symbol}" Width="45" FontSize="15" Margin="10,0,0,0"/> + <TextBlock Text="{Binding CurrentPrice}" Width="100" FontSize="15" /> + <TextBlock Text="{Binding PreviousPrice}" Width="100" FontSize="15" /> + <Image Source="{Binding Delta, Converter={StaticResource DeltaToIconConverter}}" Width="20" /> + <TextBlock Text="{Binding Delta, Converter={StaticResource AbsoluteNumberConverter}}" Width="85" FontSize="15" /> + <!--<TextBlock Text="{Binding Timestamp, Converter={StaticResource DateTimeToTimeConverter}}" Width="100" FontSize="15" />--> + </StackPanel> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> + </UserControl> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/StocksView.xaml.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,15 @@ +using System.Windows.Controls; + +namespace Stocks.UI +{ + /// <summary> + /// Interaction logic for StocksView.xaml + /// </summary> + public partial class StocksView : UserControl + { + public StocksView() + { + InitializeComponent(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MetroWpf/Stocks.UI/StocksViewModel.cs Tue Mar 20 13:37:46 2012 +0000 @@ -0,0 +1,107 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Windows.Input; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using Stocks.Common; +using Stocks.Common.Events; + +namespace Stocks.UI +{ + public class StocksViewModel : ViewModelBase + { + private readonly IStocksService _service; + + public BindingList<DisplayStockPrice> DisplayStockPrices { get; set; } + public ICommand ServiceCommand { get; set; } + public ICommand SubscriptionCommand { get; set; } + + public StocksViewModel(IStocksService service) + { + _service = service; + GetLatestPrices(); + + SubscriptionCommand = new RelayCommand(SubscriptionCommandExecute); + ServiceCommand = new RelayCommand(ServiceRunningCommandExecute); + + var priceUpdates = Observable.FromEventPattern<PriceChangedEventArgs>( + _service, "PriceChanged"); + + priceUpdates.Where(e => Subscribed) + //.Throttle(TimeSpan.FromSeconds(1)) + .Subscribe(PriceChanged); + } + + public void PriceChanged(EventPattern<PriceChangedEventArgs> e) + { + var displayRate = DisplayStockPrices.First( + rate => rate.Symbol == e.EventArgs.Price.Symbol); + + if (displayRate != null) + displayRate.Update(e.EventArgs.Price); + } + + + private void GetLatestPrices() + { + DisplayStockPrices = new BindingList<DisplayStockPrice>(); + var currentRates = _service.GetFullCurrentPrices(); + foreach (var latestRate in currentRates) + { + var displayRate = DisplayStockPrice.Create(latestRate); + DisplayStockPrices.Add(displayRate); + } + } + + private const string SubscribedPropertyName = "Subscribed"; + private bool _subscribed = false; + + public bool Subscribed + { + get { return _subscribed; } + set + { + if (_subscribed == value) return; + _subscribed = value; + RaisePropertyChanged(SubscribedPropertyName); + } + } + + private const string ServiceRunningPropertyName = "ServiceRunning"; + private bool _serviceRunning; + + public bool ServiceRunning + { + get { return _serviceRunning; } + set + { + if (_serviceRunning == value) return; + _serviceRunning = value; + RaisePropertyChanged(ServiceRunningPropertyName); + } + } + + private void ServiceRunningCommandExecute() + { + if (_service.IsRunning) + { + _service.Stop(); + ServiceRunning = false; + } + else + { + _service.Start(); + ServiceRunning = true; + } + } + + private void SubscriptionCommandExecute() + { + //toggle subscribed + Subscribed = !Subscribed; + } + } +} \ No newline at end of file