summaryrefslogtreecommitdiff
path: root/Juick
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2013-04-03 05:03:20 +0400
committerGravatar Vitaly Takmazov2013-04-03 05:03:20 +0400
commit151e56ec2e3466389804b8874b4224bf6bd08097 (patch)
tree4b2c07be26036e470afba1f399619c8d64ae8216 /Juick
parent4959050e7996e92ac97f4d05064130251a0628c5 (diff)
New pages, LoginViewModel, Validation (in progress)
Diffstat (limited to 'Juick')
-rw-r--r--Juick/App.xaml.cs25
-rw-r--r--Juick/Classes/AccountManager.cs62
-rw-r--r--Juick/Controls/MessageList.xaml2
-rw-r--r--Juick/Juick.csproj2
-rw-r--r--Juick/LoginView.xaml36
-rw-r--r--Juick/LoginView.xaml.cs22
-rw-r--r--Juick/MainPage.xaml19
-rw-r--r--Juick/MainPage.xaml.cs55
-rw-r--r--Juick/NewPostView.xaml.cs2
-rw-r--r--Juick/ViewModels/LoginViewModel.cs72
-rw-r--r--Juick/ViewModels/PageViewModel.cs4
-rw-r--r--Juick/ViewModels/Validation/DataViewModelBase.cs44
12 files changed, 245 insertions, 100 deletions
diff --git a/Juick/App.xaml.cs b/Juick/App.xaml.cs
index 98dc31c..03e0d10 100644
--- a/Juick/App.xaml.cs
+++ b/Juick/App.xaml.cs
@@ -10,22 +10,31 @@ namespace Juick
{
public partial class App : Application
{
- public void NavigateTo(Uri param) {
- RootFrame.Navigate(param);
+ public void NavigateTo(Uri param, bool removeFromStack = false)
+ {
+ var page = (PhoneApplicationPage) RootFrame.Content;
+ var service = page.NavigationService;
+ service.Navigate(param);
+ if (removeFromStack)
+ {
+ page.Dispatcher.BeginInvoke(() => service.RemoveBackEntry());
+ }
}
- private static AppViewModel context = null;
+ private static AppViewModel _context = null;
public static AppViewModel AppContext
{
get
{
// Delay creation of the view model until necessary
- if (context == null) {
- context = new AppViewModel();
- context.Pages.Add(new PageViewModel(context) { RestUri = "/home?1=1" });
- context.Pages.Add(new PageViewModel(context) { RestUri = "/messages?1=1" });
+ if (_context == null) {
+ _context = new AppViewModel();
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/home?1=1", Caption = "My feed"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1", Caption = "Last"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1&popular=1", Caption = "Top"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1&media=all", Caption = "Images" });
};
- return context;
+ return _context;
}
}
/// <summary>
diff --git a/Juick/Classes/AccountManager.cs b/Juick/Classes/AccountManager.cs
index 8f8e366..4741992 100644
--- a/Juick/Classes/AccountManager.cs
+++ b/Juick/Classes/AccountManager.cs
@@ -1,53 +1,63 @@
using System;
using System.IO.IsolatedStorage;
using System.Net;
+using System.Windows;
using System.Windows.Controls;
namespace Juick.Classes
{
public class AccountManager
{
- const string FileName = "account.xml";
- public NetworkCredential Credentials
+ private string _userName;
+ private string _password;
+
+ public string UserName
{
get
{
- string userName;
- string password;
- IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("user", out userName);
- IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("password", out password);
- return new NetworkCredential
- {
- UserName = userName,
- Password = password,
- };
+ if (_userName == null)
+ {
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("user", out _userName);
+ }
+ return _userName;
}
set
{
- var creds = value;
- if (creds != null)
- {
- IsolatedStorageSettings.ApplicationSettings["user"] = value.UserName;
- IsolatedStorageSettings.ApplicationSettings["password"] = value.Password;
- }
- else
+ _userName = value;
+ IsolatedStorageSettings.ApplicationSettings["user"] = _userName;
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ if (_password == null)
{
- IsolatedStorageSettings.ApplicationSettings["user"] = null;
- IsolatedStorageSettings.ApplicationSettings["password"] = null;
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("password", out _password);
}
+ return _password;
+ }
+ set
+ {
+ _password = value;
+ IsolatedStorageSettings.ApplicationSettings["password"] = value;
}
}
- public void SignIn(Page page, string login, string pass, Uri nextUri)
+ public bool IsAuthenticated
{
- Credentials = new NetworkCredential(login, pass);
- page.NavigationService.Navigate(nextUri);
- page.Dispatcher.BeginInvoke(() => page.NavigationService.RemoveBackEntry());
+ get {
+ bool authenticated;
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<bool>("authenticated", out authenticated);
+ return authenticated;
+ }
+ set { IsolatedStorageSettings.ApplicationSettings["authenticated"] = value; }
}
-
+
public void SignOut(Page page)
{
- Credentials = null;
+ IsAuthenticated = false;
page.NavigationService.Navigate(new Uri("/LoginView.xaml", UriKind.Relative));
page.Dispatcher.BeginInvoke(() => page.NavigationService.RemoveBackEntry());
}
diff --git a/Juick/Controls/MessageList.xaml b/Juick/Controls/MessageList.xaml
index 7a7d823..afa6c69 100644
--- a/Juick/Controls/MessageList.xaml
+++ b/Juick/Controls/MessageList.xaml
@@ -14,7 +14,7 @@
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot">
- <toolkit:LongListSelector x:Name="longList" Margin="0, 0, -12, 0"
+ <toolkit:LongListSelector Margin="0, 0, -12, 0"
ItemsSource="{Binding Items}" IsFlatList="true">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Link">
diff --git a/Juick/Juick.csproj b/Juick/Juick.csproj
index ebe128c..33fbd5c 100644
--- a/Juick/Juick.csproj
+++ b/Juick/Juick.csproj
@@ -103,9 +103,11 @@
<DependentUpon>ThreadView.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\AppViewModel.cs" />
+ <Compile Include="ViewModels\LoginViewModel.cs" />
<Compile Include="ViewModels\PageViewModel.cs" />
<Compile Include="ViewModels\PostItem.cs" />
<Compile Include="ViewModels\ThreadViewModel.cs" />
+ <Compile Include="ViewModels\Validation\DataViewModelBase.cs" />
<Compile Include="ViewModels\ViewModelBase.cs" />
</ItemGroup>
<ItemGroup>
diff --git a/Juick/LoginView.xaml b/Juick/LoginView.xaml
index ad1d70c..004580a 100644
--- a/Juick/LoginView.xaml
+++ b/Juick/LoginView.xaml
@@ -3,15 +3,19 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
- xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+ xmlns:phoneshell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:viewModels="clr-namespace:Juick.ViewModels"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
- shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
+ phoneshell:SystemTray.IsVisible="True">
+ <phone:PhoneApplicationPage.DataContext>
+ <viewModels:LoginViewModel />
+ </phone:PhoneApplicationPage.DataContext>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
@@ -28,24 +32,26 @@
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,119,0,0" Name="textBlock1" Text="Username" VerticalAlignment="Top" />
- <TextBox Height="72" HorizontalAlignment="Left" Margin="25,155,0,0" Name="textBox1" Text="{Binding UserName}" VerticalAlignment="Top" Width="409" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,246,0,0" Name="textBlock2" Text="Password" VerticalAlignment="Top" />
- <Button Content="Sign in" Height="72" HorizontalAlignment="Left" Margin="274,360,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
- <PasswordBox Height="72" HorizontalAlignment="Left" Password="{Binding Password}" Margin="25,282,0,0" Name="textBox2" VerticalAlignment="Top" Width="409" />
+ <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,119,0,0" Text="Username" VerticalAlignment="Top" />
+ <TextBox Height="72" HorizontalAlignment="Left" Margin="25,155,0,0" Text="{Binding Username, Mode=TwoWay}" VerticalAlignment="Top" Width="409" />
+ <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,246,0,0" Text="Password" VerticalAlignment="Top" />
+ <Button Content="Sign in" Height="72" HorizontalAlignment="Left" Margin="274,360,0,0"
+ VerticalAlignment="Top" Width="160" Command="{Binding SignInCommand}" />
+ <PasswordBox Height="72" HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}"
+ Margin="25,282,0,0" VerticalAlignment="Top" Width="409" />
</Grid>
</Grid>
<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
- <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
- <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
- <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
- <shell:ApplicationBar.MenuItems>
- <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
- <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
- </shell:ApplicationBar.MenuItems>
- </shell:ApplicationBar>
+ <phoneshell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
+ <phoneshell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
+ <phoneshell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
+ <phoneshell:ApplicationBar.MenuItems>
+ <phoneshell:ApplicationBarMenuItem Text="MenuItem 1"/>
+ <phoneshell:ApplicationBarMenuItem Text="MenuItem 2"/>
+ </phoneshell:ApplicationBar.MenuItems>
+ </phoneshell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
diff --git a/Juick/LoginView.xaml.cs b/Juick/LoginView.xaml.cs
index 098275b..06ac58c 100644
--- a/Juick/LoginView.xaml.cs
+++ b/Juick/LoginView.xaml.cs
@@ -1,5 +1,6 @@
using System.Linq;
using System.Windows;
+using Juick.ViewModels;
using Microsoft.Phone.Controls;
using System;
@@ -10,27 +11,18 @@ namespace Juick
public LoginView()
{
InitializeComponent();
- DataContext = App.AppContext.Account.Credentials;
+ Loaded += (sender, args) =>
+ {
+ while (NavigationService.BackStack.Any())
+ NavigationService.RemoveBackEntry();
+ };
}
- private Uri nextUri;
-
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
- nextUri = NavigationContext.QueryString.ContainsKey("FileId")
+ ((LoginViewModel)DataContext).NextUri = NavigationContext.QueryString.ContainsKey("FileId")
? new Uri(string.Format("/NewPostView.xaml?FileId={0}", NavigationContext.QueryString["FileId"]), UriKind.Relative)
: new Uri("/MainPage.xaml", UriKind.Relative);
}
-
- private void button1_Click(object sender, RoutedEventArgs e)
- {
- App.AppContext.Account.SignIn(this, textBox1.Text, textBox2.Password, nextUri);
- }
-
- private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
- {
- while (NavigationService.BackStack.Any())
- NavigationService.RemoveBackEntry();
- }
}
} \ No newline at end of file
diff --git a/Juick/MainPage.xaml b/Juick/MainPage.xaml
index 2e40d0f..45e1ff9 100644
--- a/Juick/MainPage.xaml
+++ b/Juick/MainPage.xaml
@@ -22,14 +22,19 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
- <controls:Pivot Title="juick" Grid.Row="1">
- <controls:PivotItem Header="My feed" Margin="0, -5, 0, 0">
- <usercontrols:MessageList x:Name="Home" />
- </controls:PivotItem>
- <controls:PivotItem Header="Last" Margin="0, -5, 0, 0">
- <usercontrols:MessageList x:Name="Last"/>
- </controls:PivotItem>
+ <controls:Pivot x:Name="Pages" Title="juick" Grid.Row="1" ItemsSource="{Binding Pages}">
+ <controls:Pivot.HeaderTemplate>
+ <DataTemplate>
+ <TextBlock Text="{Binding Caption}"></TextBlock>
+ </DataTemplate>
+ </controls:Pivot.HeaderTemplate>
+ <controls:Pivot.ItemTemplate>
+ <DataTemplate>
+ <usercontrols:MessageList />
+ </DataTemplate>
+ </controls:Pivot.ItemTemplate>
</controls:Pivot>
+
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<phoneshell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
diff --git a/Juick/MainPage.xaml.cs b/Juick/MainPage.xaml.cs
index a634e7f..1c76ee1 100644
--- a/Juick/MainPage.xaml.cs
+++ b/Juick/MainPage.xaml.cs
@@ -4,10 +4,12 @@ using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
+using Juick.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Notification;
using Microsoft.Phone.Shell;
using Juick.ViewModels;
+using RestSharp;
namespace Juick
{
@@ -23,7 +25,7 @@ namespace Juick
//string channelName = "JuickChannel"; // unused variable
InitializeComponent();
-
+
/* // Try to find the push channel.
pushChannel = HttpNotificationChannel.Find(channelName);
@@ -61,33 +63,32 @@ namespace Juick
}*/
- // Set the data context of the listbox control to the sample data
DataContext = App.AppContext;
- Home.DataContext = ((AppViewModel)DataContext).Pages[0];
- Last.DataContext = ((AppViewModel)DataContext).Pages[1];
+
Loaded += (o, args) =>
- {
- var progressIndicator = SystemTray.ProgressIndicator;
+ {
+ Pages.SelectedIndex = 0;
+ var progressIndicator = SystemTray.ProgressIndicator;
- if (progressIndicator != null)
- {
- return;
- }
+ if (progressIndicator != null)
+ {
+ return;
+ }
- progressIndicator = new ProgressIndicator();
+ progressIndicator = new ProgressIndicator();
- SystemTray.SetProgressIndicator(this, progressIndicator);
+ SystemTray.SetProgressIndicator(this, progressIndicator);
- Binding binding = new Binding("IsDataLoading") { Source = DataContext };
+ Binding binding = new Binding("IsDataLoading") { Source = DataContext };
- BindingOperations.SetBinding(
- progressIndicator, ProgressIndicator.IsVisibleProperty, binding);
+ BindingOperations.SetBinding(
+ progressIndicator, ProgressIndicator.IsVisibleProperty, binding);
- binding = new Binding("IsDataLoading") { Source = DataContext };
+ binding = new Binding("IsDataLoading") { Source = DataContext };
- BindingOperations.SetBinding(
- progressIndicator, ProgressIndicator.IsIndeterminateProperty, binding);
- };
+ BindingOperations.SetBinding(
+ progressIndicator, ProgressIndicator.IsIndeterminateProperty, binding);
+ };
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
@@ -102,7 +103,7 @@ namespace Juick
// Ensure that there is at least one key in the query string, and check
// whether the "FileId" key is present.
- navigateUri = string.IsNullOrEmpty(App.AppContext.Account.Credentials.UserName) ? loginUriPart : newPostUriPart;
+ navigateUri = App.AppContext.Account.IsAuthenticated ? newPostUriPart : loginUriPart;
if (queryStrings.ContainsKey("FileId"))
{
FileId = queryStrings["FileId"];
@@ -112,6 +113,11 @@ namespace Juick
{
NavigationService.Navigate(new Uri(navigateUri, UriKind.Relative));
}
+ App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.UserName, App.AppContext.Account.Password);
+ if (App.AppContext.Account.IsAuthenticated)
+ foreach (var page in ((AppViewModel)DataContext).Pages)
+ page.RefreshData();
+
}
void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
@@ -163,10 +169,11 @@ namespace Juick
private void ApplicationBarIconButtonClick(object sender, EventArgs e)
{
- App.AppContext.Pages[0].Items.Clear();
- App.AppContext.Pages[1].Items.Clear();
- App.AppContext.Pages[0].RefreshData();
- App.AppContext.Pages[1].RefreshData();
+ foreach (var page in App.AppContext.Pages)
+ {
+ page.Items.Clear();
+ page.RefreshData();
+ }
}
private void ApplicationBarMenuItemClick(object sender, EventArgs e)
diff --git a/Juick/NewPostView.xaml.cs b/Juick/NewPostView.xaml.cs
index 4f3cc06..dbc45c4 100644
--- a/Juick/NewPostView.xaml.cs
+++ b/Juick/NewPostView.xaml.cs
@@ -43,7 +43,7 @@ namespace Juick
// whether the "FileId" key is present.
if (queryStrings.ContainsKey("FileId"))
{
- App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.Credentials.UserName, App.AppContext.Account.Credentials.Password);
+ App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.UserName, App.AppContext.Account.Password);
// Retrieve the picture from the media library using the FileID
// passed to the application.
MediaLibrary library = new MediaLibrary();
diff --git a/Juick/ViewModels/LoginViewModel.cs b/Juick/ViewModels/LoginViewModel.cs
new file mode 100644
index 0000000..95528fe
--- /dev/null
+++ b/Juick/ViewModels/LoginViewModel.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Net;
+using System.Windows;
+using Juick.Classes;
+using Juick.ViewModels.Validation;
+using RestSharp;
+
+namespace Juick.ViewModels
+{
+ public class LoginViewModel : DataViewModelBase
+ {
+ private string _username;
+ private string _password;
+ private DelegateCommand<RoutedEventArgs> _signInCommand;
+
+ public string Username
+ {
+ get
+ {
+ return _username ?? (_username = App.AppContext.Account.UserName);
+ }
+ set
+ {
+ _username = value;
+ NotifyPropertyChanged("Username");
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ return _password ?? (_password = App.AppContext.Account.Password);
+ }
+ set
+ {
+ _password = value;
+ NotifyPropertyChanged("Password");
+ }
+ }
+
+ public Uri NextUri { get; set; }
+
+ public DelegateCommand<RoutedEventArgs> SignInCommand
+ {
+ get
+ {
+ return _signInCommand ?? (_signInCommand = new DelegateCommand<RoutedEventArgs>(CheckAuth, () => true));
+ }
+ }
+
+ public void CheckAuth(RoutedEventArgs e)
+ {
+ App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(Username, Password);
+ App.AppContext.Client.ExecuteAsync(new RestRequest("/post", Method.POST), response =>
+ {
+ if (response.StatusCode ==
+ HttpStatusCode.BadRequest)
+ {
+ App.AppContext.Account.UserName = Username;
+ App.AppContext.Account.Password = Password;
+ App.AppContext.Account.IsAuthenticated = true;
+ ((App)Application.Current).NavigateTo(NextUri, true);
+ }
+ else
+ {
+ AddError("Username", "Invalid username or password", false);
+ }
+ });
+ }
+ }
+}
diff --git a/Juick/ViewModels/PageViewModel.cs b/Juick/ViewModels/PageViewModel.cs
index 02f86ff..e398caf 100644
--- a/Juick/ViewModels/PageViewModel.cs
+++ b/Juick/ViewModels/PageViewModel.cs
@@ -31,11 +31,10 @@ namespace Juick.ViewModels
set
{
_restUri = value;
- RefreshData();
}
}
- public virtual string Caption { get { return "juick"; } }
+ public virtual string Caption { get; set; }
public ObservableCollection<PostItem> Items { get; private set; }
@@ -70,7 +69,6 @@ namespace Juick.ViewModels
}
var request = new RestRequest(requestUri);
- _context.Client.Authenticator = new HttpBasicAuthenticator(_context.Account.Credentials.UserName, _context.Account.Credentials.Password);
_context.Client.ExecuteAsync<List<Message>>(request, response =>
{
_context.IsDataLoading = false;
diff --git a/Juick/ViewModels/Validation/DataViewModelBase.cs b/Juick/ViewModels/Validation/DataViewModelBase.cs
new file mode 100644
index 0000000..dcbf34b
--- /dev/null
+++ b/Juick/ViewModels/Validation/DataViewModelBase.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Juick.ViewModels.Validation
+{
+ public class DataViewModelBase : ViewModelBase, IDataErrorInfo
+ {
+
+ private readonly Dictionary<String, List<String>> _errors =
+ new Dictionary<string, List<string>>();
+
+ // Adds the specified error to the errors collection if it is not already
+ // present, inserting it in the first position if isWarning is false.
+ public void AddError(string propertyName, string error, bool isWarning)
+ {
+ if (!_errors.ContainsKey(propertyName))
+ _errors[propertyName] = new List<string>();
+
+ if (_errors[propertyName].Contains(error)) return;
+ if (isWarning) _errors[propertyName].Add(error);
+ else _errors[propertyName].Insert(0, error);
+ }
+
+ // Removes the specified error from the errors collection if it is present.
+ public void RemoveError(string propertyName, string error)
+ {
+ if (!_errors.ContainsKey(propertyName) || !_errors[propertyName].Contains(error)) return;
+ _errors[propertyName].Remove(error);
+ if (_errors[propertyName].Count == 0) _errors.Remove(propertyName);
+ }
+
+ public string Error { get { throw new NotImplementedException(); } }
+
+ public string this[string columnName]
+ {
+ get
+ {
+ return (!_errors.ContainsKey(columnName) ? null :
+ String.Join(Environment.NewLine, _errors[columnName]));
+ }
+ }
+ }
+}