Merge pull request #117 from usaginya/dev

优化通知提示和界面部分功能
This commit is contained in:
MistEO
2022-01-16 22:46:17 +08:00
committed by GitHub
9 changed files with 784 additions and 174 deletions

View File

@@ -11,7 +11,7 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.Uwp.Notifications;
using System.Windows;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stylet;
@@ -73,8 +73,11 @@ namespace MeoAsstGui
_ptr = AsstCreateEx(System.IO.Directory.GetCurrentDirectory(), _callback, IntPtr.Zero);
if (_ptr == IntPtr.Zero)
{
_windowManager.ShowMessageBox("出现未知异常", "错误");
Environment.Exit(0);
Execute.OnUIThread(() =>
{
_windowManager.ShowMessageBox("出现未知异常", "错误", icon: MessageBoxImage.Error);
Environment.Exit(0);
});
}
var tvm = _container.Get<TaskQueueViewModel>();
tvm.Idle = true;
@@ -123,7 +126,7 @@ namespace MeoAsstGui
break;
case AsstMsg.InitFailed:
_windowManager.ShowMessageBox("初始化错误!请检查是否使用了中文路径", "错误");
_windowManager.ShowMessageBox("初始化错误!请检查是否使用了中文路径", "错误", icon: MessageBoxImage.Error);
Environment.Exit(0);
break;
@@ -182,7 +185,10 @@ namespace MeoAsstGui
case AsstMsg.AllTasksCompleted:
mainModel.Idle = true;
mainModel.AddLog("任务已全部完成");
new ToastContentBuilder().AddText("任务已全部完成!").Show();
using (var toast = new ToastNotification("任务已全部完成!"))
{
toast.Show();
}
mainModel.CheckAndShutdown();
break;
}
@@ -190,10 +196,11 @@ namespace MeoAsstGui
private void procSubTaskMsg(AsstMsg msg, JObject details)
{
string taskChain = details["taskchain"].ToString();
string classType = details["class"].ToString();
// 下面几行注释暂时没用到,先注释起来...
//string taskChain = details["taskchain"].ToString();
//string classType = details["class"].ToString();
var mainModel = _container.Get<TaskQueueViewModel>();
//var mainModel = _container.Get<TaskQueueViewModel>();
switch (msg)
{
case AsstMsg.SubTaskError:
@@ -293,7 +300,7 @@ namespace MeoAsstGui
{
case "StageDrops":
{
string cur_drops = "";
string cur_drops = string.Empty;
JArray drops = (JArray)subTaskDetails["drops"];
foreach (var item in drops)
{
@@ -304,7 +311,7 @@ namespace MeoAsstGui
cur_drops = cur_drops.EndsWith("\n") ? cur_drops.TrimEnd('\n') : "无";
mainModel.AddLog("当次掉落:\n" + cur_drops);
string all_drops = "";
string all_drops = string.Empty;
JArray statistics = (JArray)subTaskDetails["stats"];
foreach (var item in statistics)
{
@@ -324,7 +331,7 @@ namespace MeoAsstGui
case "RecruitTagsDetected":
{
JArray tags = (JArray)subTaskDetails["tags"];
string log_content = "";
string log_content = string.Empty;
foreach (var tag_name in tags)
{
string tag_str = tag_name.ToString();
@@ -338,7 +345,10 @@ namespace MeoAsstGui
case "RecruitSpecialTag":
{
string special = subTaskDetails["tag"].ToString();
new ToastContentBuilder().AddText("公招提示" + special).Show();
using (var toast = new ToastNotification("公招提示"))
{
toast.AddContentText(special).ShowRecruit();
}
}
break;
@@ -347,7 +357,10 @@ namespace MeoAsstGui
int level = (int)subTaskDetails["level"];
if (level >= 5)
{
new ToastContentBuilder().AddText("公招出 " + level + " 星了哦!").Show();
using (var toast = new ToastNotification($"公招出 {level} 星了哦!"))
{
toast.AddContentText(new string('★', level)).ShowRecruit(row: 2);
}
mainModel.AddLog(level + " 星 Tags", "darkorange", "Bold");
}
else
@@ -360,7 +373,7 @@ namespace MeoAsstGui
case "RecruitTagsSelected":
{
JArray selected = (JArray)subTaskDetails["tags"];
string selected_log = "";
string selected_log = string.Empty;
foreach (var tag in selected)
{
selected_log += tag.ToString() + "\n";
@@ -404,13 +417,16 @@ namespace MeoAsstGui
case "RecruitSpecialTag":
{
string special = subTaskDetails["tag"].ToString();
new ToastContentBuilder().AddText("公招提示" + special).Show();
using (var toast = new ToastNotification("公招提示"))
{
toast.AddContentText(special).ShowRecruit();
}
}
break;
case "RecruitResult":
{
string resultContent = "";
string resultContent = string.Empty;
JArray result_array = (JArray)subTaskDetails["result"];
int level = (int)subTaskDetails["level"];
foreach (var combs in result_array)
@@ -431,7 +447,10 @@ namespace MeoAsstGui
recruitModel.RecruitResult = resultContent;
if (level >= 5)
{
new ToastContentBuilder().AddText("公招出 " + level + " 星了哦!").Show();
using (var toast = new ToastNotification($"公招出 {level} 星了哦!"))
{
toast.AddContentText(new string('★', level)).ShowRecruit(row: 2);
}
}
}
break;

View File

@@ -9,8 +9,10 @@
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace MeoAsstGui
{
@@ -30,54 +32,40 @@ namespace MeoAsstGui
/// <summary>
/// Just a flag that the binding has been applied.
/// </summary>
private static readonly DependencyProperty VerticalScrollBindingProperty =
DependencyProperty.RegisterAttached("VerticalScrollBinding", typeof(bool?), typeof(ScrollViewerBinding));
private static readonly DependencyProperty VerticalOffsetBindingProperty =
DependencyProperty.RegisterAttached("VerticalOffsetBinding", typeof(bool?), typeof(ScrollViewerBinding));
public static double GetVerticalOffset(DependencyObject depObj)
{
ScrollViewer scrollViewer = depObj as ScrollViewer;
if (scrollViewer == null)
if (!(depObj is ScrollViewer))
return 0;
double maxHeight = scrollViewer.ScrollableHeight;
return (double)depObj.GetValue(VerticalOffsetProperty) / maxHeight;
return (double)depObj.GetValue(VerticalOffsetProperty);
}
public static void SetVerticalOffset(DependencyObject depObj, double value)
{
if (value <= 1)
{
// by set
ScrollViewer scrollViewer = depObj as ScrollViewer;
if (scrollViewer == null)
return;
double maxHeight = scrollViewer.ScrollableHeight;
if (!(depObj is ScrollViewer))
return;
depObj.SetValue(VerticalOffsetProperty, value * maxHeight);
}
else
{
// by scroll
depObj.SetValue(VerticalOffsetProperty, value);
}
depObj.SetValue(VerticalOffsetProperty, value);
}
private static void OnVerticalOffsetPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
private static void OnVerticalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if (scrollViewer == null)
if (!(d is ScrollViewer scrollViewer))
return;
BindVerticalOffset(scrollViewer);
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
public static void BindVerticalOffset(ScrollViewer scrollViewer)
private static void BindVerticalOffset(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(VerticalScrollBindingProperty) != null)
if (scrollViewer.GetValue(VerticalOffsetBindingProperty) != null)
return;
scrollViewer.SetValue(VerticalScrollBindingProperty, true);
scrollViewer.SetValue(VerticalOffsetBindingProperty, true);
scrollViewer.ScrollChanged += (s, se) =>
{
if (se.VerticalChange == 0)
@@ -88,32 +76,187 @@ namespace MeoAsstGui
#endregion VerticalOffset attached property
#region ScrollableHeight attached property
#region ViewportHeight attached property
//public static readonly DependencyProperty ScrollableHeightProperty =
// DependencyProperty.RegisterAttached("ScrollableHeight", typeof(double),
// typeof(ScrollViewerBinding), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnScrollableHeightPropertyChanged));
public static readonly DependencyProperty ViewportHeightProperty =
DependencyProperty.RegisterAttached("ViewportHeight", typeof(double),
typeof(ScrollViewerBinding), new FrameworkPropertyMetadata(double.NaN,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnViewportHeightPropertyChanged));
//public static double GetScrollableHeight(DependencyObject depObj)
//{
// ScrollViewer scrollViewer = depObj as ScrollViewer;
// if (scrollViewer == null)
// return 0;
// return scrollViewer.ScrollableHeight;
//}
private static readonly DependencyProperty ViewportHeightBindingProperty =
DependencyProperty.RegisterAttached("ViewportHeightBinding", typeof(bool?), typeof(ScrollViewerBinding));
//public static void SetScrollableHeight(DependencyObject depObj, double value)
//{
// // pass
//}
public static double GetViewportHeight(DependencyObject depObj)
{
if (!(depObj is ScrollViewer scrollViewer))
return double.NaN;
//private static readonly DependencyProperty ScrollableHeightBindingProperty =
// DependencyProperty.RegisterAttached("ScrollableHeightBinding", typeof(bool?), typeof(ScrollViewerBinding));
return scrollViewer.ViewportHeight;
}
//private static void OnScrollableHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
//{
//}
public static void SetViewportHeight(DependencyObject depObj, double value)
{
if (!(depObj is ScrollViewer))
return;
depObj.SetValue(ViewportHeightProperty, value);
}
private static void OnViewportHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScrollViewer scrollViewer))
return;
BindViewportHeight(scrollViewer);
}
private static void BindViewportHeight(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(ViewportHeightBindingProperty) != null)
return;
scrollViewer.SetValue(ViewportHeightBindingProperty, true);
scrollViewer.Loaded += (s, se) =>
{
SetViewportHeight(scrollViewer, scrollViewer.ViewportHeight);
};
scrollViewer.ScrollChanged += (s, se) =>
{
SetViewportHeight(scrollViewer, se.ViewportHeight);
};
}
#endregion ViewportHeight attached property
#region ExtentHeight attached property
public static readonly DependencyProperty ExtentHeightProperty =
DependencyProperty.RegisterAttached("ExtentHeight", typeof(double),
typeof(ScrollViewerBinding), new FrameworkPropertyMetadata(double.NaN,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnExtentHeightPropertyChanged));
private static readonly DependencyProperty ExtentHeightBindingProperty =
DependencyProperty.RegisterAttached("ExtentHeightBinding", typeof(bool?), typeof(ScrollViewerBinding));
public static double GetExtentHeight(DependencyObject depObj)
{
if (!(depObj is ScrollViewer scrollViewer))
return double.NaN;
return scrollViewer.ExtentHeight;
}
public static void SetExtentHeight(DependencyObject depObj, double value)
{
if (!(depObj is ScrollViewer))
return;
depObj.SetValue(ExtentHeightProperty, value);
}
private static void OnExtentHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScrollViewer scrollViewer))
return;
BindExtentHeight(scrollViewer);
}
private static void BindExtentHeight(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(ExtentHeightBindingProperty) != null)
return;
scrollViewer.SetValue(ExtentHeightBindingProperty, true);
scrollViewer.Loaded += (s, se) =>
{
SetExtentHeight(scrollViewer, scrollViewer.ExtentHeight);
};
}
#endregion ExtentHeight attached property
#region RectangleVerticalOffsetList attached property
/// <summary>
/// RectangleIndex attached property
/// </summary>
public static readonly DependencyProperty RectangleVerticalOffsetListProperty =
DependencyProperty.RegisterAttached("RectangleVerticalOffsetList",
typeof(List<double>),
typeof(ScrollViewerBinding),
new FrameworkPropertyMetadata(new List<double>(),
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnRectangleVerticalOffsetListPropertyChanged));
/// <summary>
/// Just a flag that the binding has been applied.
/// </summary>
private static readonly DependencyProperty RectangleVerticalOffsetListBindingProperty =
DependencyProperty.RegisterAttached("RectangleVerticalOffsetListBinding", typeof(bool?), typeof(ScrollViewerBinding));
public static List<double> GetRectangleVerticalOffsetList(DependencyObject depObj)
{
return (List<double>)depObj.GetValue(RectangleVerticalOffsetListProperty);
}
public static void SetRectangleVerticalOffsetList(DependencyObject depObj, List<double> value)
{
if (!(depObj is ScrollViewer))
return;
depObj.SetValue(RectangleVerticalOffsetListProperty, value);
}
private static void OnRectangleVerticalOffsetListPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScrollViewer scrollViewer))
return;
BindRectangleVerticalOffsetList(scrollViewer);
}
private static void BindRectangleVerticalOffsetList(ScrollViewer scrollViewer)
{
if (scrollViewer.GetValue(RectangleVerticalOffsetListBindingProperty) != null)
return;
scrollViewer.SetValue(RectangleVerticalOffsetListBindingProperty, true);
// 当滚动条载入时,遍历 StackPanel 中的所有 Rectangle 子元素对应位置
scrollViewer.Loaded += (s, se) =>
{
if (!scrollViewer.HasContent || !(scrollViewer.Content is StackPanel))
return;
var rectangleOffsetList = new List<double>();
var stackPanel = (StackPanel)scrollViewer.Content;
var point = new Point(0, scrollViewer.VerticalOffset);
foreach (var child in stackPanel.Children)
{
// 以 Rectangle 为定位元素
if (!(child is Rectangle))
continue;
// 转换计算并保存 Rectangle 在滚动面板中的垂直位置
var targetPosition = ((FrameworkElement)child).TransformToVisual(scrollViewer).Transform(point);
rectangleOffsetList.Add(targetPosition.Y);
}
if (rectangleOffsetList.Count > 0)
SetRectangleVerticalOffsetList(scrollViewer, rectangleOffsetList);
};
}
#endregion RectangleVerticalOffsetList attached property
#endregion ScrollableHeight attached property
}
}

View File

@@ -0,0 +1,373 @@
// MeoAsstGui - A part of the MeoAssistantArknights project
// Copyright (C) 2021 MistEO and Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using System;
using System.ComponentModel;
using System.Drawing;
using System.Media;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Notification.Wpf;
using Notification.Wpf.Base;
using Notification.Wpf.Constants;
using Notification.Wpf.Controls;
namespace MeoAsstGui
{
public class ToastNotification : IDisposable
{
private NotificationManager _notificationManager = new NotificationManager();
/// <summary>
/// 通知标题
/// </summary>
private string _notificationTitle = string.Empty;
/// <summary>
/// 通知文本内容
/// </summary>
private StringBuilder _contentCollection = new StringBuilder();
/// <summary>
/// 应用图标资源
/// </summary>
private ImageSource GetAppIcon()
{
try
{
var icon = Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath);
var imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
catch (Exception)
{
return new BitmapImage();
}
}
/// <summary>
/// Toast通知基于 Notification.Wpf 实现,方便管理通知样式
/// <para>建议使用 using 调用,如果不使用 using 调用,建议手动调用 Dispose() 方法释放</para>
/// </summary>
/// <param name="title">通知标题</param>
public ToastNotification(string title = null)
{
// 初始化通知标题
_notificationTitle = title ?? _notificationTitle;
#region Notification.Wpf
// 同时显示最大数量
NotificationConstants.NotificationsOverlayWindowMaxCount = 5;
// 默认显示位置
NotificationConstants.MessagePosition = NotificationPosition.BottomRight;
// 最小显示宽度
NotificationConstants.MinWidth = 350d;
// 最大显示宽度
NotificationConstants.MaxWidth = 380d;
#endregion
}
/// <summary>
/// 添加一行内容文本
/// </summary>
/// <param name="text">文本内容</param>
/// <returns>返回类本身,可继续调用其它方法</returns>
public ToastNotification AddContentText(string text = null)
{
_contentCollection.AppendLine(text ?? string.Empty);
return this;
}
#region
#region
public enum NotificationSounds
{
[Description("默认响声")]
Beep,
[Description("感叹号")]
Exclamation,
[Description("星号")]
Asterisk,
[Description("问题")]
Question,
[Description("关键性停止")]
Hand,
[Description("通知 (Windows 10 及以上特有,低版本系统直接用也可以)")]
Notification,
[Description("无声")]
None
}
#endregion
/// <summary>
/// 播放通知提示音
/// 如果要播放音频文件,参考微软文档 SoundPlayer 类
/// </summary>
/// <param name="sounds">提示音类型</param>
protected async Task PlayNotificationSoundAsync(NotificationSounds sounds = NotificationSounds.None)
{
await Task.Run(() =>
{
try
{
switch (sounds)
{
case NotificationSounds.Exclamation: SystemSounds.Exclamation.Play(); break;
case NotificationSounds.Asterisk: SystemSounds.Asterisk.Play(); break;
case NotificationSounds.Question: SystemSounds.Question.Play(); break;
case NotificationSounds.Beep: SystemSounds.Beep.Play(); break;
case NotificationSounds.Hand: SystemSounds.Hand.Play(); break;
case NotificationSounds.Notification:
using (var key = Registry.CurrentUser.OpenSubKey(@"AppEvents\Schemes\Apps\.Default\Notification.Default\.Current"))
{
if (key != null)
{
// 获取 (Default) 项
var o = key.GetValue(null);
if (o != null)
{
var theSound = new SoundPlayer((string)o);
theSound.Play();
}
}
else
{
// 如果不支持就播放其它提示音
PlayNotificationSoundAsync(NotificationSounds.Asterisk);
}
}
break;
case NotificationSounds.None:
default: break;
}
}
catch (Exception) { }
}).ConfigureAwait(false);
}
#endregion
#region
#region
// 左边按钮
protected string _buttonLeftText = null;
protected Action _buttonLeftAction = null;
// 右边按钮
protected string _buttonRightText = null;
protected Action _buttonRightAction = null;
#endregion
/// <summary>
/// 给通知添加一个在左边的按钮,比如确定按钮,多次设置只会按最后一次设置生效
/// </summary>
/// <param name="text">按钮标题</param>
/// <param name="action">按钮执行方法</param>
/// <returns>返回类本身,可继续调用其它方法</returns>
public ToastNotification AddButtonLeft(string text, Action action)
{
_buttonLeftText = text;
_buttonLeftAction = action;
return this;
}
/// <summary>
/// 给通知添加一个在右边的按钮,比如取消按钮,多次设置只会按最后一次设置生效
/// </summary>
/// <param name="text">按钮标题</param>
/// <param name="action">按钮执行方法</param>
/// <returns>返回类本身,可继续调用其它方法</returns>
public ToastNotification AddButtonRight(string text, Action action)
{
_buttonRightText = text;
_buttonRightAction = action;
return this;
}
#endregion
#region
#region
/// <summary>
/// 创建一个基本文本字体样式
/// </summary>
public TextContentSettings BaseTextSettings => new TextContentSettings()
{
FontStyle = FontStyles.Normal,
FontFamily = new System.Windows.Media.FontFamily("Microsoft Yahei"),
FontSize = 14,
FontWeight = FontWeights.Normal,
TextAlignment = TextAlignment.Left,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalTextAlignment = VerticalAlignment.Stretch,
Opacity = 1d
};
/// <summary>
/// 创建一个基本通知内容模板
/// </summary>
public NotificationContent BaseContent()
{
var content = new NotificationContent()
{
Title = _notificationTitle,
Message = _contentCollection.ToString(),
Type = NotificationType.None,
Icon = GetAppIcon(),
CloseOnClick = true,
RowsCount = 1,
TrimType = NotificationTextTrimType.AttachIfMoreRows,
Background = (SolidColorBrush)new BrushConverter().ConvertFrom("#FF332A3A"),
LeftButtonContent = _buttonLeftText ?? NotificationConstants.DefaultLeftButtonContent,
LeftButtonAction = _buttonLeftAction ?? null,
RightButtonContent = _buttonRightText ?? NotificationConstants.DefaultRightButtonContent,
RightButtonAction = _buttonRightAction ?? null,
};
// 默认的标题文本样式
var titleTextSettings = BaseTextSettings;
titleTextSettings.FontSize = 18d;
content.TitleTextSettings = titleTextSettings;
// 默认的内容文本样式
var messageTextSettings = BaseTextSettings;
content.MessageTextSettings = messageTextSettings;
return content;
}
#endregion
#region
/// <summary>
/// 显示通知
/// </summary>
/// <param name="lifeTime">通知显示时间 (s)</param>
/// <param name="row">内容显示行数,如果内容太多建议使用 ShowMore()</param>
/// <param name="sound">播放提示音</param>
/// <param name="notificationContent">通知内容</param>
public void Show(double lifeTime = 10d, uint row = 1,
NotificationSounds sound = NotificationSounds.Notification,
NotificationContent notificationContent = null)
{
notificationContent = notificationContent ?? BaseContent();
notificationContent.RowsCount = row;
// 调整显示时间,如果存在按钮的情况下显示时间将强制设为最大时间
lifeTime = lifeTime < 3d ? 3d : lifeTime;
var timeSpan = _buttonLeftAction == null && _buttonRightAction == null
? TimeSpan.FromSeconds(lifeTime)
: TimeSpan.MaxValue;
// 显示通知
_notificationManager.Show(
notificationContent,
expirationTime: timeSpan,
ShowXbtn: false);
// 播放通知提示音
PlayNotificationSoundAsync(sound);
}
/// <summary>
/// 显示内容更多的通知
/// </summary>
/// <param name="lifeTime">通知显示时间 (s)</param>
/// <param name="row">内容显示行数,只用于预览一部分通知,多出内容会放在附加按钮的窗口中</param>
/// <param name="sound">播放提示音,不设置就没有声音</param>
/// <param name="notificationContent">通知内容</param>
public void ShowMore(double lifeTime = 12d, uint row = 2,
NotificationSounds sound = NotificationSounds.None,
NotificationContent notificationContent = null)
{
notificationContent = notificationContent ?? BaseContent();
notificationContent.TrimType = NotificationTextTrimType.Attach;
Show(lifeTime: lifeTime,
row: row,
sound: sound,
notificationContent: notificationContent);
}
/// <summary>
/// 显示公招特殊标签通知
/// </summary>
/// <param name="row">内容显示行数,比如第 2 行用来放星星</param>
public void ShowRecruit(uint row = 1)
{
var content = BaseContent();
// 给通知染上资深标签相似的颜色
content.Background = (SolidColorBrush)new BrushConverter().ConvertFrom("#FF401804");
content.Foreground = (SolidColorBrush)new BrushConverter().ConvertFrom("#FFFFC800");
Show(row: row,
sound: NotificationSounds.Notification,
notificationContent: content);
}
#endregion
#endregion
/// <summary>
/// 通知使用完后释放已使用的数据
/// </summary>
public void Dispose()
{
_contentCollection.Clear();
_notificationManager = null;
_notificationTitle = null;
_contentCollection = null;
_buttonLeftText = _buttonRightText = null;
_buttonLeftAction = _buttonRightAction = null;
}
}
}

View File

@@ -47,12 +47,9 @@ namespace MeoAsstGui
using (StreamReader sr = new StreamReader(_configFilename))
{
string jsonStr = sr.ReadToEnd();
_viewStatus = (JObject)JsonConvert.DeserializeObject(jsonStr);
// 文件存在但为空会读出来一个null感觉c#这库有bug
if (_viewStatus == null)
{
_viewStatus = new JObject();
}
// 文件存在但为空会读出来一个null感觉c#这库有bug如果是null 就赋值一个空JObject
_viewStatus = (JObject)JsonConvert.DeserializeObject(jsonStr) ?? new JObject();
}
}
catch (Exception)

View File

@@ -91,6 +91,7 @@
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Numerics" />
<Reference Include="System.Windows" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
@@ -114,6 +115,7 @@
<Compile Include="Helper\AutoScroll.cs" />
<Compile Include="Helper\CombData.cs" />
<Compile Include="Helper\LogItemViewModel.cs" />
<Compile Include="Helper\ToastNotification.cs" />
<Compile Include="Helper\ScrollViewerBinding.cs" />
<Compile Include="Helper\ViewStatusStorage.cs" />
<Compile Include="Helper\FlowDocumentPagePadding.cs" />
@@ -222,6 +224,7 @@
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<None Include=".editorconfig" />
<None Include="Properties\Settings.settings">
@@ -267,9 +270,6 @@
<PackageReference Include="Microsoft.NETCore.Platforms">
<Version>5.0.4</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
<Version>7.1.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Win32.Primitives">
<Version>4.3.0</Version>
</PackageReference>
@@ -279,6 +279,9 @@
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
</PackageReference>
<PackageReference Include="Notification.Wpf">
<Version>6.1.0.1</Version>
</PackageReference>
<PackageReference Include="Stylet">
<Version>1.3.6</Version>
</PackageReference>

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Stylet;
using StyletIoC;
@@ -121,7 +122,7 @@ namespace MeoAsstGui
public List<CombData> UsesOfDronesList { get; set; }
private int _dormThreshold = System.Convert.ToInt32(ViewStatusStorage.Get("Infrast.DormThreshold", "30"));
private int _dormThreshold = Convert.ToInt32(ViewStatusStorage.Get("Infrast.DormThreshold", "30"));
public int DormThreshold
{
@@ -191,75 +192,109 @@ namespace MeoAsstGui
}
}
// 消息源01SelectedIndex2ScrollOffset
private int _notifySource = 0;
#region
private void resetNotifySource()
private enum NotifyType
{
System.Timers.Timer t = new System.Timers.Timer(20);
None,
SelectedIndex,
ScrollOffset
}
private NotifyType _notifySource = NotifyType.None;
private void ResetNotifySource()
{
var t = new System.Timers.Timer(20);
t.Elapsed += new System.Timers.ElapsedEventHandler(delegate (object source, System.Timers.ElapsedEventArgs e)
{
_notifySource = 0;
_notifySource = NotifyType.None;
});
t.AutoReset = false;
t.Enabled = true;
t.Start();
}
public double ScrollViewportHeight { get; set; }
public double ScrollExtentHeight { get; set; }
public List<double> RectangleVerticalOffsetList { get; set; }
private int _selectedIndex = 0;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
if (_notifySource == 0)
switch (_notifySource)
{
_notifySource = 1;
SetAndNotify(ref _selectedIndex, value);
ScrollOffset = (double)value / ListTitle.Count;
//_notifySource = 0;
resetNotifySource();
}
else if (_notifySource == 2)
{
SetAndNotify(ref _selectedIndex, value);
case NotifyType.None:
_notifySource = NotifyType.SelectedIndex;
SetAndNotify(ref _selectedIndex, value);
var isInRange = RectangleVerticalOffsetList != null
&& RectangleVerticalOffsetList.Count > 0
&& value < RectangleVerticalOffsetList.Count;
if (isInRange)
ScrollOffset = RectangleVerticalOffsetList[value];
ResetNotifySource();
break;
case NotifyType.ScrollOffset:
SetAndNotify(ref _selectedIndex, value);
break;
}
}
}
private double _scrollOffset = 0;
public double ScrollOffset
{
get { return _scrollOffset; }
set
{
if (_notifySource == 0)
switch (_notifySource)
{
_notifySource = 2;
SetAndNotify(ref _scrollOffset, value);
SelectedIndex = (int)(value / ScrollHeight * ListTitle.Count);
//_notifySource = 0;
resetNotifySource();
}
else if (_notifySource == 1)
{
SetAndNotify(ref _scrollOffset, value);
case NotifyType.None:
_notifySource = NotifyType.ScrollOffset;
SetAndNotify(ref _scrollOffset, value);
// 设置 ListBox SelectedIndex 为当前 ScrollOffset 索引
var isInRange = RectangleVerticalOffsetList != null && RectangleVerticalOffsetList.Count > 0;
if (isInRange)
{
// 滚动条滚动到底部,返回最后一个 Rectangle 索引
if (ScrollOffset + ScrollViewportHeight >= ScrollExtentHeight)
{
SelectedIndex = RectangleVerticalOffsetList.Count - 1;
ResetNotifySource();
break;
}
// 根据出当前 ScrollOffset 选出最后一个在可视范围的 Rectangle 索引
var rectangleSelect = RectangleVerticalOffsetList.Select((n, i) => (
rectangleAppeared: value + 10 >= n,
index: i
));
var index = rectangleSelect.LastOrDefault(n => n.rectangleAppeared).index;
SelectedIndex = index;
}
ResetNotifySource();
break;
case NotifyType.SelectedIndex:
SetAndNotify(ref _scrollOffset, value);
break;
}
}
}
private double _scrollHeight = 684; // ScrollViewer.ScrollableHeight, 不知道该咋获取,当前的默认值是这个
public double ScrollHeight
{
get { return _scrollHeight; }
set
{
SetAndNotify(ref _scrollHeight, value);
}
}
#endregion
/* 信用商店设置 */
@@ -371,7 +406,7 @@ namespace MeoAsstGui
}
}
private string _proxy = ViewStatusStorage.Get("VersionUpdate.Proxy", "");
private string _proxy = ViewStatusStorage.Get("VersionUpdate.Proxy", string.Empty);
public string Proxy
{

View File

@@ -59,14 +59,16 @@ namespace MeoAsstGui
}
TaskItemViewModels = new ObservableCollection<DragItemViewModel>(temp_order_list);
StageList = new List<CombData>();
StageList.Add(new CombData { Display = "当前关卡", Value = "" });
StageList.Add(new CombData { Display = "上次作战", Value = "LastBattle" });
StageList.Add(new CombData { Display = "剿灭作战", Value = "Annihilation" });
StageList.Add(new CombData { Display = "龙门币-5", Value = "CE-5" });
StageList.Add(new CombData { Display = "红票-5", Value = "AP-5" });
StageList.Add(new CombData { Display = "经验-5", Value = "LS-5" });
StageList.Add(new CombData { Display = "技能-5", Value = "CA-5" });
StageList = new List<CombData>
{
new CombData { Display = "当前关卡", Value = string.Empty },
new CombData { Display = "上次作战", Value = "LastBattle" },
new CombData { Display = "剿灭作战", Value = "Annihilation" },
new CombData { Display = "龙门币-5", Value = "CE-5" },
new CombData { Display = "红票-5", Value = "AP-5" },
new CombData { Display = "经验-5", Value = "LS-5" },
new CombData { Display = "技能-5", Value = "CA-5" }
};
// “风雪过境” 活动关卡
//StageList.Add(new CombData { Display = "BI-7", Value = "BI-7" });
//StageList.Add(new CombData { Display = "BI-8", Value = "BI-8" });
@@ -87,6 +89,8 @@ namespace MeoAsstGui
{
ClearLog();
SaveSettingValue();
AddLog("正在连接模拟器……");
var asstProxy = _container.Get<AsstProxy>();
@@ -177,6 +181,19 @@ namespace MeoAsstGui
Idle = true;
}
/// <summary>
/// 保存界面设置数值
/// </summary>
private void SaveSettingValue()
{
// 吃理智药个数
ViewStatusStorage.Set("MainFunction.UseMedicine.Quantity", MedicineNumber);
// 吃石头颗数
ViewStatusStorage.Set("MainFunction.UseStone.Quantity", StoneNumber);
// 指定刷关次数
ViewStatusStorage.Set("MainFunction.TimesLimited.Quantity", MaxTimes);
}
private bool appendFight()
{
int medicine = 0;
@@ -272,11 +289,11 @@ namespace MeoAsstGui
public void CheckAndShutdown()
{
if (Shutdown == true)
if (Shutdown)
{
System.Diagnostics.Process.Start("shutdown.exe", "-s -t 60");
var result = _windowManager.ShowMessageBox("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK);
var result = _windowManager.ShowMessageBox("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK, MessageBoxImage.Question);
if (result == MessageBoxResult.OK)
{
System.Diagnostics.Process.Start("shutdown.exe", "-a");
@@ -334,7 +351,7 @@ namespace MeoAsstGui
}
}
private string _medicineNumber = "999";
private string _medicineNumber = ViewStatusStorage.Get("MainFunction.UseMedicine.Quantity", "999");
public string MedicineNumber
{
@@ -360,7 +377,7 @@ namespace MeoAsstGui
}
}
private string _stoneNumber = "0";
private string _stoneNumber = ViewStatusStorage.Get("MainFunction.UseStone.Quantity", "0");
public string StoneNumber
{
@@ -382,7 +399,7 @@ namespace MeoAsstGui
}
}
private string _maxTimes = "5";
private string _maxTimes = ViewStatusStorage.Get("MainFunction.TimesLimited.Quantity", "5");
public string MaxTimes
{

View File

@@ -17,7 +17,6 @@ using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using Microsoft.Toolkit.Uwp.Notifications;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stylet;
@@ -116,10 +115,14 @@ namespace MeoAsstGui
{
return false;
}
new ToastContentBuilder()
.AddText("检测到新版本包,正在解压,请稍等……")
.AddText(UpdateTag)
.Show();
using (var toast = new ToastNotification("检测到新版本包"))
{
toast.AddContentText("正在解压,请稍等……")
.AddContentText(UpdateTag)
.ShowMore();
}
string extractDir = Directory.GetCurrentDirectory() + "\\NewVersionExtract";
// 解压
System.IO.Compression.ZipFile.ExtractToDirectory(UpdatePackageName, extractDir);
@@ -186,15 +189,23 @@ namespace MeoAsstGui
UpdateTag = _lastestJson["name"].ToString();
UpdateInfo = _lastestJson["body"].ToString();
var openUrlToastButton = new ToastButton();
openUrlToastButton.SetContent("前往页面查看");
openUrlToastButton.SetProtocolActivation(new Uri(_viewUrl));
new ToastContentBuilder()
.AddText("检测到新版本,正在后台下载……")
.AddText(UpdateTag)
.AddText(UpdateInfo)
.AddButton(openUrlToastButton)
.Show();
var openUrlToastButton = (
text: "前往页面查看",
action: new Action(() =>
{
Process.Start(_viewUrl);
})
);
using (var toast = new ToastNotification("检测到新版本"))
{
toast.AddContentText("正在后台下载……")
.AddContentText(UpdateTag)
.AddContentText(UpdateInfo)
.AddButtonLeft(openUrlToastButton.text, openUrlToastButton.action)
.ShowMore(row: 3);
}
// 下载压缩包
const int downloadRetryMaxTimes = 2;
string downloadTempFilename = UpdatePackageName + ".tmp";
@@ -208,23 +219,30 @@ namespace MeoAsstGui
break;
}
}
if (!downloaded)
{
new ToastContentBuilder()
.AddText("新版本下载失败")
.AddText("请尝试手动下载后将压缩包放到目录下_(:з」∠)_")
.AddButton(openUrlToastButton)
.Show();
using (var toast = new ToastNotification("新版本下载失败"))
{
toast.AddContentText("请尝试手动下载后将压缩包放到目录下_(:з」∠)_")
.AddButtonLeft(openUrlToastButton.text, openUrlToastButton.action)
.Show();
}
return false;
}
File.Copy(downloadTempFilename, UpdatePackageName, true);
File.Delete(downloadTempFilename);
// 把相关信息存下来,更新完之后启动的时候显示
new ToastContentBuilder()
.AddText("新版本下载完成")
.AddText("软件将在下次启动时自动更新!")
.AddText("✿✿ヽ(°▽°)ノ✿")
.Show();
using (var toast = new ToastNotification("新版本下载完成"))
{
toast.AddContentText("软件将在下次启动时自动更新!")
.AddContentText("✿✿ヽ(°▽°)ノ✿")
.Show(lifeTime: 10d, row: 2);
}
return true;
}
@@ -293,18 +311,19 @@ namespace MeoAsstGui
private string RequestApi(string url)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
httpWebRequest.Method = "GET";
httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36";
httpWebRequest.Accept = "application/vnd.github.v3+json";
var settings = _container.Get<SettingsViewModel>();
if (settings.Proxy.Length > 0)
{
httpWebRequest.Proxy = new WebProxy(settings.Proxy);
}
//httpWebRequest.Timeout = 20000;
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
httpWebRequest.Method = "GET";
httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36";
httpWebRequest.Accept = "application/vnd.github.v3+json";
var settings = _container.Get<SettingsViewModel>();
if (settings.Proxy.Length > 0)
{
httpWebRequest.Proxy = new WebProxy(settings.Proxy);
}
//httpWebRequest.Timeout = 20000;
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8);
string responseContent = streamReader.ReadToEnd();
@@ -423,18 +442,20 @@ namespace MeoAsstGui
ViewStatusStorage.Set(filename, cloud_sha);
File.Delete(tempname);
}
if (updated)
{
new ToastContentBuilder()
.AddText("资源已更新,重启软件生效!")
.AddText(message)
.Show();
return true;
}
else
if (!updated)
{
return false;
}
using (var toast = new ToastNotification("资源已更新"))
{
toast.AddContentText("重启软件生效!")
.AddContentText(message)
.ShowMore();
}
return true;
}
private static void CopyFilesRecursively(string sourcePath, string targetPath)
@@ -456,8 +477,8 @@ namespace MeoAsstGui
{
RequestClose();
IsFirstBootAfterUpdate = false;
UpdateTag = "";
UpdateInfo = "";
UpdateTag = string.Empty;
UpdateInfo = string.Empty;
}
}
}

View File

@@ -16,9 +16,11 @@
<ListBox x:Name="MasterListBox" ItemsSource="{Binding ListTitle}" Grid.Column="0" Margin="10"
IsSynchronizedWithCurrentItem="true" SelectedIndex="{Binding SelectedIndex}" />
<ScrollViewer local:ScrollViewerBinding.VerticalOffset="{Binding ScrollOffset}"
HorizontalAlignment="Stretch" Grid.Column="1" Margin="20, 10">
<!--local:ScrollViewerBinding.ScrollableHeight="{Binding ScrollHeight, Mode=OneWayToSource}"-->
<ScrollViewer HorizontalAlignment="Stretch" Grid.Column="1" Margin="20, 10"
local:ScrollViewerBinding.ViewportHeight="{Binding ScrollViewportHeight}"
local:ScrollViewerBinding.ExtentHeight="{Binding ScrollExtentHeight}"
local:ScrollViewerBinding.VerticalOffset="{Binding ScrollOffset}"
local:ScrollViewerBinding.RectangleVerticalOffsetList="{Binding RectangleVerticalOffsetList}">
<StackPanel IsEnabled="{Binding Idle}">
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="1" />
<TextBlock Style="{StaticResource TextBlockDefault}" Text="{Binding ListTitle[0]}" Foreground="Gray" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left" />