mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
[WIP] Basic Notification Compat intergration
This commit is contained in:
parent
33689931ca
commit
3a2b74b049
File diff suppressed because it is too large
Load Diff
87
DisplayMagician/DesktopNotificationActivator.cs
Normal file
87
DisplayMagician/DesktopNotificationActivator.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
|
using DesktopNotifications;
|
||||||
|
using static DesktopNotifications.NotificationActivator;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DisplayMagician
|
||||||
|
{
|
||||||
|
// The GUID must be unique to your app. Create a new GUID if copying this code.
|
||||||
|
[ClassInterface(ClassInterfaceType.None)]
|
||||||
|
[ComSourceInterfaces(typeof(INotificationActivationCallback))]
|
||||||
|
[Guid("56F14154-6339-4B94-8B82-80F78D5BCEAF"), ComVisible(true)]
|
||||||
|
public class DesktopNotificationActivator : NotificationActivator
|
||||||
|
{
|
||||||
|
public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId)
|
||||||
|
{
|
||||||
|
/*Application.Current.Dispatcher.Invoke(delegate
|
||||||
|
{
|
||||||
|
// Tapping on the top-level header launches with empty args
|
||||||
|
if (arguments.Length == 0)
|
||||||
|
{
|
||||||
|
// Perform a normal launch
|
||||||
|
OpenWindowIfNeeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the query string (using NuGet package QueryString.NET)
|
||||||
|
QueryString args = QueryString.Parse(invokedArgs);
|
||||||
|
|
||||||
|
// See what action is being requested
|
||||||
|
switch (args["action"])
|
||||||
|
{
|
||||||
|
// Open the image
|
||||||
|
case "viewImage":
|
||||||
|
|
||||||
|
// The URL retrieved from the toast args
|
||||||
|
string imageUrl = args["imageUrl"];
|
||||||
|
|
||||||
|
// Make sure we have a window open and in foreground
|
||||||
|
OpenWindowIfNeeded();
|
||||||
|
|
||||||
|
// And then show the image
|
||||||
|
(App.Current.Windows[0] as MainWindow).ShowImage(imageUrl);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Background: Quick reply to the conversation
|
||||||
|
case "reply":
|
||||||
|
|
||||||
|
// Get the response the user typed
|
||||||
|
string msg = userInput["tbReply"];
|
||||||
|
|
||||||
|
// And send this message
|
||||||
|
SendMessage(msg);
|
||||||
|
|
||||||
|
// If there's no windows open, exit the app
|
||||||
|
if (App.Current.Windows.Count == 0)
|
||||||
|
{
|
||||||
|
Application.Current.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenWindowIfNeeded()
|
||||||
|
{
|
||||||
|
/*// Make sure we have a window open (in case user clicked toast while app closed)
|
||||||
|
if (App.Current.Windows.Count == 0)
|
||||||
|
{
|
||||||
|
new MainWindow().Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate the window, bringing it to focus
|
||||||
|
App.Current.Windows[0].Activate();
|
||||||
|
|
||||||
|
// And make sure to maximize the window too, in case it was currently minimized
|
||||||
|
App.Current.Windows[0].WindowState = WindowState.Normal;*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
506
DisplayMagician/DesktopNotificationManagerCompat.cs
Normal file
506
DisplayMagician/DesktopNotificationManagerCompat.cs
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
// ******************************************************************
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// This code is licensed under the MIT License (MIT).
|
||||||
|
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||||
|
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||||
|
// ******************************************************************
|
||||||
|
|
||||||
|
/*
|
||||||
|
* License for the RegisterActivator portion of code from FrecherxDachs
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020 Michael Dietrich
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
* */
|
||||||
|
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Text;
|
||||||
|
using Windows.UI.Notifications;
|
||||||
|
using static DesktopNotifications.NotificationActivator;
|
||||||
|
|
||||||
|
namespace DesktopNotifications
|
||||||
|
{
|
||||||
|
public class DesktopNotificationManagerCompat
|
||||||
|
{
|
||||||
|
public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated";
|
||||||
|
|
||||||
|
private static bool _registeredAumidAndComServer;
|
||||||
|
private static string _aumid;
|
||||||
|
private static bool _registeredActivator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If you're not using MSIX or sparse packages, you must call this method to register your AUMID with the Compat library and to
|
||||||
|
/// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running
|
||||||
|
/// under Desktop Bridge. Call this upon application startup, before calling any other APIs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aumid">An AUMID that uniquely identifies your application.</param>
|
||||||
|
public static void RegisterAumidAndComServer<T>(string aumid)
|
||||||
|
where T : NotificationActivator
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(aumid))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must provide an AUMID.", nameof(aumid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If running as Desktop Bridge
|
||||||
|
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||||
|
{
|
||||||
|
// Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
|
||||||
|
// Desktop Bridge apps are registered with platform through their manifest.
|
||||||
|
// Their LocalServer32 key is also registered through their manifest.
|
||||||
|
_aumid = null;
|
||||||
|
_registeredAumidAndComServer = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_aumid = aumid;
|
||||||
|
|
||||||
|
String exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||||
|
RegisterComServer<T>(exePath);
|
||||||
|
|
||||||
|
_registeredAumidAndComServer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterComServer<T>(String exePath)
|
||||||
|
where T : NotificationActivator
|
||||||
|
{
|
||||||
|
// We register the EXE to start up when the notification is activated
|
||||||
|
string regString = String.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}", typeof(T).GUID);
|
||||||
|
using (var key = Registry.CurrentUser.CreateSubKey(regString))
|
||||||
|
{
|
||||||
|
// Include a flag so we know this was a toast activation and should wait for COM to process
|
||||||
|
// We also wrap EXE path in quotes for extra security
|
||||||
|
key.SetValue("LocalServer32", '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsElevated)
|
||||||
|
{
|
||||||
|
// For elevated apps, we need to ensure they'll activate in existing running process by adding
|
||||||
|
// some values in local machine
|
||||||
|
using (var key = Registry.LocalMachine.CreateSubKey(regString))
|
||||||
|
{
|
||||||
|
// Same as above, except also including AppId to link to our AppId entry below
|
||||||
|
key.SetValue("LocalServer32", '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||||
|
key.SetValue("AppId", "{" + typeof(T).GUID + "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This tells COM to match any client, so Action Center will activate our elevated process.
|
||||||
|
// More info: https://docs.microsoft.com/windows/win32/com/runas
|
||||||
|
using (var key = Registry.LocalMachine.CreateSubKey(String.Format("SOFTWARE\\Classes\\AppID\\{{{0}}}", typeof(T).GUID)))
|
||||||
|
{
|
||||||
|
key.SetValue("RunAs", "Interactive User");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the activator type as a COM server client so that Windows can launch your activator.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Your implementation of NotificationActivator. Must have GUID and ComVisible attributes on class.</typeparam>
|
||||||
|
public static void RegisterActivator<T>()
|
||||||
|
where T : NotificationActivator, new()
|
||||||
|
{
|
||||||
|
// Big thanks to FrecherxDachs for figuring out the following code which works in .NET Core 3: https://github.com/FrecherxDachs/UwpNotificationNetCoreTest
|
||||||
|
var uuid = typeof(T).GUID;
|
||||||
|
uint _cookie;
|
||||||
|
CoRegisterClassObject(uuid, new NotificationActivatorClassFactory<T>(), CLSCTX_LOCAL_SERVER,
|
||||||
|
REGCLS_MULTIPLEUSE, out _cookie);
|
||||||
|
|
||||||
|
_registeredActivator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[Guid("00000001-0000-0000-C000-000000000046")]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
private interface IClassFactory
|
||||||
|
{
|
||||||
|
[PreserveSig]
|
||||||
|
int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
|
||||||
|
|
||||||
|
[PreserveSig]
|
||||||
|
int LockServer(bool fLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int CLASS_E_NOAGGREGATION = -2147221232;
|
||||||
|
private const int E_NOINTERFACE = -2147467262;
|
||||||
|
private const int CLSCTX_LOCAL_SERVER = 4;
|
||||||
|
private const int REGCLS_MULTIPLEUSE = 1;
|
||||||
|
private const int S_OK = 0;
|
||||||
|
private static readonly Guid IUnknownGuid = new Guid("00000000-0000-0000-C000-000000000046");
|
||||||
|
|
||||||
|
private class NotificationActivatorClassFactory<T> : IClassFactory where T : NotificationActivator, new()
|
||||||
|
{
|
||||||
|
public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject)
|
||||||
|
{
|
||||||
|
ppvObject = IntPtr.Zero;
|
||||||
|
|
||||||
|
if (pUnkOuter != IntPtr.Zero)
|
||||||
|
Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
|
||||||
|
|
||||||
|
if (riid == typeof(T).GUID || riid == IUnknownGuid)
|
||||||
|
// Create the instance of the .NET object
|
||||||
|
ppvObject = Marshal.GetComInterfaceForObject(new T(),
|
||||||
|
typeof(INotificationActivationCallback));
|
||||||
|
else
|
||||||
|
// The object that ppvObject points to does not support the
|
||||||
|
// interface identified by riid.
|
||||||
|
Marshal.ThrowExceptionForHR(E_NOINTERFACE);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int LockServer(bool fLock)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("ole32.dll")]
|
||||||
|
private static extern int CoRegisterClassObject(
|
||||||
|
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
|
||||||
|
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
|
||||||
|
uint dwClsContext,
|
||||||
|
uint flags,
|
||||||
|
out uint lpdwRegister);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a toast notifier. You must have called <see cref="RegisterActivator{T}"/> first (and also <see cref="RegisterAumidAndComServer(string)"/> if you're a classic Win32 app), or this will throw an exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ToastNotifier CreateToastNotifier()
|
||||||
|
{
|
||||||
|
EnsureRegistered();
|
||||||
|
|
||||||
|
if (_aumid != null)
|
||||||
|
{
|
||||||
|
// Non-Desktop Bridge
|
||||||
|
return ToastNotificationManager.CreateToastNotifier(_aumid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Desktop Bridge
|
||||||
|
return ToastNotificationManager.CreateToastNotifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="DesktopNotificationHistoryCompat"/> object. You must have called <see cref="RegisterActivator{T}"/> first (and also <see cref="RegisterAumidAndComServer(string)"/> if you're a classic Win32 app), or this will throw an exception.
|
||||||
|
/// </summary>
|
||||||
|
public static DesktopNotificationHistoryCompat History
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
EnsureRegistered();
|
||||||
|
|
||||||
|
return new DesktopNotificationHistoryCompat(_aumid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureRegistered()
|
||||||
|
{
|
||||||
|
// If not registered AUMID yet
|
||||||
|
if (!_registeredAumidAndComServer)
|
||||||
|
{
|
||||||
|
// Check if Desktop Bridge
|
||||||
|
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||||
|
{
|
||||||
|
// Implicitly registered, all good!
|
||||||
|
_registeredAumidAndComServer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, incorrect usage
|
||||||
|
throw new Exception("You must call RegisterAumidAndComServer first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not registered activator yet
|
||||||
|
if (!_registeredActivator)
|
||||||
|
{
|
||||||
|
// Incorrect usage
|
||||||
|
throw new Exception("You must call RegisterActivator first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean representing whether http images can be used within toasts. This is true if running with package identity (MSIX or sparse package).
|
||||||
|
/// </summary>
|
||||||
|
public static bool CanUseHttpImages { get { return DesktopBridgeHelpers.IsRunningAsUwp(); } }
|
||||||
|
|
||||||
|
private static bool IsElevated
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs
|
||||||
|
/// </summary>
|
||||||
|
private class DesktopBridgeHelpers
|
||||||
|
{
|
||||||
|
const long APPMODEL_ERROR_NO_PACKAGE = 15700L;
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
|
||||||
|
|
||||||
|
private static bool? _isRunningAsUwp;
|
||||||
|
public static bool IsRunningAsUwp()
|
||||||
|
{
|
||||||
|
if (_isRunningAsUwp == null)
|
||||||
|
{
|
||||||
|
if (IsWindows7OrLower)
|
||||||
|
{
|
||||||
|
_isRunningAsUwp = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int length = 0;
|
||||||
|
StringBuilder sb = new StringBuilder(0);
|
||||||
|
int result = GetCurrentPackageFullName(ref length, sb);
|
||||||
|
|
||||||
|
sb = new StringBuilder(length);
|
||||||
|
result = GetCurrentPackageFullName(ref length, sb);
|
||||||
|
|
||||||
|
_isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _isRunningAsUwp.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWindows7OrLower
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int versionMajor = Environment.OSVersion.Version.Major;
|
||||||
|
int versionMinor = Environment.OSVersion.Version.Minor;
|
||||||
|
double version = versionMajor + (double)versionMinor / 10;
|
||||||
|
return version <= 6.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the toast notifications for an app including the ability the clear all toast history and removing individual toasts.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DesktopNotificationHistoryCompat
|
||||||
|
{
|
||||||
|
private string _aumid;
|
||||||
|
private ToastNotificationHistory _history;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Do not call this. Instead, call <see cref="DesktopNotificationManagerCompat.History"/> to obtain an instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aumid"></param>
|
||||||
|
internal DesktopNotificationHistoryCompat(string aumid)
|
||||||
|
{
|
||||||
|
_aumid = aumid;
|
||||||
|
_history = ToastNotificationManager.History;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all notifications sent by this app from action center.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (_aumid != null)
|
||||||
|
{
|
||||||
|
_history.Clear(_aumid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_history.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all notifications sent by this app that are currently still in Action Center.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A collection of toasts.</returns>
|
||||||
|
public IReadOnlyList<ToastNotification> GetHistory()
|
||||||
|
{
|
||||||
|
return _aumid != null ? _history.GetHistory(_aumid) : _history.GetHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an individual toast, with the specified tag label, from action center.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||||
|
public void Remove(string tag)
|
||||||
|
{
|
||||||
|
if (_aumid != null)
|
||||||
|
{
|
||||||
|
_history.Remove(tag, string.Empty, _aumid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_history.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a toast notification from the action using the notification's tag and group labels.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||||
|
/// <param name="group">The group label of the toast notification to be removed.</param>
|
||||||
|
public void Remove(string tag, string group)
|
||||||
|
{
|
||||||
|
if (_aumid != null)
|
||||||
|
{
|
||||||
|
_history.Remove(tag, group, _aumid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_history.Remove(tag, group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a group of toast notifications, identified by the specified group label, from action center.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="group">The group label of the toast notifications to be removed.</param>
|
||||||
|
public void RemoveGroup(string group)
|
||||||
|
{
|
||||||
|
if (_aumid != null)
|
||||||
|
{
|
||||||
|
_history.RemoveGroup(group, _aumid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_history.RemoveGroup(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apps must implement this activator to handle notification activation.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class NotificationActivator : NotificationActivator.INotificationActivationCallback
|
||||||
|
{
|
||||||
|
public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount)
|
||||||
|
{
|
||||||
|
OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method will be called when the user clicks on a foreground or background activation on a toast. Parent app must implement this method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="arguments">The arguments from the original notification. This is either the launch argument if the user clicked the body of your toast, or the arguments from a button on your toast.</param>
|
||||||
|
/// <param name="userInput">Text and selection values that the user entered in your toast.</param>
|
||||||
|
/// <param name="appUserModelId">Your AUMID.</param>
|
||||||
|
public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId);
|
||||||
|
|
||||||
|
// These are the new APIs for Windows 10
|
||||||
|
#region NewAPIs
|
||||||
|
[StructLayout(LayoutKind.Sequential), Serializable]
|
||||||
|
public struct NOTIFICATION_USER_INPUT_DATA
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string Key;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport,
|
||||||
|
Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true),
|
||||||
|
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
public interface INotificationActivationCallback
|
||||||
|
{
|
||||||
|
void Activate(
|
||||||
|
[In, MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
string appUserModelId,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
string invokedArgs,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
|
||||||
|
NOTIFICATION_USER_INPUT_DATA[] data,
|
||||||
|
[In, MarshalAs(UnmanagedType.U4)]
|
||||||
|
uint dataCount);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Text and selection values that the user entered on your notification. The Key is the ID of the input, and the Value is what the user entered.
|
||||||
|
/// </summary>
|
||||||
|
public class NotificationUserInput : IReadOnlyDictionary<string, string>
|
||||||
|
{
|
||||||
|
private NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] _data;
|
||||||
|
|
||||||
|
internal NotificationUserInput(NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] data)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string this[string key] => _data.First(i => i.Key == key).Value;
|
||||||
|
|
||||||
|
public IEnumerable<string> Keys => _data.Select(i => i.Key);
|
||||||
|
|
||||||
|
public IEnumerable<string> Values => _data.Select(i => i.Value);
|
||||||
|
|
||||||
|
public int Count => _data.Length;
|
||||||
|
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
return _data.Any(i => i.Key == key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _data.Select(i => new KeyValuePair<string, string>(i.Key, i.Value)).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(string key, out string value)
|
||||||
|
{
|
||||||
|
foreach (var item in _data)
|
||||||
|
{
|
||||||
|
if (item.Key == key)
|
||||||
|
{
|
||||||
|
value = item.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -86,6 +86,8 @@
|
|||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="DesktopNotificationActivator.cs" />
|
||||||
|
<Compile Include="DesktopNotificationManagerCompat.cs" />
|
||||||
<Compile Include="GameLibraries\Game.cs" />
|
<Compile Include="GameLibraries\Game.cs" />
|
||||||
<Compile Include="GameLibraries\SteamAppInfoParser\AppInfo.cs" />
|
<Compile Include="GameLibraries\SteamAppInfoParser\AppInfo.cs" />
|
||||||
<Compile Include="GameLibraries\SteamAppInfoParser\EUniverse.cs" />
|
<Compile Include="GameLibraries\SteamAppInfoParser\EUniverse.cs" />
|
||||||
@ -255,6 +257,9 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
|
||||||
|
<Version>6.1.1</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="MintPlayer.IconUtils">
|
<PackageReference Include="MintPlayer.IconUtils">
|
||||||
<Version>1.0.4</Version>
|
<Version>1.0.4</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@ -264,6 +269,9 @@
|
|||||||
<PackageReference Include="NLog">
|
<PackageReference Include="NLog">
|
||||||
<Version>4.7.6</Version>
|
<Version>4.7.6</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="QueryString.NET">
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="ValveKeyValue">
|
<PackageReference Include="ValveKeyValue">
|
||||||
<Version>0.3.1.152</Version>
|
<Version>0.3.1.152</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -13,6 +13,9 @@ using DisplayMagician.Shared;
|
|||||||
using DisplayMagician.UIForms;
|
using DisplayMagician.UIForms;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
|
using DesktopNotifications;
|
||||||
|
|
||||||
//using System.Runtime.InteropServices;
|
//using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace DisplayMagician {
|
namespace DisplayMagician {
|
||||||
@ -53,7 +56,11 @@ namespace DisplayMagician {
|
|||||||
// taskbar pinning and similar.
|
// taskbar pinning and similar.
|
||||||
// This is a helper function that sets the AUMID to a static default defined
|
// This is a helper function that sets the AUMID to a static default defined
|
||||||
// within ShellUtils under the DisplayMagician.Shared project.
|
// within ShellUtils under the DisplayMagician.Shared project.
|
||||||
ShellUtils.SetDefaultProcessExplicitAppUserModelID();
|
//ShellUtils.SetDefaultProcessExplicitAppUserModelID();
|
||||||
|
|
||||||
|
// Register AUMID, COM server, and activator
|
||||||
|
DesktopNotificationManagerCompat.RegisterAumidAndComServer<DesktopNotificationActivator>(ShellUtils.AUMID);
|
||||||
|
DesktopNotificationManagerCompat.RegisterActivator<DesktopNotificationActivator>();
|
||||||
|
|
||||||
// Prepare NLog for logging
|
// Prepare NLog for logging
|
||||||
var config = new NLog.Config.LoggingConfiguration();
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
13
DisplayMagician/UIForms/MainForm.Designer.cs
generated
13
DisplayMagician/UIForms/MainForm.Designer.cs
generated
@ -31,6 +31,7 @@
|
|||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||||
|
this.btn_toast = new System.Windows.Forms.Button();
|
||||||
this.btn_settings = new System.Windows.Forms.Button();
|
this.btn_settings = new System.Windows.Forms.Button();
|
||||||
this.lbl_create_profile = new System.Windows.Forms.Label();
|
this.lbl_create_profile = new System.Windows.Forms.Label();
|
||||||
this.btn_setup_display_profiles = new System.Windows.Forms.Button();
|
this.btn_setup_display_profiles = new System.Windows.Forms.Button();
|
||||||
@ -71,6 +72,7 @@
|
|||||||
//
|
//
|
||||||
// splitContainer1.Panel1
|
// splitContainer1.Panel1
|
||||||
//
|
//
|
||||||
|
this.splitContainer1.Panel1.Controls.Add(this.btn_toast);
|
||||||
this.splitContainer1.Panel1.Controls.Add(this.btn_settings);
|
this.splitContainer1.Panel1.Controls.Add(this.btn_settings);
|
||||||
this.splitContainer1.Panel1.Controls.Add(this.lbl_create_profile);
|
this.splitContainer1.Panel1.Controls.Add(this.lbl_create_profile);
|
||||||
this.splitContainer1.Panel1.Controls.Add(this.btn_setup_display_profiles);
|
this.splitContainer1.Panel1.Controls.Add(this.btn_setup_display_profiles);
|
||||||
@ -86,6 +88,16 @@
|
|||||||
this.splitContainer1.Panel2.Controls.Add(this.pb_game_shortcut);
|
this.splitContainer1.Panel2.Controls.Add(this.pb_game_shortcut);
|
||||||
this.splitContainer1.TabStop = false;
|
this.splitContainer1.TabStop = false;
|
||||||
//
|
//
|
||||||
|
// btn_toast
|
||||||
|
//
|
||||||
|
this.btn_toast.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
|
||||||
|
this.btn_toast.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
|
||||||
|
resources.ApplyResources(this.btn_toast, "btn_toast");
|
||||||
|
this.btn_toast.ForeColor = System.Drawing.Color.White;
|
||||||
|
this.btn_toast.Name = "btn_toast";
|
||||||
|
this.btn_toast.UseVisualStyleBackColor = true;
|
||||||
|
this.btn_toast.Click += new System.EventHandler(this.btn_toast_Click);
|
||||||
|
//
|
||||||
// btn_settings
|
// btn_settings
|
||||||
//
|
//
|
||||||
this.btn_settings.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
|
this.btn_settings.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
|
||||||
@ -303,5 +315,6 @@
|
|||||||
private System.Windows.Forms.Label lbl_create_profile;
|
private System.Windows.Forms.Label lbl_create_profile;
|
||||||
private System.Windows.Forms.Label lbl_create_shortcut;
|
private System.Windows.Forms.Label lbl_create_shortcut;
|
||||||
private System.Windows.Forms.Button btn_settings;
|
private System.Windows.Forms.Button btn_settings;
|
||||||
|
private System.Windows.Forms.Button btn_toast;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,10 +12,16 @@ using System.Threading;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using DisplayMagician.Shared;
|
using DisplayMagician.Shared;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
|
using DesktopNotifications;
|
||||||
|
using Microsoft.QueryStringDotNET;
|
||||||
|
using Windows.UI.Notifications;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using AutoUpdaterDotNET;
|
using AutoUpdaterDotNET;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Windows.Data.Xml.Dom;
|
||||||
|
using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
|
|
||||||
namespace DisplayMagician.UIForms
|
namespace DisplayMagician.UIForms
|
||||||
{
|
{
|
||||||
@ -390,5 +396,78 @@ namespace DisplayMagician.UIForms
|
|||||||
else if (!mySettings.MinimiseOnStart && cb_minimise_notification_area.Checked)
|
else if (!mySettings.MinimiseOnStart && cb_minimise_notification_area.Checked)
|
||||||
cb_minimise_notification_area.Checked = false;
|
cb_minimise_notification_area.Checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btn_toast_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Construct the toast content
|
||||||
|
ToastContent toastContent = new ToastContent()
|
||||||
|
{
|
||||||
|
// Arguments when the user taps body of toast
|
||||||
|
Launch = "DisplayMagician_Notification",
|
||||||
|
|
||||||
|
Visual = new ToastVisual()
|
||||||
|
{
|
||||||
|
BindingGeneric = new ToastBindingGeneric()
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new AdaptiveText()
|
||||||
|
{
|
||||||
|
Text = "Hello from DisplayMagician!"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
Actions = new ToastActionsCustom()
|
||||||
|
{
|
||||||
|
Inputs =
|
||||||
|
{
|
||||||
|
new ToastTextBox("tbReply")
|
||||||
|
{
|
||||||
|
PlaceholderContent = "Type a response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Buttons =
|
||||||
|
{
|
||||||
|
// Note that there's no reason to specify background activation, since our COM
|
||||||
|
// activator decides whether to process in background or launch foreground window
|
||||||
|
new ToastButton("Reply", new QueryString()
|
||||||
|
{
|
||||||
|
{ "action", "reply" },
|
||||||
|
{ "conversationId", conversationId.ToString() }
|
||||||
|
|
||||||
|
}.ToString()),
|
||||||
|
|
||||||
|
new ToastButton("Like", new QueryString()
|
||||||
|
{
|
||||||
|
{ "action", "like" },
|
||||||
|
{ "conversationId", conversationId.ToString() }
|
||||||
|
|
||||||
|
}.ToString()),
|
||||||
|
|
||||||
|
new ToastButton("View", new QueryString()
|
||||||
|
{
|
||||||
|
{ "action", "viewImage" },
|
||||||
|
{ "imageUrl", image }
|
||||||
|
|
||||||
|
}.ToString())
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure to use Windows.Data.Xml.Dom
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
doc.LoadXml(toastContent.GetContent());
|
||||||
|
|
||||||
|
// And create the toast notification
|
||||||
|
var toast = new ToastNotification(doc);
|
||||||
|
|
||||||
|
// And then show it
|
||||||
|
DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,36 @@
|
|||||||
<data name="splitContainer1.Orientation" type="System.Windows.Forms.Orientation, System.Windows.Forms">
|
<data name="splitContainer1.Orientation" type="System.Windows.Forms.Orientation, System.Windows.Forms">
|
||||||
<value>Horizontal</value>
|
<value>Horizontal</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="btn_toast.FlatStyle" type="System.Windows.Forms.FlatStyle, System.Windows.Forms">
|
||||||
|
<value>Flat</value>
|
||||||
|
</data>
|
||||||
|
<data name="btn_toast.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
|
||||||
|
<value>NoControl</value>
|
||||||
|
</data>
|
||||||
|
<data name="btn_toast.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
|
<value>366, 64</value>
|
||||||
|
</data>
|
||||||
|
<data name="btn_toast.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
|
<value>75, 23</value>
|
||||||
|
</data>
|
||||||
|
<data name="btn_toast.TabIndex" type="System.Int32, mscorlib">
|
||||||
|
<value>8</value>
|
||||||
|
</data>
|
||||||
|
<data name="btn_toast.Text" xml:space="preserve">
|
||||||
|
<value>Show Toast</value>
|
||||||
|
</data>
|
||||||
|
<data name=">>btn_toast.Name" xml:space="preserve">
|
||||||
|
<value>btn_toast</value>
|
||||||
|
</data>
|
||||||
|
<data name=">>btn_toast.Type" xml:space="preserve">
|
||||||
|
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
<data name=">>btn_toast.Parent" xml:space="preserve">
|
||||||
|
<value>splitContainer1.Panel1</value>
|
||||||
|
</data>
|
||||||
|
<data name=">>btn_toast.ZOrder" xml:space="preserve">
|
||||||
|
<value>0</value>
|
||||||
|
</data>
|
||||||
<data name="btn_settings.FlatStyle" type="System.Windows.Forms.FlatStyle, System.Windows.Forms">
|
<data name="btn_settings.FlatStyle" type="System.Windows.Forms.FlatStyle, System.Windows.Forms">
|
||||||
<value>Flat</value>
|
<value>Flat</value>
|
||||||
</data>
|
</data>
|
||||||
@ -157,7 +187,7 @@
|
|||||||
<value>splitContainer1.Panel1</value>
|
<value>splitContainer1.Panel1</value>
|
||||||
</data>
|
</data>
|
||||||
<data name=">>btn_settings.ZOrder" xml:space="preserve">
|
<data name=">>btn_settings.ZOrder" xml:space="preserve">
|
||||||
<value>0</value>
|
<value>1</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="lbl_create_profile.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
<data name="lbl_create_profile.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
||||||
<value>None</value>
|
<value>None</value>
|
||||||
@ -190,7 +220,7 @@
|
|||||||
<value>splitContainer1.Panel1</value>
|
<value>splitContainer1.Panel1</value>
|
||||||
</data>
|
</data>
|
||||||
<data name=">>lbl_create_profile.ZOrder" xml:space="preserve">
|
<data name=">>lbl_create_profile.ZOrder" xml:space="preserve">
|
||||||
<value>1</value>
|
<value>2</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btn_setup_display_profiles.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
<data name="btn_setup_display_profiles.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
||||||
<value>None</value>
|
<value>None</value>
|
||||||
@ -223,7 +253,7 @@
|
|||||||
<value>splitContainer1.Panel1</value>
|
<value>splitContainer1.Panel1</value>
|
||||||
</data>
|
</data>
|
||||||
<data name=">>btn_setup_display_profiles.ZOrder" xml:space="preserve">
|
<data name=">>btn_setup_display_profiles.ZOrder" xml:space="preserve">
|
||||||
<value>2</value>
|
<value>3</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="pb_display_profile.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
|
<data name="pb_display_profile.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
|
||||||
<value>Fill</value>
|
<value>Fill</value>
|
||||||
@ -10771,7 +10801,7 @@
|
|||||||
<value>splitContainer1.Panel1</value>
|
<value>splitContainer1.Panel1</value>
|
||||||
</data>
|
</data>
|
||||||
<data name=">>pb_display_profile.ZOrder" xml:space="preserve">
|
<data name=">>pb_display_profile.ZOrder" xml:space="preserve">
|
||||||
<value>3</value>
|
<value>4</value>
|
||||||
</data>
|
</data>
|
||||||
<data name=">>splitContainer1.Panel1.Name" xml:space="preserve">
|
<data name=">>splitContainer1.Panel1.Name" xml:space="preserve">
|
||||||
<value>splitContainer1.Panel1</value>
|
<value>splitContainer1.Panel1</value>
|
||||||
@ -10798,7 +10828,7 @@
|
|||||||
<value>NoControl</value>
|
<value>NoControl</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="lbl_create_shortcut.Location" type="System.Drawing.Point, System.Drawing">
|
<data name="lbl_create_shortcut.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
<value>220, 253</value>
|
<value>220, 257</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="lbl_create_shortcut.Size" type="System.Drawing.Size, System.Drawing">
|
<data name="lbl_create_shortcut.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
<value>343, 22</value>
|
<value>343, 22</value>
|
||||||
@ -10837,7 +10867,7 @@
|
|||||||
<value>NoControl</value>
|
<value>NoControl</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cb_minimise_notification_area.Location" type="System.Drawing.Point, System.Drawing">
|
<data name="cb_minimise_notification_area.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
<value>234, 357</value>
|
<value>234, 363</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cb_minimise_notification_area.Size" type="System.Drawing.Size, System.Drawing">
|
<data name="cb_minimise_notification_area.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
<value>332, 20</value>
|
<value>332, 20</value>
|
||||||
@ -10870,7 +10900,7 @@
|
|||||||
<value>Microsoft Sans Serif, 9.75pt</value>
|
<value>Microsoft Sans Serif, 9.75pt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="lbl_version.Location" type="System.Drawing.Point, System.Drawing">
|
<data name="lbl_version.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
<value>12, 358</value>
|
<value>12, 364</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="lbl_version.Size" type="System.Drawing.Size, System.Drawing">
|
<data name="lbl_version.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
<value>25, 16</value>
|
<value>25, 16</value>
|
||||||
@ -10906,7 +10936,7 @@
|
|||||||
<value>Microsoft Sans Serif, 21.75pt</value>
|
<value>Microsoft Sans Serif, 21.75pt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btn_setup_game_shortcuts.Location" type="System.Drawing.Point, System.Drawing">
|
<data name="btn_setup_game_shortcuts.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
<value>212, 180</value>
|
<value>212, 184</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btn_setup_game_shortcuts.Size" type="System.Drawing.Size, System.Drawing">
|
<data name="btn_setup_game_shortcuts.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
<value>360, 50</value>
|
<value>360, 50</value>
|
||||||
@ -10939,7 +10969,7 @@
|
|||||||
<value>NoControl</value>
|
<value>NoControl</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btn_exit.Location" type="System.Drawing.Point, System.Drawing">
|
<data name="btn_exit.Location" type="System.Drawing.Point, System.Drawing">
|
||||||
<value>700, 354</value>
|
<value>700, 360</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btn_exit.Size" type="System.Drawing.Size, System.Drawing">
|
<data name="btn_exit.Size" type="System.Drawing.Size, System.Drawing">
|
||||||
<value>75, 23</value>
|
<value>75, 23</value>
|
||||||
|
Loading…
Reference in New Issue
Block a user