diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-12-18 06:05:35 +0200 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-12-18 06:05:35 +0200 |
| commit | 54ebb63d6bfca47737abd6766cf82245472a6aef (patch) | |
| tree | 42e5e7d64aa191916d4e40a32f646cb958f5812b /Software/Visual_Studio/StubsUtils | |
| parent | 86bbaee1a545f08dc7bfb4efe5f4696d6e4dccdd (diff) | |
| download | Tango-54ebb63d6bfca47737abd6766cf82245472a6aef.tar.gz Tango-54ebb63d6bfca47737abd6766cf82245472a6aef.zip | |
StubsUtils Win10 notification.
Diffstat (limited to 'Software/Visual_Studio/StubsUtils')
19 files changed, 872 insertions, 5 deletions
diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/Notification.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/Notification.cs new file mode 100644 index 000000000..58ab8aaa1 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/Notification.cs @@ -0,0 +1,113 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; +using System.Windows.Media.Animation; +using Notifications.Wpf.Utils; + +namespace Notifications.Wpf.Controls +{ + [TemplatePart(Name = "PART_CloseButton", Type = typeof(Button))] + public class Notification : ContentControl + { + private TimeSpan _closingAnimationTime = TimeSpan.Zero; + + public bool IsClosing { get; set; } + + public static readonly RoutedEvent NotificationCloseInvokedEvent = EventManager.RegisterRoutedEvent( + "NotificationCloseInvoked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Notification)); + + public static readonly RoutedEvent NotificationClosedEvent = EventManager.RegisterRoutedEvent( + "NotificationClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Notification)); + + static Notification() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(Notification), + new FrameworkPropertyMetadata(typeof(Notification))); + } + + public event RoutedEventHandler NotificationCloseInvoked + { + add { AddHandler(NotificationCloseInvokedEvent, value); } + remove { RemoveHandler(NotificationCloseInvokedEvent, value); } + } + + public event RoutedEventHandler NotificationClosed + { + add { AddHandler(NotificationClosedEvent, value); } + remove { RemoveHandler(NotificationClosedEvent, value); } + } + + public static bool GetCloseOnClick(DependencyObject obj) + { + return (bool)obj.GetValue(CloseOnClickProperty); + } + + public static void SetCloseOnClick(DependencyObject obj, bool value) + { + obj.SetValue(CloseOnClickProperty, value); + } + + public static readonly DependencyProperty CloseOnClickProperty = + DependencyProperty.RegisterAttached("CloseOnClick", typeof(bool), typeof(Notification), new FrameworkPropertyMetadata(false,CloseOnClickChanged)); + + private static void CloseOnClickChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + var button = dependencyObject as Button; + if (button == null) + { + return; + } + + var value = (bool)dependencyPropertyChangedEventArgs.NewValue; + + if (value) + { + button.Click += (sender, args) => + { + var notification = VisualTreeHelperExtensions.GetParent<Notification>(button); + notification?.Close(); + }; + } + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + var closeButton = GetTemplateChild("PART_CloseButton") as Button; + if (closeButton != null) + closeButton.Click += OnCloseButtonOnClick; + + var storyboards = Template.Triggers.OfType<EventTrigger>().FirstOrDefault(t => t.RoutedEvent == NotificationCloseInvokedEvent)?.Actions.OfType<BeginStoryboard>().Select(a => a.Storyboard); + _closingAnimationTime = new TimeSpan(storyboards?.Max(s => Math.Min((s.Duration.HasTimeSpan ? s.Duration.TimeSpan + (s.BeginTime ?? TimeSpan.Zero) : TimeSpan.MaxValue).Ticks, s.Children.Select(ch => ch.Duration.TimeSpan + (s.BeginTime ?? TimeSpan.Zero)).Max().Ticks)) ?? 0); + + } + + private void OnCloseButtonOnClick(object sender, RoutedEventArgs args) + { + var button = sender as Button; + if (button == null) return; + + button.Click -= OnCloseButtonOnClick; + Close(); + } + + //TODO: .NET40 + public async void Close() + { + if (IsClosing) + { + return; + } + + IsClosing = true; + + RaiseEvent(new RoutedEventArgs(NotificationCloseInvokedEvent)); + await Task.Delay(_closingAnimationTime); + RaiseEvent(new RoutedEventArgs(NotificationClosedEvent)); + } + } +} diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/NotificationArea.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/NotificationArea.cs new file mode 100644 index 000000000..7163e46f4 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/NotificationArea.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Threading; + +namespace Notifications.Wpf.Controls +{ + public class NotificationArea : Control + { + + + + public NotificationPosition Position + { + get { return (NotificationPosition)GetValue(PositionProperty); } + set { SetValue(PositionProperty, value); } + } + + // Using a DependencyProperty as the backing store for Position. This enables animation, styling, binding, etc... + public static readonly DependencyProperty PositionProperty = + DependencyProperty.Register("Position", typeof(NotificationPosition), typeof(NotificationArea), new PropertyMetadata(NotificationPosition.BottomRight)); + + + public int MaxItems + { + get { return (int)GetValue(MaxItemsProperty); } + set { SetValue(MaxItemsProperty, value); } + } + + public static readonly DependencyProperty MaxItemsProperty = + DependencyProperty.Register("MaxItems", typeof(int), typeof(NotificationArea), new PropertyMetadata(int.MaxValue)); + + private IList _items; + + public NotificationArea() + { + NotificationManager.AddArea(this); + } + + static NotificationArea() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NotificationArea), + new FrameworkPropertyMetadata(typeof(NotificationArea))); + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + var itemsControl = GetTemplateChild("PART_Items") as Panel; + _items = itemsControl?.Children; + } + +#if NET40 + public void Show(object content, TimeSpan expirationTime, Action onClick, Action onClose) +#else + public async void Show(object content, TimeSpan expirationTime, Action onClick, Action onClose) +#endif + { + var notification = new Notification + { + Content = content + }; + + notification.MouseLeftButtonDown += (sender, args) => + { + if (onClick != null) + { + onClick.Invoke(); + (sender as Notification)?.Close(); + } + }; + notification.NotificationClosed += (sender, args) => onClose?.Invoke(); + notification.NotificationClosed += OnNotificationClosed; + + if (!IsLoaded) + { + return; + } + + var w = Window.GetWindow(this); + var x = PresentationSource.FromVisual(w); + if (x == null) + { + return; + } + + lock (_items) + { + _items.Add(notification); + + if (_items.OfType<Notification>().Count(i => !i.IsClosing) > MaxItems) + { + _items.OfType<Notification>().First(i => !i.IsClosing).Close(); + } + } + +#if NET40 + DelayExecute(expirationTime, () => + { +#else + if (expirationTime == TimeSpan.MaxValue) + { + return; + } + await Task.Delay(expirationTime); +#endif + notification.Close(); +#if NET40 + }); +#endif + } + + private void OnNotificationClosed(object sender, RoutedEventArgs routedEventArgs) + { + var notification = sender as Notification; + _items.Remove(notification); + } + +#if NET40 + private static void DelayExecute(TimeSpan delay, Action actionToExecute) + { + if (actionToExecute != null) + { + var timer = new DispatcherTimer + { + Interval = delay + }; + timer.Tick += (sender, args) => + { + timer.Stop(); + actionToExecute(); + }; + timer.Start(); + } + } +#endif + } + + public enum NotificationPosition + { + TopLeft, + TopRight, + BottomLeft, + BottomRight + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/ReversibleStackPanel.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/ReversibleStackPanel.cs new file mode 100644 index 000000000..f13126a29 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Controls/ReversibleStackPanel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace Notifications.Wpf.Controls +{ + public class ReversibleStackPanel : StackPanel + { + + + public bool ReverseOrder + { + get { return (bool)GetValue(ReverseOrderProperty); } + set { SetValue(ReverseOrderProperty, value); } + } + + public static readonly DependencyProperty ReverseOrderProperty = + DependencyProperty.Register("ReverseOrder", typeof(bool), typeof(ReversibleStackPanel), new PropertyMetadata(false)); + + + protected override Size ArrangeOverride(Size arrangeSize) + { + double x = 0; + double y = 0; + + IEnumerable<UIElement> children = ReverseOrder ? InternalChildren.Cast<UIElement>().Reverse() : InternalChildren.Cast<UIElement>(); + foreach (UIElement child in children) + { + Size size; + + if (Orientation == Orientation.Horizontal) + { + size = new Size(child.DesiredSize.Width, Math.Max(arrangeSize.Height, child.DesiredSize.Height)); + child.Arrange(new Rect(new Point(x, y), size)); + x += size.Width; + } + else + { + size = new Size(Math.Max(arrangeSize.Width, child.DesiredSize.Width), child.DesiredSize.Height); + child.Arrange(new Rect(new Point(x, y), size)); + y += size.Height; + } + } + + return Orientation == Orientation.Horizontal ? new Size(x, arrangeSize.Height) : new Size(arrangeSize.Width, y); + } + } +} diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/INotificationManager.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/INotificationManager.cs new file mode 100644 index 000000000..736a46e2b --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/INotificationManager.cs @@ -0,0 +1,9 @@ +using System; + +namespace Notifications.Wpf +{ + public interface INotificationManager + { + void Show(object content, string areaName = "", TimeSpan? expirationTime = null, Action onClick = null, Action onClose = null); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationContent.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationContent.cs new file mode 100644 index 000000000..3b942374b --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationContent.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Notifications.Wpf +{ + public class NotificationContent + { + public string Title { get; set; } + public string Message { get; set; } + + public NotificationType Type { get; set; } + } + + public enum NotificationType + { + Information, + Success, + Warning, + Error + } +} diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationManager.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationManager.cs new file mode 100644 index 000000000..0e8091e8a --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationManager.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Threading; +using Notifications.Wpf.Controls; + +namespace Notifications.Wpf +{ + public class NotificationManager : INotificationManager + { + private readonly Dispatcher _dispatcher; + private static readonly List<NotificationArea> Areas = new List<NotificationArea>(); + private static NotificationsOverlayWindow _window; + + public NotificationManager(Dispatcher dispatcher = null) + { + if (dispatcher == null) + { + dispatcher = Application.Current?.Dispatcher ?? Dispatcher.CurrentDispatcher; + } + + _dispatcher = dispatcher; + } + + public void Show(object content, string areaName = "", TimeSpan? expirationTime = null, Action onClick = null, + Action onClose = null) + { + if (!_dispatcher.CheckAccess()) + { + _dispatcher.BeginInvoke( + new Action(() => Show(content, areaName, expirationTime, onClick, onClose))); + return; + } + + if (expirationTime == null) expirationTime = TimeSpan.FromSeconds(5); + + if (areaName == string.Empty && _window == null) + { + var workArea = SystemParameters.WorkArea; + + _window = new NotificationsOverlayWindow + { + Left = workArea.Left, + Top = workArea.Top, + Width = workArea.Width, + Height = workArea.Height + }; + + _window.Show(); + } + + foreach (var area in Areas.Where(a => a.Name == areaName)) + { + area.Show(content, (TimeSpan) expirationTime, onClick, onClose); + } + } + + internal static void AddArea(NotificationArea area) + { + Areas.Add(area); + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationTemplateSelector.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationTemplateSelector.cs new file mode 100644 index 000000000..aab11a7fa --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationTemplateSelector.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace Notifications.Wpf +{ + public class NotificationTemplateSelector : DataTemplateSelector + { + private DataTemplate _defaultStringTemplate; + private DataTemplate _defaultNotificationTemplate; + + private void GetTemplatesFromResources(FrameworkElement container) + { + _defaultStringTemplate = + container?.FindResource("DefaultStringTemplate") as DataTemplate; + _defaultNotificationTemplate = + container?.FindResource("DefaultNotificationTemplate") as DataTemplate; + } + + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (_defaultStringTemplate == null && _defaultNotificationTemplate == null) + { + GetTemplatesFromResources((FrameworkElement)container); + } + + if (item is string) + { + return _defaultStringTemplate; + } + if (item is NotificationContent) + { + return _defaultNotificationTemplate; + } + + return base.SelectTemplate(item, container); + + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.csproj b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.csproj new file mode 100644 index 000000000..0e5db5694 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.csproj @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{5C9A4F46-263D-4C23-B361-F09E14BB109E}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Notifications.Wpf</RootNamespace> + <AssemblyName>Notifications.Wpf</AssemblyName> + <TargetFrameworkVersion>v4.6.1</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> + <PlatformTarget>AnyCPU</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.0|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <DefineConstants>TRACE;NET40</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <OutputPath>bin\Release\net40\</OutputPath> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <DefineConstants>TRACE;NET45</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <OutputPath>bin\Release\net45\</OutputPath> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.6.1|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <DefineConstants>TRACE;NET461</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <OutputPath>bin\Release\net461\</OutputPath> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + </PropertyGroup> + <ItemGroup> + <Reference Include="PresentationCore" /> + <Reference Include="PresentationFramework" /> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xaml" /> + <Reference Include="System.Xml" /> + <Reference Include="WindowsBase" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Controls\Notification.cs" /> + <Compile Include="Controls\NotificationArea.cs" /> + <Compile Include="Controls\ReversibleStackPanel.cs" /> + <Compile Include="INotificationManager.cs" /> + <Compile Include="NotificationContent.cs" /> + <Compile Include="NotificationManager.cs" /> + <Compile Include="NotificationTemplateSelector.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="NotificationsOverlayWindow.xaml.cs"> + <DependentUpon>NotificationsOverlayWindow.xaml</DependentUpon> + </Compile> + <Compile Include="Utils\VisualTreeHelperExtensions.cs" /> + </ItemGroup> + <ItemGroup> + <Page Include="Themes\Generic.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Page Include="NotificationsOverlayWindow.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + </ItemGroup> + <ItemGroup> + <None Include="Notifications.Wpf.nuspec" /> + <None Include="pack.bat" /> + </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"> + <GetAssemblyIdentity AssemblyFiles="$(OutputPath)$(AssemblyName).dll"> + <Output TaskParameter="Assemblies" ItemName="OutputAssemblyInfo" /> + </GetAssemblyIdentity> + <Message Text="Info: %(OutputAssemblyInfo.Version)" /> + </Target> + <Target Name="Package"> + <!-- Ensure the Package directory exists for this project --> + <RemoveDir Directories="NuGet" /> + <MakeDir Directories="NuGet" /> + <!-- Package the project --> + <Exec WorkingDirectory="$(BuildDir)" Command="NuGet.exe pack -OutputDir "NuGet" -Properties "Configuration=$(Configuration)"" /> + </Target> +</Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.nuspec b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.nuspec new file mode 100644 index 000000000..c4f36a497 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Notifications.Wpf.nuspec @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<package > + <metadata> + <id>$id$</id> + <version>$version$</version> + <title>$title$</title> + <authors>$author$</authors> + <owners>$author$</owners> + <projectUrl>https://github.com/Federerer/Notifications.Wpf</projectUrl> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <description>$description$</description> + <releaseNotes>First release.</releaseNotes> + <copyright>Copyright 2017</copyright> + <tags>Toast Notifications WPF</tags> + </metadata> + <files> + <file src="bin\Release\net40\$id$.dll" target="lib\net40\" /> + <file src="bin\Release\net45\$id$.dll" target="lib\net45\" /> + <file src="bin\Release\net461\$id$.dll" target="lib\net461\" /> + </files> +</package>
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml new file mode 100644 index 000000000..1c80f11ec --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml @@ -0,0 +1,18 @@ +<Window x:Class="Notifications.Wpf.NotificationsOverlayWindow" + 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:controls1="clr-namespace:Notifications.Wpf.Controls" + mc:Ignorable="d" + Title="ToastWindow" + Background="Transparent" + ShowInTaskbar="False" + WindowStyle="None" + AllowsTransparency="True" + Topmost="True" + ShowActivated="False" + d:DesignWidth="900" + d:DesignHeight="600"> + <controls1:NotificationArea Position="BottomRight" Margin="8"/> +</Window> diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml.cs new file mode 100644 index 000000000..ecb595694 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/NotificationsOverlayWindow.xaml.cs @@ -0,0 +1,16 @@ +using System.Windows; + +namespace Notifications.Wpf +{ + /// <summary> + /// Interaction logic for ToastWindow.xaml + /// </summary> + public partial class NotificationsOverlayWindow : Window + { + public NotificationsOverlayWindow() + { + InitializeComponent(); + } + + } +} diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Properties/AssemblyInfo.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d0022abe6 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +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("Notifications.Wpf")] +[assembly: AssemblyDescription("Toast notifications for WPF")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Adrian Gaś")] +[assembly: AssemblyProduct("Notifications.Wpf")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] + +// 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("5c9a4f46-263d-4c23-b361-f09e14bb109e")] + +// 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: AssemblyInformationalVersion("0.1.0")] +[assembly: AssemblyVersion("0.1.0")] +[assembly: AssemblyFileVersion("0.1.0")]
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Themes/Generic.xaml b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Themes/Generic.xaml new file mode 100644 index 000000000..76de0e6d7 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Themes/Generic.xaml @@ -0,0 +1,156 @@ +<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Notifications.Wpf.Controls" + xmlns:system="clr-namespace:System;assembly=mscorlib" + xmlns:wpf="clr-namespace:Notifications.Wpf"> + + <Path x:Key="ErrorIcon" Data="M 2.6874995 2.8246546e-7 -4.7683716e-7 2.6855503 5.3007795 7.9863303 -4.7683716e-7 13.28516 2.6874995 15.97266 l 5.29883 -5.30079 5.3007805 5.30079 2.68554 -2.6875 -5.29882 -5.2988297 5.29882 -5.30078 L 13.28711 2.8246546e-7 7.9863295 5.2988303 2.6874995 2.8246546e-7 Z" Fill="White" x:Shared="False"/> + <Path x:Key="SuccessIcon" Data="M 15.56055 5.9323048e-7 7.53125 10.197261 2.73242 5.2304706 0 7.8710906 7.82422 15.968751 18.54492 2.3515606 15.56055 5.9323048e-7 Z" Fill="White" x:Shared="False"/> + <Path x:Key="InfoIcon" Data="M 10.968748 8.9809305e-8 C 4.9320181 8.9809305e-8 -1.9073487e-6 4.9320201 -1.9073487e-6 10.96875 c 0 6.03672 4.9322600073487 10.9668 10.9687499073487 10.9668 6.03648 0 10.96875 -4.93008 10.96875 -10.9668 C 21.937498 4.9320201 17.005458 8.9809305e-8 10.968748 8.9809305e-8 Z m 0 2.000000010190695 c 4.95043 0 8.96875 4.0183 8.96875 8.9687499 0 4.95044 -4.01809 8.9668 -8.96875 8.9668 -4.9506899 0 -8.9687499 -4.01636 -8.9687499 -8.9668 0 -4.9504499 4.0183 -8.9687499 8.9687499 -8.9687499 z m -1.4999999 2.49805 0 3 2.9999999 0 0 -3 -2.9999999 0 z m 0 4.4707 0 8.9101599 2.9999999 0 0 -8.9101599 -2.9999999 0 z" Fill="White" x:Shared="False"/> + <Path x:Key="WarningIcon" Data="M 12.414089 4.6396565e-7 C 12.128679 -9.5360343e-6 11.832699 0.06810046 11.574249 0.19726046 c -0.29252 0.14627 -0.55012 0.39584 -0.70899 0.67383 l -0.002 0.002 L 0.22067905 19.77348 l -0.0117 0.0234 C 0.08326905 20.04831 -9.5367432e-7 20.33976 -9.5367432e-7 20.64844 c 0 0.30629 0.0851000036743 0.62597 0.23633000367432 0.89063 0.13469 0.2357 0.31957 0.44504 0.5332 0.60937 l 0.0137 0.0117 0.0156 0.01 C 1.076789 22.36867 1.440719 22.48654 1.785149 22.48654 l 21.28516 0 c 0.3398 0 0.70907 -0.12364 0.98828 -0.33398 0.2208 -0.16158 0.42089 -0.3689 0.56055 -0.61328 0.15122 -0.26466 0.23633 -0.58434 0.23633 -0.89063 0 -0.30868 -0.0852 -0.60013 -0.21094 -0.85156 l -0.01 -0.0234 -10.66992 -18.90038954 -0.002 -0.002 c -0.15887 -0.27808 -0.41633 -0.52756 -0.70899 -0.67383 -0.25845 -0.12918 -0.55443 -0.19726999603 -0.83984 -0.19725999603 z m 0 2.19531003603435 10.32617 18.2910095 -20.625 0 10.29883 -18.2910095 z m -1.48633 3.84765 0 8.9121095 3 0 0 -8.9121095 -3 0 z m 0 10.3808595 0 3 3 0 0 -3 -3 0 z" Fill="White" x:Shared="False"/> + + <Style TargetType="Button" x:Key="CloseButtonStyle"> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Button"> + <TextBlock Text="⛌" FontSize="12" /> + </ControlTemplate> + </Setter.Value> + </Setter> + <Setter Property="HorizontalAlignment" Value="Right"/> + <Setter Property="VerticalAlignment" Value="Top"/> + </Style> + + <wpf:NotificationTemplateSelector x:Key="NotificationTemplateSelector"/> + + <ControlTemplate x:Key="NotificationTemplate" TargetType="{x:Type controls:Notification}"> + <ControlTemplate.Resources> + <DataTemplate DataType="{x:Type system:String}" x:Key="DefaultStringTemplate"> + <Border Background="{Binding RelativeSource={RelativeSource AncestorType=controls:Notification}, Path=Background}" MinHeight="80"> + <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding}"/> + </Border> + </DataTemplate> + <DataTemplate DataType="{x:Type wpf:NotificationContent}" x:Key="DefaultNotificationTemplate"> + <Border x:Name="Border" Padding="12" MinHeight="80"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> + <ContentControl Margin="0,0,12,0" Width="25" Height="25" VerticalAlignment="Top"> + <ContentControl x:Name="Icon" HorizontalAlignment="Center" VerticalAlignment="Center"/> + </ContentControl> + <DockPanel Grid.Column="1"> + <TextBlock DockPanel.Dock="Top" Text="{Binding Title}" FontWeight="Medium" TextTrimming="CharacterEllipsis" /> + <TextBlock Text="{Binding Message}" TextWrapping="Wrap" Opacity=".8" Margin="0,0,12,0"/> + </DockPanel> + </Grid> + </Border> + <DataTemplate.Triggers> + <DataTrigger Binding="{Binding Type}" Value="Information"> + <Setter TargetName="Icon" Property="Content" Value="{StaticResource InfoIcon}"/> + <Setter TargetName="Border" Property="Background" Value="CornflowerBlue"/> + </DataTrigger> + <DataTrigger Binding="{Binding Type}" Value="Success"> + <Setter TargetName="Icon" Property="Content" Value="{StaticResource SuccessIcon}"/> + <Setter TargetName="Border" Property="Background" Value="#353535"/> + </DataTrigger> + <DataTrigger Binding="{Binding Type}" Value="Warning"> + <Setter TargetName="Icon" Property="Content" Value="{StaticResource WarningIcon}"/> + <Setter TargetName="Border" Property="Background" Value="Orange"/> + </DataTrigger> + <DataTrigger Binding="{Binding Type}" Value="Error"> + <Setter TargetName="Icon" Property="Content" Value="{StaticResource ErrorIcon}"/> + <Setter TargetName="Border" Property="Background" Value="OrangeRed"/> + </DataTrigger> + </DataTemplate.Triggers> + </DataTemplate> + </ControlTemplate.Resources> + <Border Background="{TemplateBinding Background}" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" + Margin="8,8,0,0"> + <Grid> + <ContentPresenter/> + <Button x:Name="PART_CloseButton" Style="{StaticResource CloseButtonStyle}" Margin="12" Foreground="{TemplateBinding Foreground}" Opacity=".8"/> + </Grid> + </Border> + <ControlTemplate.Triggers> + <EventTrigger RoutedEvent="Loaded"> + <BeginStoryboard> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:.5" /> + <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleX" From="0" To="1" Duration="0:0:.2"/> + <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="0" To="1" Duration="0:0:.2"/> + </Storyboard> + </BeginStoryboard> + </EventTrigger> + <EventTrigger RoutedEvent="controls:Notification.NotificationCloseInvoked" > + <BeginStoryboard> + <Storyboard Duration="0:0:.1"> + <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleX" From="1" To="0.1" Duration="0:0:.2"/> + </Storyboard> + </BeginStoryboard> + <BeginStoryboard> + <Storyboard BeginTime="0:0:.2"> + <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="1" To="0" Duration="0:0:.2"/> + </Storyboard> + </BeginStoryboard> + </EventTrigger> + </ControlTemplate.Triggers> + </ControlTemplate> + + <Style TargetType="{x:Type controls:Notification}"> + <!--<Setter Property="Height" Value="100"/>--> + <Setter Property="UseLayoutRounding" Value="True"/> + <Setter Property="SnapsToDevicePixels" Value="True"/> + <Setter Property="Width" Value="350"/> + <Setter Property="FontSize" Value="14"></Setter> + <Setter Property="Background" Value="#444444"/> + <Setter Property="Foreground" Value="White"/> + <Setter Property="Template" Value="{StaticResource NotificationTemplate}"/> + <Setter Property="ContentTemplateSelector" Value="{StaticResource NotificationTemplateSelector}"/> + <Setter Property="LayoutTransform"> + <Setter.Value> + <ScaleTransform /> + </Setter.Value> + </Setter> + <Setter Property="Effect"> + <Setter.Value> + <DropShadowEffect BlurRadius="5" Direction="0" ShadowDepth="0" Opacity=".25"/> + </Setter.Value> + </Setter> + </Style> + + <Style TargetType="{x:Type controls:NotificationArea}"> + <Setter Property="Margin" Value="0,0,8,8"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type controls:NotificationArea}"> + <controls:ReversibleStackPanel x:Name="PART_Items"/> + <ControlTemplate.Triggers> + <Trigger Property="Position" Value="TopLeft"> + <Setter TargetName="PART_Items" Property="VerticalAlignment" Value="Top"/> + <Setter TargetName="PART_Items" Property="HorizontalAlignment" Value="Left"/> + </Trigger> + <Trigger Property="Position" Value="TopRight"> + <Setter TargetName="PART_Items" Property="VerticalAlignment" Value="Top"/> + <Setter TargetName="PART_Items" Property="HorizontalAlignment" Value="Right"/> + </Trigger> + <Trigger Property="Position" Value="BottomLeft"> + <Setter TargetName="PART_Items" Property="ReverseOrder" Value="True"/> + <Setter TargetName="PART_Items" Property="VerticalAlignment" Value="Bottom"/> + <Setter TargetName="PART_Items" Property="HorizontalAlignment" Value="Left"/> + </Trigger> + <Trigger Property="Position" Value="BottomRight"> + <Setter TargetName="PART_Items" Property="ReverseOrder" Value="True"/> + <Setter TargetName="PART_Items" Property="VerticalAlignment" Value="Bottom"/> + <Setter TargetName="PART_Items" Property="HorizontalAlignment" Value="Right"/> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + +</ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Utils/VisualTreeHelperExtensions.cs b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Utils/VisualTreeHelperExtensions.cs new file mode 100644 index 000000000..e930e92da --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/Utils/VisualTreeHelperExtensions.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.Media; + +namespace Notifications.Wpf.Utils +{ + internal class VisualTreeHelperExtensions + { + public static T GetParent<T>(DependencyObject child) where T : DependencyObject + { + var parent = VisualTreeHelper.GetParent(child); + + if (parent == null) return null; + + var tParent = parent as T; + if (tParent != null) + { + return tParent; + } + + return GetParent<T>(parent); + } + } +} diff --git a/Software/Visual_Studio/StubsUtils/Notifications.Wpf/pack.bat b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/pack.bat new file mode 100644 index 000000000..59c6be7d2 --- /dev/null +++ b/Software/Visual_Studio/StubsUtils/Notifications.Wpf/pack.bat @@ -0,0 +1,3 @@ +msbuild /p:Configuration="Release 4.0" +msbuild /p:Configuration="Release 4.5" +msbuild /t:Build;Package /p:Configuration="Release 4.6.1"
\ No newline at end of file diff --git a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/MainWindow.xaml b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/MainWindow.xaml index f7113076c..4fab80302 100644 --- a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/MainWindow.xaml +++ b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/MainWindow.xaml @@ -8,7 +8,7 @@ xmlns:tb="http://www.hardcodet.net/taskbar" xmlns:views="clr-namespace:Tango.StubsUtils.Service.UI.Views" mc:Ignorable="d" - Title="Tango - Stubs Service" Height="400" Width="700" EnableDWMDropShadow="True" WindowTitleBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" OverrideDefaultWindowCommandsBrush="{StaticResource FSE_PrimaryForegroundBrush}" TitleForeground="{StaticResource FSE_PrimaryForegroundBrush}" TitleCaps="False" BorderThickness="1" WindowStartupLocation="CenterScreen" Background="{StaticResource FSE_PrimaryBackgroundBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" DataContext="{Binding RelativeSource={RelativeSource Self}}" Visibility="Hidden"> + Title="Tango Stubs Service" Height="400" Width="700" EnableDWMDropShadow="True" WindowTitleBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderBrush="{StaticResource FSE_PrimaryBackgroundLightBrush}" OverrideDefaultWindowCommandsBrush="{StaticResource FSE_PrimaryForegroundBrush}" TitleForeground="{StaticResource FSE_PrimaryForegroundBrush}" TitleCaps="False" BorderThickness="1" WindowStartupLocation="CenterScreen" Background="{StaticResource FSE_PrimaryBackgroundBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" DataContext="{Binding RelativeSource={RelativeSource Self}}" Visibility="Hidden"> <Grid> <views:MainView x:Name="MainView" /> </Grid> diff --git a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Tango.StubsUtils.Service.UI.csproj b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Tango.StubsUtils.Service.UI.csproj index a4c59bef2..c77c0926e 100644 --- a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Tango.StubsUtils.Service.UI.csproj +++ b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Tango.StubsUtils.Service.UI.csproj @@ -57,6 +57,7 @@ </Reference> <Reference Include="System" /> <Reference Include="System.Data" /> + <Reference Include="System.Drawing" /> <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <HintPath>..\..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath> </Reference> @@ -161,6 +162,10 @@ <Project>{8491d07b-c1f6-4b62-a412-41b9fd2d6538}</Project> <Name>Tango.SharedUI</Name> </ProjectReference> + <ProjectReference Include="..\Notifications.Wpf\Notifications.Wpf.csproj"> + <Project>{5c9a4f46-263d-4c23-b361-f09e14bb109e}</Project> + <Name>Notifications.Wpf</Name> + </ProjectReference> <ProjectReference Include="..\Tango.StubsUtils.Service\Tango.StubsUtils.Service.csproj"> <Project>{452df7f4-bfbf-45b1-9a27-d6b1888ac10b}</Project> <Name>Tango.StubsUtils.Service</Name> diff --git a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/ViewModels/MainViewVM.cs b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/ViewModels/MainViewVM.cs index 21a183d8a..16078fc47 100644 --- a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/ViewModels/MainViewVM.cs @@ -1,4 +1,5 @@ -using System; +using Notifications.Wpf; +using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; @@ -23,6 +24,7 @@ namespace Tango.StubsUtils.Service.UI.ViewModels { private ProducerConsumerQueue<String> _logsQueue; private Thread _logsThread; + private NotificationManager _notification; private ServiceUISettings _settings; public ServiceUISettings Settings @@ -87,6 +89,8 @@ namespace Tango.StubsUtils.Service.UI.ViewModels _logsThread.IsBackground = true; _logsThread.Start(); + _notification = new NotificationManager(); + Init(); } @@ -196,7 +200,7 @@ namespace Tango.StubsUtils.Service.UI.ViewModels { AvailablePorts = SerialPort.GetPortNames().ToList(); - if (!AvailablePorts.Contains(SelectedPort)) + if (SelectedPort == null) { SelectedPort = AvailablePorts.FirstOrDefault(); } @@ -216,11 +220,30 @@ namespace Tango.StubsUtils.Service.UI.ViewModels { await Service.Connect(SelectedPort); SaveSettings(); - //ToastNotificationManager.CreateToastNotifier("MyApplicationId").Show(toast); + + if (IsTrayIconVisible) + { + _notification.Show(new NotificationContent() + { + Title = "Tango Stubs Service", + Message = $"Connected ({SelectedPort})", + Type = NotificationType.Success + }); + } } catch (Exception ex) { LogManager.Log(ex, "Error connecting to the specified serial port."); + + if (IsTrayIconVisible) + { + _notification.Show(new NotificationContent() + { + Title = "Tango Stubs Service", + Message = $"Connection Error ({SelectedPort})", + Type = NotificationType.Error + }); + } } } else diff --git a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Views/MainView.xaml b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Views/MainView.xaml index 7cb2e1413..667a25502 100644 --- a/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Views/MainView.xaml +++ b/Software/Visual_Studio/StubsUtils/Tango.StubsUtils.Service.UI/Views/MainView.xaml @@ -94,7 +94,7 @@ </Style> </Button.Style> </Button> - <ComboBox DockPanel.Dock="Left" Margin="0 0 20 0" ItemsSource="{Binding AvailablePorts}" SelectedItem="{Binding SelectedPort}" IsEnabled="{Binding Service.IsConnected,Converter={StaticResource BooleanInverseConverter}}"></ComboBox> + <ComboBox DockPanel.Dock="Left" Margin="0 0 20 0" ItemsSource="{Binding AvailablePorts}" SelectedItem="{Binding SelectedPort}" IsEnabled="{Binding Service.IsConnected,Converter={StaticResource BooleanInverseConverter}}" Text="{Binding SelectedPort}"></ComboBox> </DockPanel> </Border> </Grid> |
