From de099bd3b50b8ea52b212b8d322626582c2648be Mon Sep 17 00:00:00 2001 From: Roy Ben-Shabat Date: Sun, 22 Apr 2018 13:35:22 +0300 Subject: Implemented new TangoIOC container & TangoMessenger. Got rid of MVVMLite libs ! Got rid of IShutdownRequestBlocker, IShutdownListener, IModuleRequestListener. Implemented IStudioViewModel & StudioViewModel. --- .../ViewModels/MainViewVM.cs | 28 ++++++++-------------- 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs index e8e7170b1..6a3b232d5 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs @@ -19,11 +19,11 @@ using Tango.BL; namespace Tango.MachineStudio.MachineDesigner.ViewModels { - public class MainViewVM : ViewModel, IModuleRequestListener + public class MainViewVM : StudioViewModel { private bool _isSaving; private INotificationProvider _notification; - + #region Properties @@ -156,8 +156,8 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels SaveCommand = new RelayCommand(Save, (x) => !_isSaving); AddIdsCommand = new RelayCommand(AddIds, (x) => !_isSaving && Configuration.IdsPacks.Count < 8); RemoveIdsCommand = new RelayCommand(RemoveIds, (x) => !_isSaving && SelectedIds != null); - SetVersionConfigurationCommand = new RelayCommand(SetVersionConfiguration,(x) => !_isSaving); - SetAsDefaultCommand = new RelayCommand(SetAsDefaultConfiguration,(x) => !_isSaving); + SetVersionConfigurationCommand = new RelayCommand(SetVersionConfiguration, (x) => !_isSaving); + SetAsDefaultCommand = new RelayCommand(SetAsDefaultConfiguration, (x) => !_isSaving); } #endregion @@ -615,7 +615,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels /// private void SetAsDefaultConfiguration() { - _notification.ShowModalDialog(async (vm) => + _notification.ShowModalDialog(async (vm) => { try { @@ -644,7 +644,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels _notification.ShowError(ex.Message); } - }, () => + }, () => { }); @@ -652,20 +652,12 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels #endregion - #region IModuleRequestListener + #region IStudioModuleVM - /// - /// Called when the request has been made. - /// - /// The module instance. - /// The arguments. - public void OnRequestModule(IStudioModule module, object args) + public override void OnModuleRequest(params object[] args) { - if (module is MachineDesignerModule) - { - SelectedMachine = Adapter.Machines.SingleOrDefault(x => x.Guid == (args as Machine).Guid); - } - } + SelectedMachine = Adapter.Machines.SingleOrDefault(x => x.Guid == (args[0] as Machine).Guid); + } #endregion } -- cgit v1.3.1 From 97e45f70267d961168b77b149022b94022e0e199 Mon Sep 17 00:00:00 2001 From: Roy Ben-Shabat Date: Tue, 24 Apr 2018 19:05:53 +0300 Subject: Working on reporting... --- Software/DB/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/Tango_log.ldf | Bin 1572864 -> 1572864 bytes Software/Graphics/bug.png | Bin 0 -> 5276 bytes .../ViewModels/MainViewVM.cs | 5 + .../Views/MainView.xaml | 4 +- .../Views/MainView.xaml.cs | 5 + .../Notifications/DialogViewVM.cs | 6 + .../Tango.MachineStudio.UI/Images/bug.png | Bin 0 -> 5276 bytes .../TFS/SystemInformationModel.cs | 19 ++ .../TFS/SystemInformationTemplate.cshtml | 70 ++++++ .../TFS/TeamFoundationServiceExtendedClient.cs | 160 +++++++++++++ .../TFS/TeamMembersProvider.cs | 19 ++ .../TFS/WorkItemValidationAttribute.cs | 32 +++ .../Tango.MachineStudio.UI.csproj | 22 ++ .../Tango.MachineStudio.UI/ViewModelLocator.cs | 4 + .../ViewModels/LoadingViewVM.cs | 14 +- .../ViewModels/MainViewVM.cs | 28 ++- .../ViewModels/ReportIssueViewVM.cs | 43 ++++ .../Tango.MachineStudio.UI/Views/MainView.xaml | 16 ++ .../Views/ReportIssueView.xaml | 92 ++++++++ .../Views/ReportIssueView.xaml.cs | 28 +++ .../Visual_Studio/Tango.CodeGeneration/Helper.cs | 13 +- .../Visual_Studio/Tango.Core/ExtendedObject.cs | 5 +- .../ExtensionMethods/BitmapExtensions.cs | 24 ++ .../Visual_Studio/Tango.Core/Tango.Core.csproj | 1 + Software/Visual_Studio/Tango.Logging/FileLogger.cs | 10 +- .../ExtensionMethods/FrameworkElementExtensions.cs | 18 ++ .../Tango.SharedUI/Rendering/RenderWindow.xaml | 10 + .../Tango.SharedUI/Rendering/RenderWindow.xaml.cs | 255 +++++++++++++++++++++ .../Tango.SharedUI/Tango.SharedUI.csproj | 8 + Software/Visual_Studio/Tango.SharedUI/ViewModel.cs | 21 +- .../Tango.TFS/ITeamFoundationServiceClient.cs | 26 +++ Software/Visual_Studio/Tango.TFS/Priority.cs | 5 + .../Tango.TFS/TeamFoundationServiceClient.cs | 210 ++++++++++++----- Software/Visual_Studio/Tango.TFS/TeamMember.cs | 5 + Software/Visual_Studio/Tango.TFS/WorkItem.cs | 148 ++++++++++-- 36 files changed, 1241 insertions(+), 85 deletions(-) create mode 100644 Software/Graphics/bug.png create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug.png create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamMembersProvider.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ReportIssueViewVM.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs create mode 100644 Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs create mode 100644 Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs create mode 100644 Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml create mode 100644 Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf index 3e52613c4..ebd7dc607 100644 Binary files a/Software/DB/Tango.mdf and b/Software/DB/Tango.mdf differ diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf index ba2a23609..9f1c3dcfe 100644 Binary files a/Software/DB/Tango_log.ldf and b/Software/DB/Tango_log.ldf differ diff --git a/Software/Graphics/bug.png b/Software/Graphics/bug.png new file mode 100644 index 000000000..ed4b8e6e6 Binary files /dev/null and b/Software/Graphics/bug.png differ diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs index 6a3b232d5..421f2daa7 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs @@ -140,6 +140,11 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels #region Constructors + public MainViewVM() + { + + } + /// /// Initializes a new instance of the class. /// diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml index 57f4d0eb9..79949405e 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml @@ -68,7 +68,7 @@ - + @@ -85,7 +85,7 @@ - + MACHINE DESIGNER diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml.cs index 1aa1a1961..67d42a350 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MainView.xaml.cs @@ -30,9 +30,14 @@ namespace Tango.MachineStudio.MachineDesigner.Views private bool _highlightShown; private DoubleAnimation _highlightAnimation; + public ColumnDefinition PanelColumnDefinition { get; set; } + public MainView() { InitializeComponent(); + + PanelColumnDefinition = panelColumnDefinition; + DraggingSurface = dragSufrace; this.Loaded += (x, y) => { diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Notifications/DialogViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Notifications/DialogViewVM.cs index 5fcd63071..c8d606efc 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Notifications/DialogViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Notifications/DialogViewVM.cs @@ -24,6 +24,7 @@ namespace Tango.MachineStudio.Common.Notifications { CanClose = true; CloseCommand = new RelayCommand(Cancel, (x) => CanClose); + OKCommand = new RelayCommand(Accept, (x) => CanClose); } private bool _canClose; @@ -41,6 +42,11 @@ namespace Tango.MachineStudio.Common.Notifications /// public RelayCommand CloseCommand { get; set; } + /// + /// Gets or sets the ok command. + /// + public RelayCommand OKCommand { get; set; } + /// /// Called when the dialog has been shown. /// diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug.png b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug.png new file mode 100644 index 000000000..ed4b8e6e6 Binary files /dev/null and b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug.png differ diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs new file mode 100644 index 000000000..eb9ef8012 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL.Entities; + +namespace Tango.MachineStudio.UI.TFS +{ + public class SystemInformationModel + { + public String ApplicationVersion { get; set; } + public String EmbeddedVersion { get; set; } + public String UserName { get; set; } + public String HostName { get; set; } + public Machine Machine { get; set; } + public String ConfigurationString { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml new file mode 100644 index 000000000..7b0c4a896 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml @@ -0,0 +1,70 @@ +@{ + Tango.MachineStudio.UI.TFS.SystemInformationModel vm = Model as Tango.MachineStudio.UI.TFS.SystemInformationModel; +} + +
+
System Information
+ + + + + + + + + + + + + + + + + + + + + + + + +
Application Version:@vm.ApplicationVersion
Embedded Version:@vm.EmbeddedVersion
Host Name:@vm.HostName
User:@vm.UserName
+ +
Machine
+ + @if (vm.Machine != null) + { + + + + + + + + + + + + + + + + + + + + + + + + +
Organization:@vm.Machine.Organization.Name
Name:@vm.Machine.Name
S/N:@vm.Machine.SerialNumber
Version:@vm.Machine.MachineVersion.Version
+ +
Configuration
+
@vm.ConfigurationString
+ } + else + { +
Not Connected
+ } +
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs new file mode 100644 index 000000000..27b257e61 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using Tango.BL.Entities; +using Tango.Core.DI; +using Tango.Core.Helpers; +using Tango.Integration.Operation; +using Tango.Logging; +using Tango.MachineStudio.Common.Authentication; +using Tango.MachineStudio.Common.StudioApplication; +using Tango.SharedUI.Helpers; +using Tango.TFS; + +namespace Tango.MachineStudio.UI.TFS +{ + public class TeamFoundationServiceExtendedClient : TeamFoundationServiceClient + { + public Project Project { get; private set; } + + private bool _isInitialized; + public bool IsInitialized + { + get { return _isInitialized; } + private set { _isInitialized = value; RaisePropertyChangedAuto(); } + } + + public TeamFoundationServiceExtendedClient(string collectionURL, string userName, string personalToken) : base(collectionURL, userName, personalToken) + { + + } + + public Task UploadWorkItem(WorkItem workItem) + { + return UploadWorkItem(Project, workItem); + } + + public void Initialize() + { + Task.Factory.StartNew(() => + { + if (!IsInitialized) + { + Project = GetProject("Tango").Result; + IsInitialized = true; + } + }); + } + + public WorkItem CreateBug() + { + WorkItem item = new WorkItem(); + + IAuthenticationProvider auth = TangoIOC.Default.GetInstance(); + IStudioApplicationManager app = TangoIOC.Default.GetInstance(); + + item.Area = Project.Areas.FirstOrDefault(); + item.Iteration = Project.Iterations.FirstOrDefault(); + + TeamMember currentUser = Project.Members.SingleOrDefault(x => x.UniqueName.ToLower().Contains(auth.CurrentUser.Email.ToLower())); + item.CreatedBy = currentUser; + item.ChangedBy = currentUser; + item.AuthorizedAs = currentUser; + + item.FoundInBuild = app.Version; + item.Priority = Priority.Priority3; + item.Severity = Severity.Medium; + item.State = State.New; + item.Type = WorkItemType.Bug; + + var bitmap = UIHelper.TakeSnapshot(MainWindow.Instance); + String filePath = PathHelper.GetTempFilePath(); + bitmap.SaveJpeg(filePath, 30); + + item.Attachments.Add(new Attachment() + { + Description = "Screen Capture", + FilePath = filePath, + Name = "Screen Capture.jpg", + }); + + FileLogger appFileLogger = LogManager.Default.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; + FileLogger embeddedFileLogger = MachineOperator.EmbeddedLogManager.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; + + if (appFileLogger != null) + { + String appLogFile = PathHelper.GetTempFilePath(); + File.Copy(appFileLogger.LogFile, appLogFile); + + item.Attachments.Add(new Attachment() + { + Description = "Application Log File", + FilePath = appLogFile, + Name = Path.GetFileName(appFileLogger.LogFile), + }); + } + + if (embeddedFileLogger != null && File.Exists(embeddedFileLogger.LogFile)) + { + String embeddedLogFile = PathHelper.GetTempFilePath(); + File.Copy(appFileLogger.LogFile, embeddedLogFile); + + item.Attachments.Add(new Attachment() + { + Description = "Embedded Log File", + FilePath = embeddedLogFile, + Name = Path.GetFileName(embeddedFileLogger.LogFile), + }); + } + + SystemInformationModel sysModel = new SystemInformationModel(); + sysModel.ApplicationVersion = app.Version; + sysModel.EmbeddedVersion = "Fake Version"; + sysModel.HostName = Environment.MachineName; + sysModel.UserName = auth.CurrentUser.Contact.FullName; + + if (app.ConnectedMachine != null) + { + Machine machine = app.ConnectedMachine.Machine; + + sysModel.Machine = machine; + + MachineDesigner.Views.MainView machineView = new MachineDesigner.Views.MainView(); + machineView.Width = 1280; + machineView.Height = 1100; + machineView.PanelColumnDefinition.Width = new System.Windows.GridLength(0); + machineView.stackHeader.Visibility = Visibility.Collapsed; + machineView.Background = System.Windows.Media.Brushes.White; + machineView.DataContext = new MachineDesigner.ViewModels.MainViewVM() { SelectedMachine = machine, Configuration = machine.Configuration }; + + String configImageFile = PathHelper.GetTempFilePath(); + machineView.RenderToFile(configImageFile, System.Drawing.Imaging.ImageFormat.Jpeg, new System.Windows.Size(machineView.Width, machineView.Height), 100); + + item.Attachments.Add(new Attachment() + { + Description = "Machine Configuration", + FilePath = configImageFile, + Name = "Machine Configuration.jpg" + }); + + sysModel.ConfigurationString = machine.Configuration.CloneConfiguration().ToJsonString(); + } + + String html = String.Empty; + + using (var stream = EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.MachineStudio.UI.TFS.SystemInformationTemplate.cshtml")) + { + StreamReader reader = new StreamReader(stream); + html = reader.ReadToEnd(); + } + + item.SystemInformation = CodeGeneration.Helper.Parse(html, sysModel); + + return item; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamMembersProvider.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamMembersProvider.cs new file mode 100644 index 000000000..0691ca6bd --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamMembersProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.AutoComplete.Editors; +using Tango.Core.DI; + +namespace Tango.MachineStudio.UI.TFS +{ + public class TeamMembersProvider : ISuggestionProvider + { + public IEnumerable GetSuggestions(string filter) + { + return TangoIOC.Default.GetInstance().Project.Members.Where(x => x.AssignName.ToLower().Contains(filter.ToLower())); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs new file mode 100644 index 000000000..1418d0576 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.TFS; + +namespace Tango.MachineStudio.UI.TFS +{ + public class WorkItemValidationAttribute : ValidationAttribute + { + public override bool IsValid(object value) + { + WorkItem item = value as WorkItem; + + if (String.IsNullOrWhiteSpace(item.Title)) + { + ErrorMessage = "Title is empty"; + return false; + } + + if (item.AssignedTo == null) + { + ErrorMessage = "Assigned To is empty"; + return false; + } + + return true; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj index 722d37d77..8ab6bb188 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj @@ -147,6 +147,10 @@ MessageBoxWindow.xaml + + + + @@ -156,6 +160,7 @@ + @@ -182,6 +187,9 @@ DialogWindow.xaml + + ReportIssueView.xaml + ShutdownView.xaml @@ -258,6 +266,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -319,6 +331,10 @@ {f441feee-322a-4943-b566-110e12fd3b72} Tango.BL + + {caedae94-11ed-473c-888a-268a6d38cd20} + Tango.CodeGeneration + {a34ee0f0-649d-41c8-8489-b6f1cc6924ee} Tango.Core @@ -347,6 +363,10 @@ {8491d07b-c1f6-4b62-a412-41b9fd2d6538} Tango.SharedUI + + {998f8471-dc1b-41b6-9d96-354e1b4e7a32} + Tango.TFS + {74e700b0-1156-4126-be40-ee450d3c3026} Tango.Transport @@ -440,6 +460,8 @@ + + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs index c4b8a1a59..8e6f11452 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs @@ -19,9 +19,11 @@ using Tango.MachineStudio.UI.Navigation; using Tango.MachineStudio.UI.Notifications; using Tango.MachineStudio.UI.StudioApplication; using Tango.MachineStudio.UI.SupervisingController; +using Tango.MachineStudio.UI.TFS; using Tango.MachineStudio.UI.ViewModels; using Tango.MachineStudio.UI.Views; using Tango.Settings; +using Tango.TFS; namespace Tango.MachineStudio.UI { @@ -60,7 +62,9 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); + TangoIOC.Default.Register(new TeamFoundationServiceExtendedClient("https://twinetfs.visualstudio.com/DefaultCollection", "Roy", "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa")); TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs index 512341f7e..2b2b442f7 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs @@ -15,6 +15,7 @@ using Tango.SharedUI; using Tango.BL; using Tango.MachineStudio.Common.EventLogging; using Tango.BL.Enumerations; +using Tango.MachineStudio.UI.TFS; namespace Tango.MachineStudio.UI.ViewModels { @@ -25,6 +26,7 @@ namespace Tango.MachineStudio.UI.ViewModels public class LoadingViewVM : ViewModel { private INotificationProvider _notificationProvider; + private TeamFoundationServiceExtendedClient _tfs; private INavigationManager _navigationManager; private IStudioModuleLoader _studioModuleLoader; private IEventLogger _eventLogger; @@ -45,8 +47,9 @@ namespace Tango.MachineStudio.UI.ViewModels /// The navigation manager. /// The studio module loader. /// The notification provider. - public LoadingViewVM(IStudioApplicationManager applicationManager, INavigationManager navigationManager, IStudioModuleLoader studioModuleLoader, INotificationProvider notificationProvider, IEventLogger eventLogger) + public LoadingViewVM(IStudioApplicationManager applicationManager, INavigationManager navigationManager, IStudioModuleLoader studioModuleLoader, INotificationProvider notificationProvider, IEventLogger eventLogger, TeamFoundationServiceExtendedClient teamFoundationClient) { + _tfs = teamFoundationClient; _eventLogger = eventLogger; ApplicationManager = applicationManager; _navigationManager = navigationManager; @@ -66,6 +69,15 @@ namespace Tango.MachineStudio.UI.ViewModels { try { + try + { + _tfs.Initialize(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Could not initialize Team Foundation Service client."); + } + ObservablesEntitiesAdapter.Instance.Initialize(); _eventLogger.Log(EventTypes.ApplicationStarted, "Application Started!"); diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs index 2144983de..0882267e8 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs @@ -29,6 +29,7 @@ using Tango.MachineStudio.Common.StudioApplication; using Tango.MachineStudio.Common.Update; using Tango.MachineStudio.UI.StudioApplication; using Tango.MachineStudio.UI.SupervisingController; +using Tango.MachineStudio.UI.TFS; using Tango.MachineStudio.UI.Views; using Tango.MachineStudio.UI.Windows; using Tango.PMR.Stubs; @@ -142,6 +143,11 @@ namespace Tango.MachineStudio.UI.ViewModels /// public RelayCommand ResolveMachineEventCommand { get; set; } + /// + /// Gets or sets the report issue command. + /// + public RelayCommand ReportIssueCommand { get; set; } + private IAuthenticationProvider _authenticationProvider; /// /// Gets or sets the authentication provider. @@ -182,6 +188,11 @@ namespace Tango.MachineStudio.UI.ViewModels set { _applicationManager = value; RaisePropertyChangedAuto(); } } + /// + /// Gets or sets the TFS client. + /// + public TeamFoundationServiceExtendedClient TFSClient { get; set; } + /// /// Gets or sets the speech provider. /// @@ -247,8 +258,9 @@ namespace Tango.MachineStudio.UI.ViewModels IEventLogger eventLogger, IDiagnosticsFrameProvider frameProvider, ISpeechProvider speechProvider, - IHtmlPresenter htmlPresenter) : base() + IHtmlPresenter htmlPresenter, TeamFoundationServiceExtendedClient tfs) : base() { + TFSClient = tfs; _eventLogger = eventLogger; _navigation = navigationManager; AuthenticationProvider = authenticationProvider; @@ -288,6 +300,8 @@ namespace Tango.MachineStudio.UI.ViewModels ToggleSpeechCommand = new RelayCommand(() => { SpeechProvider.Mute = !SpeechProvider.Mute; }); ResolveMachineEventCommand = new RelayCommand(ResolveMachineEvent); + + ReportIssueCommand = new RelayCommand(ReportIssue); } private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable e) @@ -628,5 +642,17 @@ namespace Tango.MachineStudio.UI.ViewModels _notificationProvider.ShowWarning("Could not locate guidance content for the specified event."); } } + + private void ReportIssue() + { + _notificationProvider.ShowModalDialog(new ReportIssueViewVM(TFSClient.Project, TFSClient.CreateBug()), async (vm) => + { + using (_notificationProvider.PushTaskItem("Uploading bug report...")) + { + await TFSClient.UploadWorkItem(vm.WorkItem); + } + + }, null); + } } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ReportIssueViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ReportIssueViewVM.cs new file mode 100644 index 000000000..9660db8a5 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ReportIssueViewVM.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.UI.TFS; +using Tango.SharedUI.Components; +using Tango.TFS; + +namespace Tango.MachineStudio.UI.ViewModels +{ + public class ReportIssueViewVM : DialogViewVM + { + public SelectedObjectCollection SelectedTags { get; set; } + + public Project Project { get; set; } + + [WorkItemValidation] + public WorkItem WorkItem { get; set; } + + public ReportIssueViewVM() : base() + { + + } + + public ReportIssueViewVM(Project project, WorkItem workItem) : this() + { + Project = project; + WorkItem = workItem; + SelectedTags = new SelectedObjectCollection(Project.Tags.ToObservableCollection(), new System.Collections.ObjectModel.ObservableCollection()); + } + + protected override void Accept() + { + if (Validate()) + { + WorkItem.Tags = SelectedTags.SynchedSource.ToList(); + base.Accept(); + } + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml index dd3d122b6..86723479b 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml @@ -316,6 +316,22 @@ + + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml new file mode 100644 index 000000000..f2e3038b9 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + Report Issue + + + + + + + + + + + + Tags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs new file mode 100644 index 000000000..91a288d3e --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +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 Tango.MachineStudio.UI.Views +{ + /// + /// Interaction logic for ReportIssueView.xaml + /// + public partial class ReportIssueView : UserControl + { + public ReportIssueView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs index dff85a011..dfbc893ee 100644 --- a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs +++ b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs @@ -13,7 +13,7 @@ namespace Tango.CodeGeneration /// /// Contains several code generation helper methods. /// - internal static class Helper + public static class Helper { /// /// Gets a code template by the code object type. @@ -55,6 +55,17 @@ namespace Tango.CodeGeneration return Engine.Razor.RunCompile(template, Guid.NewGuid().ToString(), model.GetType(), model).Replace("
", "").Replace("
", ""); } + /// + /// Parses the specified text. + /// + /// The text. + /// The model. + /// + public static String Parse(String text,object model) + { + return Engine.Razor.RunCompile(text, Guid.NewGuid().ToString(), model.GetType(), model); + } + /// /// Indents the c sharp code. /// diff --git a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs index be6302a4b..8ed7c5ecd 100644 --- a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs +++ b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; @@ -28,6 +29,7 @@ namespace Tango.Core /// /// Gets the default log manager. /// + [JsonIgnore] public LogManager LogManager { get { return LogManager.Default; } @@ -82,6 +84,7 @@ namespace Tango.Core /// Gets a value indicating whether we are currently in VS design mode. ///
[NotMapped] + [JsonIgnore] public bool DesignMode { get { return (DesignerProperties.GetIsInDesignMode(new DependencyObject())); } diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs new file mode 100644 index 000000000..3cca605e9 --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Threading.Tasks; + + +public static class BitmapExtensions +{ + /// + /// Saves the specified bitmap as a JPEG file with the specified quality (0-100). + /// + /// The bitmap. + /// The file path. + /// The quality. + public static void SaveJpeg(this Bitmap bitmap, String filePath, int quality) + { + var encoder = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid); + var encParams = new EncoderParameters() { Param = new[] { new EncoderParameter(Encoder.Quality, quality) } }; + bitmap.Save(filePath, encoder, encParams); + } +} + diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index e7172cc21..a9196237b 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -70,6 +70,7 @@ + diff --git a/Software/Visual_Studio/Tango.Logging/FileLogger.cs b/Software/Visual_Studio/Tango.Logging/FileLogger.cs index 215b1d9ac..4be7ab5c2 100644 --- a/Software/Visual_Studio/Tango.Logging/FileLogger.cs +++ b/Software/Visual_Studio/Tango.Logging/FileLogger.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -58,7 +59,14 @@ namespace Tango.Logging /// The output. public void OnLog(LogItemBase output) { - File.AppendAllText(LogFile, output.ToString() + Environment.NewLine); + try + { + File.AppendAllText(LogFile, output.ToString() + Environment.NewLine); + } + catch + { + Debug.WriteLine("Error Writing To Log File!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } } private bool _isEnabled; diff --git a/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs b/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs new file mode 100644 index 000000000..422b078a4 --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media.Imaging; +using Tango.SharedUI.Rendering; + +public static class FrameworkElementExtensions +{ + public static void RenderToFile(this FrameworkElement element, String filePath, ImageFormat format, Size renderSize, int quality) + { + RenderWindow renderer = new RenderWindow(element); + renderer.RenderToFileSynced(new Size(element.Width, element.Height), renderSize, filePath, format, quality, null); + } +} diff --git a/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml new file mode 100644 index 000000000..51a8b78ea --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs new file mode 100644 index 000000000..638bf7267 --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +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.Shapes; + +namespace Tango.SharedUI.Rendering +{ + /// + /// Represents an on window chart renderer. + /// + public partial class RenderWindow : Window + { + private Action _fileCallback; + private Action _bitmapCallback; + private Size _renderSize; + private ImageFormat _format; + private int _quality; + private String _filePath; + private FrameworkElement _element; + private bool renderToFile; + + /// + /// Initializes a new instance of the class. + /// + /// The element. + public RenderWindow(FrameworkElement element) + { + InitializeComponent(); + element.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + element.VerticalAlignment = System.Windows.VerticalAlignment.Top; + _element = element; + grid.Children.Add(_element); + } + + /// + /// Occurs when the window is completely loaded. + /// + /// The sender. + /// The instance containing the event data. + private void RenderWindow_ContentRendered(object sender, EventArgs e) + { + RenderTargetBitmap bmp = CreateBitmap(grid, new Size(_renderSize.Width, _renderSize.Height)); + + if (renderToFile) + { + using (FileStream fs = new FileStream(_filePath, FileMode.Create)) + { + BitmapEncoder encoder = GetEncoderByImageFormat(_format, _quality); + + encoder.Frames.Add(BitmapFrame.Create(bmp)); + encoder.Save(fs); + } + + if (_fileCallback != null) + { + _fileCallback(_filePath); + } + } + else + { + if (_bitmapCallback != null) + { + _bitmapCallback(bmp); + } + } + + this.Close(); + } + + /// + /// Renders to file. + /// + /// Size of the design. + /// Size of the render. + /// The file path. + /// The format. + /// The ready callback. + public void RenderToFile(Size designSize, Size renderSize, String filePath, ImageFormat format, Action readyCallback = null) + { + renderToFile = true; + _renderSize = renderSize; + _filePath = filePath; + _format = format; + _fileCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.Show(); + } + + /// + /// Renders to file. + /// + /// Size of the design. + /// Size of the render. + /// The file path. + /// The format. + /// The ready callback. + public void RenderToFileSynced(Size designSize, Size renderSize, String filePath, ImageFormat format, int quality, Action readyCallback = null) + { + renderToFile = true; + _renderSize = renderSize; + _filePath = filePath; + _format = format; + _quality = quality; + _fileCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.ShowDialog(); + } + + /// + /// Renders to bitmap. + /// + /// Size of the design. + /// Size of the render. + /// The ready callback. + public void RenderToBitmap(Size designSize, Size renderSize, Action readyCallback = null) + { + _renderSize = renderSize; + _bitmapCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.Show(); + } + + /// + /// Creates the bitmap. + /// + /// The o visual. + /// Size of the o. + /// + private RenderTargetBitmap CreateBitmap(Visual oVisual, Size oSize) + { + int nWidth = (int)Math.Ceiling(oSize.Width); + int nHeight = (int)Math.Ceiling(oSize.Height); + + RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap( + nWidth, + nHeight, + 96, + 96, + PixelFormats.Pbgra32 + ); + + DrawingVisual oDrawingVisual = new DrawingVisual(); + + using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen()) + { + VisualBrush oVisualBrush = new VisualBrush(oVisual) { Stretch = Stretch.Fill }; + + oDrawingContext.DrawRectangle( + oVisualBrush, + null, + new Rect( + new Point(), + new Size(nWidth, nHeight) + ) + ); + + oDrawingContext.Close(); + oTargetBitmap.Render(oDrawingVisual); + } + + return oTargetBitmap; + } + + /// + /// Gets the encoder by image format. + /// + /// The format. + /// + private static BitmapEncoder GetEncoderByImageFormat(ImageFormat format, int quality) + { + BitmapEncoder encoder = null; + + if (format == ImageFormat.Png) + { + encoder = new PngBitmapEncoder(); + } + else if (format == ImageFormat.Jpeg) + { + encoder = new JpegBitmapEncoder(); + (encoder as JpegBitmapEncoder).QualityLevel = quality; + } + else + { + encoder = new BmpBitmapEncoder(); + } + + return encoder; + } + } +} diff --git a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj index eeae87a91..fa5d02617 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj +++ b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj @@ -108,6 +108,7 @@ ParameterizedEditor.xaml + @@ -125,6 +126,9 @@ Settings.settings True + + RenderWindow.xaml + @@ -160,6 +164,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + diff --git a/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs b/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs index 13b9206cd..4418ca437 100644 --- a/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs +++ b/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -32,6 +33,16 @@ namespace Tango.SharedUI set { _hasErrors = value; RaisePropertyChangedAuto(); } } + private ObservableCollection _validationErrors; + /// + /// Gets or sets the validation errors. + /// + public ObservableCollection ValidationErrors + { + get { return _validationErrors; } + private set { _validationErrors = value; RaisePropertyChangedAuto(); } + } + /// /// Occurs when the validation errors have changed for a property or for the entire entity. /// @@ -69,6 +80,13 @@ namespace Tango.SharedUI HasErrors = false; _currentErrors.Clear(); + if (ValidationErrors == null) + { + ValidationErrors = new ObservableCollection(); + } + + ValidationErrors.Clear(); + foreach (var prop in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { foreach (var validation in prop.GetCustomAttributes()) @@ -77,6 +95,7 @@ namespace Tango.SharedUI { HasErrors = true; _currentErrors.Add(new KeyValuePair(prop.Name, validation.ErrorMessage)); + ValidationErrors.Add(validation.ErrorMessage); RaiseError(prop.Name); } } @@ -90,7 +109,7 @@ namespace Tango.SharedUI ///
protected virtual void OnValidating() { - + } /// diff --git a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs index 0f8ae5e82..51394f663 100644 --- a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs @@ -6,14 +6,40 @@ using System.Threading.Tasks; namespace Tango.TFS { + /// + /// Represents a VSTS (Visual Studio On-line) client implementation. + /// public interface ITeamFoundationServiceClient { + /// + /// Gets a team project by the specified name. + /// + /// The name. + /// Task GetProject(String name); + /// + /// Uploads a work item to the specified team project. + /// + /// The project. + /// The work item. + /// Task UploadWorkItem(Project project, WorkItem workItem); + /// + /// Gets a work item by the specified team project and work item id. + /// + /// The project. + /// The identifier. + /// Task GetWorkItem(Project project, int id); + /// + /// Deletes the specified work item. + /// + /// The project. + /// The identifier. + /// Task DeleteWorkItem(Project project, int id); } } diff --git a/Software/Visual_Studio/Tango.TFS/Priority.cs b/Software/Visual_Studio/Tango.TFS/Priority.cs index 88e2a0ca5..8a1e57b88 100644 --- a/Software/Visual_Studio/Tango.TFS/Priority.cs +++ b/Software/Visual_Studio/Tango.TFS/Priority.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,9 +9,13 @@ namespace Tango.TFS { public enum Priority { + [Description("Critical")] Priority1 = 1, + [Description("High")] Priority2 = 2, + [Description("Medium")] Priority3 = 3, + [Description("Low")] Priority4 = 4, } } diff --git a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs index 6bcea336e..e6390260e 100644 --- a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs @@ -12,11 +12,18 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; namespace Tango.TFS { - public class TeamFoundationServiceClient : ITeamFoundationServiceClient + /// + /// Represents a VSTS (Visual Studio On-line) client implementation. + /// + /// + public class TeamFoundationServiceClient : ExtendedObject, ITeamFoundationServiceClient { + #region Extension Fields Constants + private class ExtensionFields { public const String FOUND_IN_BUILD = "Microsoft.VSTS.Build.FoundIn"; @@ -26,12 +33,35 @@ namespace Tango.TFS public const String SYSTEM_INFO = "Microsoft.VSTS.TCM.SystemInfo"; } + #endregion + + #region Properties + + /// + /// Gets the default team collection URL. + /// public String CollectionURL { get; private set; } + /// + /// Gets the personal token used to authenticate against the service. https://docs.microsoft.com/en-us/vsts/accounts/use-personal-access-tokens-to-authenticate?view=vsts + /// public String PersonalToken { get; private set; } + /// + /// Gets the VSTS user name (optional). + /// public String UserName { get; private set; } + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The collection URL. + /// Name of the user. + /// The personal token. public TeamFoundationServiceClient(String collectionURL, String userName, String personalToken) { CollectionURL = collectionURL; @@ -39,6 +69,15 @@ namespace Tango.TFS PersonalToken = personalToken; } + #endregion + + #region Public Methods + + /// + /// Gets a team project by the specified name. + /// + /// The name. + /// public Task GetProject(string name) { return Task.Factory.StartNew(() => @@ -141,15 +180,18 @@ namespace Tango.TFS }); } + /// + /// Uploads a work item to the specified team project. + /// + /// The project. + /// The work item. + /// public Task UploadWorkItem(Project project, WorkItem workItem) { return Task.Factory.StartNew(() => { var connection = CreateConnection(); - //var projCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionURL)); - //WorkItemStore s = new WorkItemStore(projCollection, WorkItemStoreFlags.BypassRules); - WorkItemTrackingHttpClient witClient = connection.GetClient(); var patchDocument = new JsonPatchDocument(); @@ -157,56 +199,59 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Title), + Path = GetFieldNameForWrite(CoreField.Title), Value = workItem.Title, }); - patchDocument.Add(new JsonPatchOperation + if (!String.IsNullOrWhiteSpace(workItem.Description)) { - Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Description), - Value = workItem.Description, - }); + patchDocument.Add(new JsonPatchOperation + { + Operation = Operation.Add, + Path = GetFieldNameForWrite(CoreField.Description), + Value = workItem.Description, + }); + } patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.IterationPath), + Path = GetFieldNameForWrite(CoreField.IterationPath), Value = workItem.Iteration.Path, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AreaPath), + Path = GetFieldNameForWrite(CoreField.AreaPath), Value = workItem.Area.Path, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AssignedTo), + Path = GetFieldNameForWrite(CoreField.AssignedTo), Value = workItem.AssignedTo.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.CreatedBy), + Path = GetFieldNameForWrite(CoreField.CreatedBy), Value = workItem.CreatedBy.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.ChangedBy), + Path = GetFieldNameForWrite(CoreField.ChangedBy), Value = workItem.ChangedBy.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AuthorizedAs), + Path = GetFieldNameForWrite(CoreField.AuthorizedAs), Value = workItem.AuthorizedAs.AssignName, }); @@ -250,7 +295,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Tags), + Path = GetFieldNameForWrite(CoreField.Tags), Value = String.Join("; ", workItem.Tags.Select(x => x.Name)), }); } @@ -260,7 +305,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.STEPS_TO_REP), + Path = GetExtensionFieldNameForWrite(ExtensionFields.STEPS_TO_REP), Value = workItem.StepsToReproduce, }); } @@ -270,7 +315,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.FOUND_IN_BUILD), + Path = GetExtensionFieldNameForWrite(ExtensionFields.FOUND_IN_BUILD), Value = workItem.FoundInBuild, }); } @@ -278,28 +323,28 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.State), + Path = GetFieldNameForWrite(CoreField.State), Value = workItem.State.ToString(), }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.SEVERITY), + Path = GetExtensionFieldNameForWrite(ExtensionFields.SEVERITY), Value = workItem.Severity.ToDescription(), }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.PRIORITY), + Path = GetExtensionFieldNameForWrite(ExtensionFields.PRIORITY), Value = (int)workItem.Priority, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.SYSTEM_INFO), + Path = GetExtensionFieldNameForWrite(ExtensionFields.SYSTEM_INFO), Value = workItem.SystemInformation, }); @@ -312,27 +357,12 @@ namespace Tango.TFS }); } - private VssConnection CreateConnection() - { - VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken)); - return connection; - } - - private String GetFieldName(CoreField field) - { - return "System." + field.ToString(); - } - - private String GetFieldNameForPut(CoreField field) - { - return "/fields/System." + field.ToString(); - } - - private String GetExtensionFieldNameForPut(String extensionFieldName) - { - return "/fields/" + extensionFieldName; - } - + /// + /// Gets a work item by the specified team project and work item id. + /// + /// The project. + /// The identifier. + /// public Task GetWorkItem(Project project, int id) { return Task.Factory.StartNew(() => @@ -346,36 +376,36 @@ namespace Tango.TFS var item = witClient.GetWorkItemAsync(id, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.All).Result; workItem.ID = item.Id.Value; - workItem.Title = item.Fields[GetFieldName(CoreField.Title)].ToString(); - workItem.Description = TryGetField(item.Fields, GetFieldName(CoreField.Description)); + workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString(); + workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description)); workItem.Area = new Area() { - Path = item.Fields[GetFieldName(CoreField.AreaPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldName(CoreField.AreaPath)].ToString()), + Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()), }; workItem.Iteration = new Iteration() { - Path = item.Fields[GetFieldName(CoreField.IterationPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldName(CoreField.IterationPath)].ToString()), + Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()), }; - workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.AssignedTo))); - workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.CreatedBy))); - workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.ChangedBy))); - workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.AuthorizedAs))); + workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo))); + workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy))); + workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy))); + workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs))); - workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldName(CoreField.WorkItemType)].ToString()); + workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString()); workItem.URL = item.Url; - if (item.Fields.ContainsKey(GetFieldName(CoreField.Tags))) + if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags))) { - List tags = item.Fields[GetFieldName(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); + List tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList(); } workItem.FoundInBuild = item.Fields[ExtensionFields.FOUND_IN_BUILD].ToString(); - workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldName(CoreField.State)].ToString()); + workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString()); workItem.Severity = ParseEnumByDescription(item.Fields[ExtensionFields.SEVERITY].ToString()); @@ -389,6 +419,12 @@ namespace Tango.TFS }); } + /// + /// Deletes the specified work item. + /// + /// The project. + /// The identifier. + /// public Task DeleteWorkItem(Project project, int id) { return Task.Factory.StartNew(() => @@ -400,6 +436,56 @@ namespace Tango.TFS }); } + #endregion + + #region Private Methods + + /// + /// Creates connection to VSTS. + /// + /// + private VssConnection CreateConnection() + { + VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken)); + return connection; + } + + /// + /// Creates a string representation of a VSTS field when reading. + /// + /// The field. + /// + private String GetFieldNameForRead(CoreField field) + { + return "System." + field.ToString(); + } + + /// + /// Creates a string representation of a VSTS field when writing. + /// + /// The field. + /// + private String GetFieldNameForWrite(CoreField field) + { + return "/fields/System." + field.ToString(); + } + + /// + /// Creates a string representation of a VSTS extension field when writing. + /// + /// The field. + /// + private String GetExtensionFieldNameForWrite(String extensionFieldName) + { + return "/fields/" + extensionFieldName; + } + + /// + /// Returns a value of the specified field key if found. Otherwise return an empty string. + /// + /// The fields. + /// The key. + /// private String TryGetField(IDictionary fields, String key) { if (fields.ContainsKey(key)) @@ -410,6 +496,12 @@ namespace Tango.TFS return String.Empty; } + /// + /// Returns the specified enum type value by it's [Description] attribute. + /// + /// + /// The description. + /// private T ParseEnumByDescription(String description) { Dictionary values = new Dictionary(); @@ -421,5 +513,7 @@ namespace Tango.TFS return values[description]; } + + #endregion } } diff --git a/Software/Visual_Studio/Tango.TFS/TeamMember.cs b/Software/Visual_Studio/Tango.TFS/TeamMember.cs index 0d8d402f8..747d550ca 100644 --- a/Software/Visual_Studio/Tango.TFS/TeamMember.cs +++ b/Software/Visual_Studio/Tango.TFS/TeamMember.cs @@ -19,5 +19,10 @@ namespace Tango.TFS get { return String.Format("{0} <{1}>", DisplayName, UniqueName); } } + public override string ToString() + { + return AssignName; + } + } } diff --git a/Software/Visual_Studio/Tango.TFS/WorkItem.cs b/Software/Visual_Studio/Tango.TFS/WorkItem.cs index 305f7d864..98ac05191 100644 --- a/Software/Visual_Studio/Tango.TFS/WorkItem.cs +++ b/Software/Visual_Studio/Tango.TFS/WorkItem.cs @@ -4,31 +4,141 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; namespace Tango.TFS { - public class WorkItem + public class WorkItem : ExtendedObject { - public int ID { get; set; } - public String URL { get; set; } - public WorkItemType Type { get; set; } - public Area Area { get; set; } - public Iteration Iteration { get; set; } - public String Title { get; set; } - public String Description { get; set; } - public State State { get; set; } - public Severity Severity { get; set; } - public Priority Priority { get; set; } - public String StepsToReproduce { get; set; } - public String SystemInformation { get; set; } - public TeamMember AssignedTo { get; set; } - public TeamMember CreatedBy { get; set; } - public TeamMember ChangedBy { get; set; } - public TeamMember AuthorizedAs { get; set; } + private int _id; + public int ID + { + get { return _id; } + internal set { _id = value; RaisePropertyChangedAuto(); } + } + + private String _url; + public String URL + { + get { return _url; } + internal set { _url = value; RaisePropertyChangedAuto(); } + } + + private WorkItemType _type; + public WorkItemType Type + { + get { return _type; } + set { _type = value; RaisePropertyChangedAuto(); } + } + + private Area _area; + public Area Area + { + get { return _area; } + set { _area = value; RaisePropertyChangedAuto(); } + } + + private Iteration _iteration; + public Iteration Iteration + { + get { return _iteration; } + set { _iteration = value; RaisePropertyChangedAuto(); } + } + + private String _title; + public String Title + { + get { return _title; } + set { _title = value; RaisePropertyChangedAuto(); } + } + + private String _description; + public String Description + { + get { return _description; } + set { _description = value; RaisePropertyChangedAuto(); } + } + + private State _state; + public State State + { + get { return _state; } + set { _state = value; RaisePropertyChangedAuto(); } + } + + private Severity _severity; + public Severity Severity + { + get { return _severity; } + set { _severity = value; RaisePropertyChangedAuto(); } + } + + private Priority _priority; + public Priority Priority + { + get { return _priority; } + set { _priority = value; RaisePropertyChangedAuto(); } + } + + private String _stepsToReproduce; + public String StepsToReproduce + { + get { return _stepsToReproduce; } + set { _stepsToReproduce = value; RaisePropertyChangedAuto(); } + } + + private String _systemInformation; + public String SystemInformation + { + get { return _systemInformation; } + set { _systemInformation = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _assignedTo; + public TeamMember AssignedTo + { + get { return _assignedTo; } + set { _assignedTo = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _createdBy; + public TeamMember CreatedBy + { + get { return _createdBy; } + set { _createdBy = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _changedBy; + public TeamMember ChangedBy + { + get { return _changedBy; } + set { _changedBy = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _authorizedAs; + public TeamMember AuthorizedAs + { + get { return _authorizedAs; } + set { _authorizedAs = value; RaisePropertyChangedAuto(); } + } + public List Attachments { get; set; } + public List Tags { get; set; } - public WorkItem UserStory { get; set; } - public String FoundInBuild { get; set; } + + private WorkItem _userStory; + public WorkItem UserStory + { + get { return _userStory; } + set { _userStory = value; RaisePropertyChangedAuto(); } + } + + private String _foundInBuild; + public String FoundInBuild + { + get { return _foundInBuild; } + set { _foundInBuild = value; RaisePropertyChangedAuto(); } + } public WorkItem() { -- cgit v1.3.1