[WIP] Add context menu support
This commit is contained in:
@@ -8,6 +8,13 @@
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="NotificationFlyout\ContextMenuFlyoutHost.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="NotificationFlyout\ContextMenuFlyoutHost.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="NotificationFlyout\NotificationFlyoutHost.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace NotificationFlyout.Uwp.UI.Controls
|
||||
{
|
||||
internal class ContextMenuFlyoutHost : Control
|
||||
{
|
||||
private Grid _root;
|
||||
|
||||
public ContextMenuFlyoutHost()
|
||||
{
|
||||
DefaultStyleKey = typeof(ContextMenuFlyoutHost);
|
||||
}
|
||||
|
||||
public void ShowFlyout()
|
||||
{
|
||||
if (_root == null) return;
|
||||
var flyout = FlyoutBase.GetAttachedFlyout(_root);
|
||||
flyout.ShowAt(_root, new FlyoutShowOptions { Placement = FlyoutPlacementMode.BottomEdgeAlignedLeft, ShowMode = FlyoutShowMode.TransientWithDismissOnPointerMoveAway });
|
||||
}
|
||||
|
||||
public void HideFlyout()
|
||||
{
|
||||
if (_root == null) return;
|
||||
var flyout = FlyoutBase.GetAttachedFlyout(_root);
|
||||
flyout.Hide();
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
_root = GetTemplateChild("Root") as Grid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:NotificationFlyout.Uwp.UI.Controls">
|
||||
<Style TargetType="controls:ContextMenuFlyoutHost">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:ContextMenuFlyoutHost">
|
||||
<Grid x:Name="Root">
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem Text="Test" />
|
||||
</MenuFlyout>
|
||||
</FlyoutBase.AttachedFlyout>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Markup;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
@@ -28,9 +32,19 @@ namespace NotificationFlyout.Uwp.UI.Controls
|
||||
typeof(UIElement), typeof(NotificationFlyout),
|
||||
new PropertyMetadata(null, OnContentPropertyChanged));
|
||||
|
||||
public static DependencyProperty ContextMenuItemsProperty =
|
||||
DependencyProperty.Register(nameof(ContextMenuItems),
|
||||
typeof(IList<MenuFlyoutItemBase>), typeof(NotificationFlyout),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
internal event EventHandler ContentChanged;
|
||||
internal event EventHandler RequestedThemeChanged;
|
||||
internal event EventHandler IconSourceChanged;
|
||||
internal event EventHandler RequestedThemeChanged;
|
||||
|
||||
public NotificationFlyout()
|
||||
{
|
||||
ContextMenuItems = new ObservableCollection<MenuFlyoutItemBase>();
|
||||
}
|
||||
|
||||
public UIElement Content
|
||||
{
|
||||
@@ -38,6 +52,12 @@ namespace NotificationFlyout.Uwp.UI.Controls
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public IList<MenuFlyoutItemBase> ContextMenuItems
|
||||
{
|
||||
get => (IList<MenuFlyoutItemBase>)GetValue(ContextMenuItemsProperty);
|
||||
set => SetValue(ContextMenuItemsProperty, value);
|
||||
}
|
||||
|
||||
public ImageSource IconSource
|
||||
{
|
||||
get => (ImageSource)GetValue(IconSourceProperty);
|
||||
|
||||
@@ -6,7 +6,7 @@ using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace NotificationFlyout.Uwp.UI.Controls
|
||||
{
|
||||
public class NotificationFlyoutHost : Control
|
||||
internal class NotificationFlyoutHost : Control
|
||||
{
|
||||
public static readonly DependencyProperty ContentProperty =
|
||||
DependencyProperty.Register(nameof(Content),
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutHost.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutPresenter.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/ContextMenuFlyoutHost.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<MSBuildWarningsAsMessages>NU1702</MSBuildWarningsAsMessages>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NotificationFlyout.Uwp.UI.Controls\NotificationFlyout.Uwp.UI.Controls.csproj" />
|
||||
<ProjectReference Include="..\NotificationFlyout.Uwp.UI\NotificationFlyout.Uwp.UI.csproj" />
|
||||
<ProjectReference Include="..\NotificationFlyout.Wpf.UI\NotificationFlyout.Wpf.UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.VCRTForwarders.140" Version="1.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+2
-2
@@ -11,11 +11,11 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
typeof(Uwp.UI.Controls.NotificationFlyout), typeof(NotificationFlyoutApplication),
|
||||
new PropertyMetadata(null, OnFlyoutPropertyChanged));
|
||||
|
||||
private readonly NotificationFlyoutXamlHost _xamlHost;
|
||||
private readonly NotificationFlyoutXamlHostWindow _xamlHost;
|
||||
|
||||
public NotificationFlyoutApplication()
|
||||
{
|
||||
_xamlHost = new NotificationFlyoutXamlHost();
|
||||
_xamlHost = new NotificationFlyoutXamlHostWindow();
|
||||
_xamlHost.Show();
|
||||
}
|
||||
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using NotificationFlyout.Uwp.UI.Controls;
|
||||
using NotificationFlyout.Wpf.UI.Extensions;
|
||||
using NotificationFlyout.Wpf.UI.Helpers;
|
||||
using System;
|
||||
|
||||
namespace NotificationFlyout.Wpf.UI.Controls
|
||||
{
|
||||
internal class ContextMenuXamlHost : XamlHostWindow<ContextMenuFlyoutHost>
|
||||
{
|
||||
public ContextMenuXamlHost()
|
||||
{
|
||||
Topmost = true;
|
||||
}
|
||||
|
||||
protected override void OnDeactivated(EventArgs args)
|
||||
{
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
flyoutHost.HideFlyout();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowContextMenuFlyout()
|
||||
{
|
||||
var position = CursorHelper.GetPhysicalCursorPos();
|
||||
this.SetWindowPosition(position.y, position.x, WindowSize, WindowSize);
|
||||
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
flyoutHost.ShowFlyout();
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-46
@@ -1,32 +1,29 @@
|
||||
using Microsoft.Toolkit.Wpf.UI.XamlHost;
|
||||
using NotificationFlyout.Uwp.UI.Controls;
|
||||
using NotificationFlyout.Uwp.UI.Controls;
|
||||
using NotificationFlyout.Wpf.UI.Extensions;
|
||||
using NotificationFlyout.Uwp.UI.Extensions;
|
||||
using NotificationFlyout.Wpf.UI.Helpers;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace NotificationFlyout.Wpf.UI.Controls
|
||||
{
|
||||
internal class NotificationFlyoutXamlHost : Window
|
||||
internal class NotificationFlyoutXamlHostWindow : XamlHostWindow<NotificationFlyoutHost>
|
||||
{
|
||||
private const string ShellTrayHandleName = "Shell_TrayWnd";
|
||||
private const double WindowSize = 5;
|
||||
|
||||
private ContextMenuXamlHost _contextMenuXamlHost;
|
||||
private Uwp.UI.Controls.NotificationFlyout _flyout;
|
||||
|
||||
private bool _isLoaded;
|
||||
|
||||
private NotificationIconHelper _notificationIconHelper;
|
||||
private SystemPersonalisationHelper _systemPersonalisationHelper;
|
||||
private TaskbarHelper _taskbarHelper;
|
||||
private WindowsXamlHost _xamlHost;
|
||||
|
||||
public NotificationFlyoutXamlHost()
|
||||
public NotificationFlyoutXamlHostWindow()
|
||||
{
|
||||
PrepareDefaultWindow();
|
||||
PrepareWindowsXamlHost();
|
||||
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
@@ -51,7 +48,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
|
||||
internal void HideFlyout()
|
||||
{
|
||||
var flyoutHost = GetFlyoutHost();
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
flyoutHost.HideFlyout();
|
||||
@@ -60,7 +57,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
|
||||
internal void ShowFlyout()
|
||||
{
|
||||
var flyoutHost = GetFlyoutHost();
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
var taskbarState = _taskbarHelper.GetCurrentState();
|
||||
@@ -78,10 +75,9 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationFlyoutHost GetFlyoutHost()
|
||||
protected override void OnDeactivated(EventArgs args)
|
||||
{
|
||||
if (_xamlHost == null) return null;
|
||||
return _xamlHost.GetUwpInternalObject() as NotificationFlyoutHost;
|
||||
HideFlyout();
|
||||
}
|
||||
|
||||
private void OnFlyoutContentChanged(object sender, EventArgs args)
|
||||
@@ -101,7 +97,15 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
|
||||
private void OnIconInvoked(object sender, NotificationIconInvokedEventArgs args)
|
||||
{
|
||||
ShowFlyout();
|
||||
if (args.MouseButton == MouseButton.Left)
|
||||
{
|
||||
ShowFlyout();
|
||||
}
|
||||
|
||||
if (args.MouseButton == MouseButton.Right)
|
||||
{
|
||||
ShowContextMenuFlyout();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs args)
|
||||
@@ -112,7 +116,6 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
_isLoaded = true;
|
||||
|
||||
UpdateWindow();
|
||||
this.Hidden();
|
||||
}
|
||||
|
||||
private void OnTaskbarChanged(object sender, EventArgs args)
|
||||
@@ -125,20 +128,11 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
UpdateIcons();
|
||||
}
|
||||
|
||||
private void PrepareDefaultWindow()
|
||||
{
|
||||
ShowInTaskbar = false;
|
||||
ShowActivated = false;
|
||||
WindowStyle = WindowStyle.None;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
AllowsTransparency = true;
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
Height = WindowSize;
|
||||
Width = WindowSize;
|
||||
}
|
||||
|
||||
private void PrepareNotificationIcon()
|
||||
{
|
||||
_contextMenuXamlHost = new ContextMenuXamlHost();
|
||||
_contextMenuXamlHost.Show();
|
||||
|
||||
_notificationIconHelper = NotificationIconHelper.Create(this);
|
||||
_notificationIconHelper.IconInvoked += OnIconInvoked;
|
||||
|
||||
@@ -151,20 +145,9 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
_taskbarHelper = TaskbarHelper.Create(this);
|
||||
_taskbarHelper.TaskbarChanged += OnTaskbarChanged;
|
||||
}
|
||||
|
||||
private void PrepareWindowsXamlHost()
|
||||
private void ShowContextMenuFlyout()
|
||||
{
|
||||
_xamlHost = new WindowsXamlHost
|
||||
{
|
||||
InitialTypeName = typeof(NotificationFlyoutHost).FullName
|
||||
};
|
||||
|
||||
_xamlHost.Height = 0;
|
||||
_xamlHost.Width = 0;
|
||||
_xamlHost.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
_xamlHost.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
|
||||
Content = _xamlHost;
|
||||
_contextMenuXamlHost.ShowContextMenuFlyout();
|
||||
}
|
||||
|
||||
private void UpdateFlyoutContent()
|
||||
@@ -174,7 +157,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
var content = _flyout.Content;
|
||||
if (content == null) return;
|
||||
|
||||
var flyoutHost = GetFlyoutHost();
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
flyoutHost.Content = content;
|
||||
@@ -208,7 +191,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
|
||||
var requestedTheme = _flyout.RequestedTheme;
|
||||
|
||||
var flyoutHost = GetFlyoutHost();
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost != null)
|
||||
{
|
||||
flyoutHost.RequestedTheme = requestedTheme;
|
||||
@@ -219,7 +202,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
var flyoutHost = GetFlyoutHost();
|
||||
var flyoutHost = GetHostContent();
|
||||
if (flyoutHost == null) return;
|
||||
|
||||
var taskbarState = _taskbarHelper.GetCurrentState();
|
||||
@@ -231,7 +214,7 @@ namespace NotificationFlyout.Wpf.UI.Controls
|
||||
var windowHeight = WindowSize * this.DpiY();
|
||||
|
||||
double top, left, height, width;
|
||||
|
||||
|
||||
var taskbarRect = taskbarState.Rect;
|
||||
switch (taskbarState.Position)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using Microsoft.Toolkit.Wpf.UI.XamlHost;
|
||||
using NotificationFlyout.Wpf.UI.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace NotificationFlyout.Wpf.UI.Controls
|
||||
{
|
||||
internal class XamlHostWindow<TXamlContent> : Window where TXamlContent : class
|
||||
{
|
||||
internal const double WindowSize = 5;
|
||||
private WindowsXamlHost _xamlHost;
|
||||
|
||||
public XamlHostWindow()
|
||||
{
|
||||
PrepareDefaultWindow();
|
||||
PrepareWindowsXamlHost();
|
||||
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
|
||||
internal TXamlContent GetHostContent()
|
||||
{
|
||||
if (_xamlHost == null) return null;
|
||||
return _xamlHost.GetUwpInternalObject() as TXamlContent;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs args)
|
||||
{
|
||||
this.Hidden();
|
||||
}
|
||||
|
||||
private void PrepareDefaultWindow()
|
||||
{
|
||||
ShowInTaskbar = false;
|
||||
ShowActivated = false;
|
||||
WindowStyle = WindowStyle.None;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
AllowsTransparency = true;
|
||||
Background = new SolidColorBrush(Colors.Red);
|
||||
Height = WindowSize;
|
||||
Width = WindowSize;
|
||||
}
|
||||
|
||||
private void PrepareWindowsXamlHost()
|
||||
{
|
||||
_xamlHost = new WindowsXamlHost
|
||||
{
|
||||
InitialTypeName = typeof(TXamlContent).FullName
|
||||
};
|
||||
|
||||
_xamlHost.Height = 0;
|
||||
_xamlHost.Width = 0;
|
||||
_xamlHost.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
_xamlHost.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
|
||||
Content = _xamlHost;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.Windows.Sdk;
|
||||
|
||||
namespace NotificationFlyout.Wpf.UI.Helpers
|
||||
{
|
||||
internal class CursorHelper
|
||||
{
|
||||
public static POINT GetPhysicalCursorPos()
|
||||
{
|
||||
PInvoke.GetPhysicalCursorPos(out POINT point);
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.Windows.Sdk;
|
||||
using System;
|
||||
using System;
|
||||
using Microsoft.Windows.Sdk;
|
||||
|
||||
namespace NotificationFlyout.Wpf.UI.Helpers
|
||||
{
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
Shell_NotifyIcon
|
||||
GetDpiForWindow
|
||||
FindWindow
|
||||
GetSystemMetricsForDpi
|
||||
DefWindowProcW
|
||||
GetSystemMetrics
|
||||
MonitorFromWindow
|
||||
RegisterWindowMessage
|
||||
FindWindow
|
||||
SHAppBarMessage
|
||||
SetWindowPos
|
||||
GetWindowLong
|
||||
SetWindowLong
|
||||
SetWindowLong
|
||||
GetDpiForWindow
|
||||
FindWindow
|
||||
GetPhysicalCursorPos
|
||||
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("NotificationFlyout.Wpf.UI.Controls")]
|
||||
@@ -88,6 +88,7 @@ namespace NotificationFlyout.Wpf.UI
|
||||
|
||||
return monitorData;
|
||||
}
|
||||
|
||||
private Rect GetWorkingArea()
|
||||
{
|
||||
if (!_multiMonitorSupport || _monitorHandle == (IntPtr)PRIMARY_MONITOR)
|
||||
|
||||
@@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
global.json = global.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotificationFlyout.Uwp.UI", "NotificationFlyout.Uwp.UI\NotificationFlyout.Uwp.UI.csproj", "{3FE0D7F7-1173-4989-BE6C-AD28FE0D4AC9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NotificationFlyout.Uwp.UI", "NotificationFlyout.Uwp.UI\NotificationFlyout.Uwp.UI.csproj", "{3FE0D7F7-1173-4989-BE6C-AD28FE0D4AC9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
Reference in New Issue
Block a user