diff --git a/DM_Weight.Commons/DM_Weight.Commons.csproj b/DM_Weight.Commons/DM_Weight.Commons.csproj new file mode 100644 index 0000000..467963e --- /dev/null +++ b/DM_Weight.Commons/DM_Weight.Commons.csproj @@ -0,0 +1,13 @@ + + + + net6.0-windows + enable + true + + + + + + + diff --git a/DM_Weight.Commons/ValidatRules/ValidationErrorMappingBehavior.cs b/DM_Weight.Commons/ValidatRules/ValidationErrorMappingBehavior.cs new file mode 100644 index 0000000..fce3c87 --- /dev/null +++ b/DM_Weight.Commons/ValidatRules/ValidationErrorMappingBehavior.cs @@ -0,0 +1,75 @@ +using Microsoft.Xaml.Behaviors; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Controls; + +namespace DM_Weight.Commons.ValidatRules +{ + public class ValidationErrorMappingBehavior : Behavior + { + #region Properties + + public static readonly DependencyProperty ValidationErrorsProperty = + DependencyProperty.Register("ValidationErrors", typeof(ObservableCollection), + typeof(ValidationErrorMappingBehavior), new PropertyMetadata(new ObservableCollection())); + + public ObservableCollection ValidationErrors + { + get { return (ObservableCollection)this.GetValue(ValidationErrorsProperty); } + set { this.SetValue(ValidationErrorsProperty, value); } + } + + public static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register("HasValidationError", + typeof(bool), typeof(ValidationErrorMappingBehavior), new PropertyMetadata(false)); + + public bool HasValidationError + { + get { return (bool)this.GetValue(HasValidationErrorProperty); } + set { this.SetValue(HasValidationErrorProperty, value); } + } + + #endregion + + #region Constructors + + public ValidationErrorMappingBehavior() + : base() + { } + + #endregion + + #region Events & Event Methods + + private void Validation_Error(object sender, ValidationErrorEventArgs e) + { + if (e.Action == ValidationErrorEventAction.Added) + { + this.ValidationErrors.Add(e.Error); + } + else + { + this.ValidationErrors.Remove(e.Error); + } + + this.HasValidationError = this.ValidationErrors.Count > 0; + } + + #endregion + + #region Support Methods + + protected override void OnAttached() + { + base.OnAttached(); + Validation.AddErrorHandler(this.AssociatedObject, Validation_Error); + } + + protected override void OnDetaching() + { + base.OnDetaching(); + Validation.RemoveErrorHandler(this.AssociatedObject, Validation_Error); + } + + #endregion + } +} diff --git a/DM_Weight.sln b/DM_Weight.sln new file mode 100644 index 0000000..cd46efa --- /dev/null +++ b/DM_Weight.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32922.545 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DM_Weight", "DM_Weight\DM_Weight.csproj", "{439FA76B-F874-40DB-BAF2-E3647CD55B10}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DM_Weight.Commons", "DM_Weight.Commons\DM_Weight.Commons.csproj", "{7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|x64.ActiveCfg = Debug|x64 + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|x64.Build.0 = Debug|x64 + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|x86.ActiveCfg = Debug|x86 + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Debug|x86.Build.0 = Debug|x86 + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|Any CPU.Build.0 = Release|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|x64.ActiveCfg = Release|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|x64.Build.0 = Release|Any CPU + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|x86.ActiveCfg = Debug|x86 + {439FA76B-F874-40DB-BAF2-E3647CD55B10}.Release|x86.Build.0 = Debug|x86 + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|x64.Build.0 = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Debug|x86.Build.0 = Debug|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|Any CPU.Build.0 = Release|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|x64.ActiveCfg = Release|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|x64.Build.0 = Release|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|x86.ActiveCfg = Release|Any CPU + {7F9FA18B-5C28-476E-97D4-B5504B8DEB9B}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E50E8179-1102-41F1-92F5-2905C75898A6} + EndGlobalSection +EndGlobal diff --git a/DM_Weight/App.config b/DM_Weight/App.config new file mode 100644 index 0000000..d80df9f --- /dev/null +++ b/DM_Weight/App.config @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DM_Weight/App.xaml b/DM_Weight/App.xaml new file mode 100644 index 0000000..ef8d445 --- /dev/null +++ b/DM_Weight/App.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/DM_Weight/App.xaml.cs b/DM_Weight/App.xaml.cs new file mode 100644 index 0000000..5913d5b --- /dev/null +++ b/DM_Weight/App.xaml.cs @@ -0,0 +1,264 @@ + +using DM_Weight.util.TabTip; +using DM_Weight.util; +using DM_Weight.ViewModels; +using DM_Weight.Views.Dialog; +using DM_Weight.Views; +using log4net.Config; +using Prism.Ioc; +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration; +using System.Data; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using Prism.Unity; +using log4net; +using System.Windows.Interop; +using System.Windows.Threading; +using System.Timers; +using DM_Weight.HIKVISION; + +namespace DM_Weight +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : PrismApplication + { + private readonly ILog logger = LogManager.GetLogger(typeof(App)); + public App() + { + TabTipAutomation.IgnoreHardwareKeyboard = HardwareKeyboardIgnoreOptions.IgnoreAll; + TabTipAutomation.BindTo(); + TabTipAutomation.BindTo(); + } + + + + protected override Window CreateShell() + { + //UI线程未捕获异常处理事件 + this.DispatcherUnhandledException += OnDispatcherUnhandledException; + //Task线程内未捕获异常处理事件 + TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; + //多线程异常 + AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; + + return Container.Resolve(); + } + + void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + logger.Error($"发生错误:{e.Exception.Message}"); + e.Handled = true; + } + + void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + + foreach (Exception item in e.Exception.InnerExceptions) + { + logger.Error($"异常类型:{item.GetType()}{Environment.NewLine}来自:{item.Source}{Environment.NewLine}异常内容:{item.Message}"); + } + + //将异常标识为已经观察到 + e.SetObserved(); + } + + void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + logger.Error($"Unhandled exception.{e.ToString()}"); + } + + + protected override void InitializeShell(Window shell) + { + + base.InitializeShell(shell); + } + + protected override void RegisterTypes(IContainerRegistry containerRegistry) + { + + //日期加时间页面 + containerRegistry.RegisterDialog(); + containerRegistry.RegisterForNavigation(); + // 注入日志 + XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config")); + //containerRegistry.RegisterInstance(LogManager.GetLogger("")); + + // 串口工具 + //containerRegistry.RegisterSingleton(); + // 指纹机工具 + //containerRegistry.RegisterSingleton(); + // 组态屏工具 + //containerRegistry.RegisterSingleton(); + // 录像机 + //containerRegistry.RegisterSingleton(); + + containerRegistry.Register(); + + // 主窗口 + containerRegistry.Register(); + containerRegistry.RegisterForNavigation(); + + // 分页 + //containerRegistry.Register(); + //containerRegistry.Register(); + + // 登录页面 + containerRegistry.RegisterForNavigation(); + + // 布局页面 + containerRegistry.RegisterForNavigation(); + + // 录入指纹模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + + #region 取药 + + ////交接柜补药 + //containerRegistry.RegisterForNavigation(); + ////交接柜补药页面弹窗 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + + + //// 处方取药页面 + //containerRegistry.RegisterForNavigation(); + //// 处方取药模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 调拨取药页面 + //containerRegistry.RegisterForNavigation(); + //// 调拨取药模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 抽屉取药页面 + //containerRegistry.RegisterForNavigation(); + //// 自选取药模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 自选取药页面 + //containerRegistry.RegisterForNavigation(); + + ////多处方取药 + //containerRegistry.RegisterForNavigation(); + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + #endregion + + #region 加药 + // 自选加药页面 + //containerRegistry.RegisterForNavigation(); + //// 调拨加药页面 + //containerRegistry.RegisterForNavigation(); + + //// 调拨取药模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 抽屉加药页面 + //containerRegistry.RegisterForNavigation(); + //// 自选加药模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + ////多批次抽屉加药 + //containerRegistry.RegisterForNavigation(); + ////药品请领 + //containerRegistry.RegisterForNavigation(); + //// 药品请领模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + ////请领列表 + //containerRegistry.RegisterForNavigation(); + ////请领入库 + //containerRegistry.RegisterForNavigation(); + + #endregion + + #region 还药 + // 还药页面 + //containerRegistry.RegisterForNavigation(); + //// 按记录归还药品模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + //// 还药页面2 + //containerRegistry.RegisterForNavigation(); + //// 按处方归还药品模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 还空瓶页面 + //containerRegistry.RegisterForNavigation(); + //// 归还空瓶模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 空瓶销毁模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + #endregion + + #region 库存管理 + // 库存列表页面 + //containerRegistry.RegisterForNavigation(); + //// 库位绑定模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + ////同一药品多批次库位绑定 + //containerRegistry.RegisterForNavigation(); + //// 库存盘点页面 + //containerRegistry.RegisterForNavigation(); + //// 药品列表页面 + //containerRegistry.RegisterForNavigation(); + + ////交接班记录 + //containerRegistry.RegisterForNavigation(); + ////交接班弹窗 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + #endregion + + #region 系统设置 + // 用户管理页面 + //containerRegistry.RegisterForNavigation(); + //// 编辑用户模态框 + //containerRegistry.RegisterDialog(); + //containerRegistry.RegisterForNavigation(); + //// 角色管理页面 + //containerRegistry.RegisterForNavigation(); + //// 系统设置 + //containerRegistry.RegisterForNavigation(); + //// 调试页面 + //containerRegistry.RegisterForNavigation(); + ////主设置页面 + //containerRegistry.RegisterForNavigation(); + ////两个冰箱抽屉设置页面 + //containerRegistry.RegisterForNavigation(); + ////只有一个冰箱抽屉设置页面 + //containerRegistry.RegisterForNavigation(); + + #endregion + + // 设备记录页面 + //containerRegistry.RegisterForNavigation(); + + //containerRegistry.RegisterForNavigation(); + + + } + } +} diff --git a/DM_Weight/AssemblyInfo.cs b/DM_Weight/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/DM_Weight/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/DM_Weight/Common/CRC16MODBUS.cs b/DM_Weight/Common/CRC16MODBUS.cs new file mode 100644 index 0000000..6384cf7 --- /dev/null +++ b/DM_Weight/Common/CRC16MODBUS.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.Common +{ + public class CRC16MODBUS + { + /// Name: CRC-16/MODBUS x16+x15+x2+1 + /// Poly: 0x8005 + /// Init: 0xFFFF + /// Refin: true + /// Refout: true + /// Xorout: 0x0000 + ///******************************添加数据CRC16MODBUS校验位******************************************* + public static byte[] CrcModBus(byte[] buffer, int start = 0, int len = 0) + { + if (buffer == null || buffer.Length == 0) return null; + if (start < 0) return null; + if (len == 0) len = buffer.Length - start; + int length = start + len; + if (length > buffer.Length) return null; + ushort crc = 0xFFFF;// Initial value + for (int i = start; i < length; i++) + { + crc ^= buffer[i]; + for (int j = 0; j < 8; j++) + { + if ((crc & 1) > 0) + crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005 + else + crc = (ushort)(crc >> 1); + } + } + byte[] ret = BitConverter.GetBytes(crc); + //Array.Reverse(ret); + return ret; + } + } +} diff --git a/DM_Weight/Common/CommonClass.cs b/DM_Weight/Common/CommonClass.cs new file mode 100644 index 0000000..daf7730 --- /dev/null +++ b/DM_Weight/Common/CommonClass.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace DM_Weight.Common +{ + public class CommonClass + { + //手动实现调用配置的逻辑 规避修改配置文件后不起作用的问题 + public static string ReadAppSetting(string key) + { + string xPath = "/configuration/appSettings//add[@key='" + key + "']"; + XmlDocument doc = new XmlDocument(); + string exeFileName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + doc.Load(exeFileName + ".dll.config"); + XmlNode node = doc.SelectSingleNode(xPath); + return node.Attributes["value"].Value; + } + public static void SaveAppSetting(string key,string value) + { + Configuration _configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + _configuration.AppSettings.Settings[key].Value = value; + _configuration.Save(); + ConfigurationManager.RefreshSection(key); + } + } +} diff --git a/DM_Weight/Common/PrismManager.cs b/DM_Weight/Common/PrismManager.cs new file mode 100644 index 0000000..459848b --- /dev/null +++ b/DM_Weight/Common/PrismManager.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.Common +{ + public class PrismManager + { + ///// + ///// 主页面区域,主要呈现登录页及登录后页面 + ///// + //public static readonly string MainViewRegionName = "MainContent"; + /// + /// 设置菜单页面跳转,主要呈现设置下子菜单 + /// + public static readonly string SettingViewRegionName = "SettingViewContent"; + ///// + ///// 主页面各菜单页 + ///// + //public static readonly string HomeViewRegionName = "HomeViewContent"; + } +} diff --git a/DM_Weight/Common/TemperatureRangeRule.cs b/DM_Weight/Common/TemperatureRangeRule.cs new file mode 100644 index 0000000..111c8b2 --- /dev/null +++ b/DM_Weight/Common/TemperatureRangeRule.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Data; + +namespace DM_Weight.Common +{ + //设置冰箱温度规则 + public class TemperatureRangeRule : ValidationRule + { + //冰箱温度设置区间为取自配置文件(2~8度) + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + bool flag = false; + string tips = string.Empty; + try + { + string[] rang = value.ToString().Split('-'); + if (rang.Length >= 2) + { + bool bSRange = int.TryParse(rang[0], out int sRange); + bool bERange = int.TryParse(rang[1], out int eRange); + if (bSRange && bERange) + { + if ((sRange < 2 || eRange > 8||sRange>8||eRange<2)) + { + tips = "温度区间设置2-8度,请检查输入"; + return new ValidationResult(flag, tips); + } + else + { + flag = true; + } + } + else + { + tips = "请输入正确的数值"; + return new ValidationResult(flag, tips); + } + + } + else + { + tips = "请输入正确的数值"; + return new ValidationResult(flag, tips); + } + return new ValidationResult(flag, tips); + } + catch (Exception ex) + { + tips = $"校验异常{ex.ToString()}"; + return new ValidationResult(flag, tips); + } + } + } +} diff --git a/DM_Weight/Components/pagination/Pagination.xaml b/DM_Weight/Components/pagination/Pagination.xaml new file mode 100644 index 0000000..8b3eee6 --- /dev/null +++ b/DM_Weight/Components/pagination/Pagination.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DM_Weight/Views/AccountWindow.xaml.cs b/DM_Weight/Views/AccountWindow.xaml.cs new file mode 100644 index 0000000..5ab3534 --- /dev/null +++ b/DM_Weight/Views/AccountWindow.xaml.cs @@ -0,0 +1,29 @@ +using DM_Weight.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace DM_Weight.Views +{ + /// + /// AccountWindow.xaml 的交互逻辑 + /// + public partial class AccountWindow : UserControl + { + public AccountWindow() + { + InitializeComponent(); + } + } +} diff --git a/DM_Weight/Views/AccountWindowForDrug.xaml b/DM_Weight/Views/AccountWindowForDrug.xaml new file mode 100644 index 0000000..f763927 --- /dev/null +++ b/DM_Weight/Views/AccountWindowForDrug.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DM_Weight/Views/AccountWindowForDrug.xaml.cs b/DM_Weight/Views/AccountWindowForDrug.xaml.cs new file mode 100644 index 0000000..1edb94e --- /dev/null +++ b/DM_Weight/Views/AccountWindowForDrug.xaml.cs @@ -0,0 +1,52 @@ +using DM_Weight.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace DM_Weight.Views +{ + /// + /// AccountWindowForDrug.xaml 的交互逻辑 + /// + public partial class AccountWindowForDrug : UserControl + { + + AccountWindowForDrugViewModel vms; + public AccountWindowForDrug() + { + InitializeComponent(); + } + /// + /// 药品名称下拉框 + /// + /// + /// + private void ComboBox_KeyUp(object sender, KeyEventArgs e) + { + ComboBox comboBox = sender as ComboBox; + vms.UpdateComboBoxItems(comboBox.Text); + if (this.vms.DrugInfos.Count > 0) + { + comboBox.IsDropDownOpen = true; + } + TextBox textBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox); + textBox.SelectionStart = textBox.Text.Length; + } + + private void UserControl_Loaded(object sender, RoutedEventArgs e) + { + vms = AccountWindowForDrugViewModel.vm; + } + } +} diff --git a/DM_Weight/Views/Dialog/DatetimeDialog.xaml b/DM_Weight/Views/Dialog/DatetimeDialog.xaml new file mode 100644 index 0000000..85b27ae --- /dev/null +++ b/DM_Weight/Views/Dialog/DatetimeDialog.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + diff --git a/DM_Weight/Views/Dialog/DatetimeDialog.xaml.cs b/DM_Weight/Views/Dialog/DatetimeDialog.xaml.cs new file mode 100644 index 0000000..4635027 --- /dev/null +++ b/DM_Weight/Views/Dialog/DatetimeDialog.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace DM_Weight.Views.Dialog +{ + /// + /// DatetimeDialog.xaml 的交互逻辑 + /// + public partial class DatetimeDialog : UserControl + { + public DatetimeDialog() + { + InitializeComponent(); + } + } +} diff --git a/DM_Weight/Views/Dialog/ShowMessageDialog.xaml b/DM_Weight/Views/Dialog/ShowMessageDialog.xaml new file mode 100644 index 0000000..9554faf --- /dev/null +++ b/DM_Weight/Views/Dialog/ShowMessageDialog.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DM_Weight/Views/LoginWindow.xaml.cs b/DM_Weight/Views/LoginWindow.xaml.cs new file mode 100644 index 0000000..19773d4 --- /dev/null +++ b/DM_Weight/Views/LoginWindow.xaml.cs @@ -0,0 +1,37 @@ +using MaterialDesignColors; +using Prism.Events; +using Prism.Ioc; +using Prism.Regions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using DM_Weight.msg; +using DM_Weight.util; +using DM_Weight.ViewModels; + +namespace DM_Weight.Views +{ + /// + /// LoginWindow.xaml 的交互逻辑 + /// + public partial class LoginWindow : UserControl + { + + public LoginWindow() + { + InitializeComponent(); + + } + + } +} diff --git a/DM_Weight/Views/MainWindow.xaml b/DM_Weight/Views/MainWindow.xaml new file mode 100644 index 0000000..8de71ab --- /dev/null +++ b/DM_Weight/Views/MainWindow.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + diff --git a/DM_Weight/Views/MainWindow.xaml.cs b/DM_Weight/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..73ee566 --- /dev/null +++ b/DM_Weight/Views/MainWindow.xaml.cs @@ -0,0 +1,47 @@ +using Prism.Events; +using Prism.Ioc; +using Prism.Regions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Unity; +using Unity.Lifetime; +using DM_Weight.msg; +using DM_Weight.util; + +namespace DM_Weight.Views +{ + /// + /// MainWindow.xaml 的交互逻辑 + /// + public partial class MainWindow : Window + { + //IRegionManager _regionManager; + //IUnityContainer _container; + public MainWindow() + { + InitializeComponent(); + //_regionManager = regionManager; + //_container = container; + //System.Windows.Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new Action(() => + //{ + + // _container.RegisterType("LoginWindow"); + // _regionManager.RequestNavigate("MainRegion", "LoginWindow"); + + //})); + + } + + } +} diff --git a/DM_Weight/favicon.ico b/DM_Weight/favicon.ico new file mode 100644 index 0000000..8a22d00 Binary files /dev/null and b/DM_Weight/favicon.ico differ diff --git a/DM_Weight/log4net.config b/DM_Weight/log4net.config new file mode 100644 index 0000000..a5c3389 --- /dev/null +++ b/DM_Weight/log4net.config @@ -0,0 +1,53 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DM_Weight/msg/LoginOutEvent.cs b/DM_Weight/msg/LoginOutEvent.cs new file mode 100644 index 0000000..1828caa --- /dev/null +++ b/DM_Weight/msg/LoginOutEvent.cs @@ -0,0 +1,13 @@ +using Prism.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.msg +{ + public class LoginOutEvent : PubSubEvent + { + } +} diff --git a/DM_Weight/msg/SnackbarEvent.cs b/DM_Weight/msg/SnackbarEvent.cs new file mode 100644 index 0000000..eefd861 --- /dev/null +++ b/DM_Weight/msg/SnackbarEvent.cs @@ -0,0 +1,14 @@ +using Prism.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DM_Weight.util; + +namespace DM_Weight.msg +{ + internal class SnackbarEvent: PubSubEvent + { + } +} diff --git a/DM_Weight/select/OrderTakeSelect.cs b/DM_Weight/select/OrderTakeSelect.cs new file mode 100644 index 0000000..03b1a3e --- /dev/null +++ b/DM_Weight/select/OrderTakeSelect.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.select +{ + public class OrderTakeSelect + { + public string? Code { get; set; } + public string? Name { get; set; } + } +} diff --git a/DM_Weight/util/AlertMsg.cs b/DM_Weight/util/AlertMsg.cs new file mode 100644 index 0000000..c59d08c --- /dev/null +++ b/DM_Weight/util/AlertMsg.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + internal class AlertMsg + { + private MsgType _type = MsgType.INFO; + private string _message = ""; + + public MsgType Type { get => _type; set => _type = value; } + public string Message { get => _message; set => _message = value; } + + } + +} diff --git a/DM_Weight/util/BindingProxy.cs b/DM_Weight/util/BindingProxy.cs new file mode 100644 index 0000000..10b858e --- /dev/null +++ b/DM_Weight/util/BindingProxy.cs @@ -0,0 +1,18 @@ +using System.Windows; + +namespace DM_Weight.util +{ + public class BindingProxy : Freezable + { + protected override Freezable CreateInstanceCore() => new BindingProxy(); + + public object? Data + { + get => GetValue(DataProperty); + set => SetValue(DataProperty, value); + } + + public static readonly DependencyProperty DataProperty = + DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); + } +} \ No newline at end of file diff --git a/DM_Weight/util/CheckComputerFreeState.cs b/DM_Weight/util/CheckComputerFreeState.cs new file mode 100644 index 0000000..1c3f789 --- /dev/null +++ b/DM_Weight/util/CheckComputerFreeState.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + public class CheckComputerFreeState + { + /// + /// 创建结构体用于返回捕获时间 + /// + [StructLayout(LayoutKind.Sequential)] + struct LASTINPUTINFO + { + /// + /// 设置结构体块容量 + /// + [MarshalAs(UnmanagedType.U4)] + public int cbSize; + + /// + /// 抓获的时间 + /// + [MarshalAs(UnmanagedType.U4)] + public uint dwTime; + } + + [DllImport("user32.dll")] + private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); + /// + /// 获取键盘和鼠标没有操作的时间 + /// + /// 用户上次使用系统到现在的时间间隔,单位为秒 + public static long GetLastInputTime() + { + LASTINPUTINFO vLastInputInfo = new LASTINPUTINFO(); + vLastInputInfo.cbSize = Marshal.SizeOf(vLastInputInfo); + if (!GetLastInputInfo(ref vLastInputInfo)) + { + return 0; + } + else + { + var count = (Environment.TickCount & Int32.MaxValue) - (long)vLastInputInfo.dwTime; + var icount = count / 1000; + return icount; + } + } + } +} diff --git a/DM_Weight/util/DeviceMsg.cs b/DM_Weight/util/DeviceMsg.cs new file mode 100644 index 0000000..bd74049 --- /dev/null +++ b/DM_Weight/util/DeviceMsg.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + public class DeviceMsg + { + private string _windowName; + private int _drawerNo; + private EventType _eventType; + private int[] _quantitys; + private string code; + private string _message; + + public string WindowName { get { return _windowName; } set { _windowName = value; } } + public int DrawerNo { get { return _drawerNo; } set { _drawerNo = value; } } + public EventType EventType { get { return _eventType; } set { _eventType = value; } } + public int[] Quantitys { get { return _quantitys; } set { _quantitys = value; } } + + public string Code { get { return code; } set { code = value; } } + + public string Message { get { return _message; } set { _message = value; } } + } + + + public enum EventType + { + DRAWEROPEN = 1, DRAWERCLOSE = 2, UPDATEQUANTITY = 3, OPENERROR = 4, CODESCAN = 5 + } +} diff --git a/DM_Weight/util/DialogServiceExtensions.cs b/DM_Weight/util/DialogServiceExtensions.cs new file mode 100644 index 0000000..e320908 --- /dev/null +++ b/DM_Weight/util/DialogServiceExtensions.cs @@ -0,0 +1,55 @@ +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + public static class DialogServiceExtensions + { + /// + /// Shows a modal dialog using a . + /// + /// + /// The name of the dialog to show. + /// The parameters to pass to the dialog. + /// The action to perform when the dialog is closed. + /// Thrown when the dialog service is not a MaterialDialogService + public static void ShowDialogHost(this IDialogService dialogService, string name, IDialogParameters parameters, Action callback) + { + if (!(dialogService is MaterialDialogService materialDialogService)) + throw new NullReferenceException("DialogService must be a MaterialDialogService"); + + materialDialogService.ShowDialogHost(name, parameters, callback); + } + + /// + /// Shows a modal dialog using a . + /// + /// + /// The name of the dialog to show. + /// The parameters to pass to the dialog. + /// The action to perform when the dialog is closed. + /// The name of the that will contain the dialog control + /// Thrown when the dialog service is not a MaterialDialogService + public static void ShowDialogHost(this IDialogService dialogService, string name, + IDialogParameters parameters, Action callback, string windowName) + { + if (!(dialogService is MaterialDialogService materialDialogService)) + throw new NullReferenceException("DialogService must be a MaterialDialogService"); + + materialDialogService.ShowDialogHost(name, windowName, parameters, callback); + } + + + public static void ShowDialogHost(this IDialogService dialogService, string name, IDialogParameters parameters, string windowName) + { + if (!(dialogService is MaterialDialogService materialDialogService)) + throw new NullReferenceException("DialogService must be a MaterialDialogService"); + + materialDialogService.ShowDialogHost(name, parameters, windowName); + } + } +} diff --git a/DM_Weight/util/FingerprintMsg.cs b/DM_Weight/util/FingerprintMsg.cs new file mode 100644 index 0000000..9cf3ba6 --- /dev/null +++ b/DM_Weight/util/FingerprintMsg.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + public class FingerprintMsg + { + public string Message { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public int Id { get; set; } + public int VerifyMethod { get; set; } + public string CardNumber { get; set; } + public int FingerIndex { get; set; } + public bool Result { get; set; } + + public override string? ToString() + { + return $"Message: {Message}, Username: {Username}, Password: {Password}, Id: {Id}, VerifyMethod: {VerifyMethod}, CardNumber: {CardNumber}, FingerIndex: {FingerIndex}, Result: {Result}"; + } + } +} diff --git a/DM_Weight/util/GridViewExtensions.cs b/DM_Weight/util/GridViewExtensions.cs new file mode 100644 index 0000000..214f09e --- /dev/null +++ b/DM_Weight/util/GridViewExtensions.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Threading; +using System.Windows; + +namespace DM_Weight.util +{ + public static class GridViewExtensions + { + #region IsContentCentered + + [Category("Common")] + [AttachedPropertyBrowsableForType(typeof(GridViewColumn))] + public static bool GetIsContentCentered(GridViewColumn gridViewColumn) + { + return (bool)gridViewColumn.GetValue(IsContentCenteredProperty); + } + public static void SetIsContentCentered(GridViewColumn gridViewColumn, bool value) + { + gridViewColumn.SetValue(IsContentCenteredProperty, value); + } + + public static readonly DependencyProperty IsContentCenteredProperty = + DependencyProperty.RegisterAttached( + "IsContentCentered", + typeof(bool), // type + typeof(GridViewExtensions), // containing type + new PropertyMetadata(default(bool), OnIsContentCenteredChanged) + ); + + private static void OnIsContentCenteredChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OnIsContentCenteredChanged((GridViewColumn)d, (bool)e.NewValue); + } + private static void OnIsContentCenteredChanged(GridViewColumn gridViewColumn, bool isContentCentered) + { + if (isContentCentered == false) { return; } + // must wait a bit otherwise GridViewColumn.DisplayMemberBinding will not yet be initialized, + new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Normal, OnColumnLoaded, gridViewColumn.Dispatcher) + { + Tag = gridViewColumn + }.Start(); + } + + static void OnColumnLoaded(object sender, EventArgs e) + { + var timer = (DispatcherTimer)sender; + timer.Stop(); + + var gridViewColumn = (GridViewColumn)timer.Tag; + if (gridViewColumn.DisplayMemberBinding == null) + { + throw new Exception("Only allowed with DisplayMemberBinding."); + } + var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock)); + textBlockFactory.SetBinding(TextBlock.TextProperty, gridViewColumn.DisplayMemberBinding); + textBlockFactory.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Center); + var cellTemplate = new DataTemplate { VisualTree = textBlockFactory }; + gridViewColumn.DisplayMemberBinding = null; // must null, otherwise CellTemplate won't be recognized + gridViewColumn.CellTemplate = cellTemplate; + } + + #endregion IsContentCentered + } +} diff --git a/DM_Weight/util/MD5.cs b/DM_Weight/util/MD5.cs new file mode 100644 index 0000000..2d1cdfc --- /dev/null +++ b/DM_Weight/util/MD5.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; + +namespace DM_Weight.util +{ + public class MD5 + { + public static string GetMD5Hash(string password) + { + //就是比string往后一直加要好的优化容器 + StringBuilder sb = new StringBuilder(); + using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) + { + //将输入字符串转换为字节数组并计算哈希。 + byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(password)); + + //X为 十六进制 X都是大写 x都为小写 + //2为 每次都是两位数 + //假设有两个数10和26,正常情况十六进制显示0xA、0x1A,这样看起来不整齐,为了好看,可以指定"X2",这样显示出来就是:0x0A、0x1A。 + //遍历哈希数据的每个字节 + //并将每个字符串格式化为十六进制字符串。 + int length = data.Length; + for (int i = 0; i < length; i++) + sb.Append(data[i].ToString("x2")); + + } + return sb.ToString(); + } + } +} diff --git a/DM_Weight/util/MaterialDialogService.cs b/DM_Weight/util/MaterialDialogService.cs new file mode 100644 index 0000000..aaa756e --- /dev/null +++ b/DM_Weight/util/MaterialDialogService.cs @@ -0,0 +1,159 @@ +using MaterialDesignThemes.Wpf; +using Prism.Ioc; +using Prism.Mvvm; +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Interop; +using System.Windows.Threading; +using System.Windows; + +namespace DM_Weight.util +{ + internal class MaterialDialogService : DialogService + { + private readonly IContainerExtension _containerExtension; + + public MaterialDialogService(IContainerExtension containerExtension) : base(containerExtension) + { + _containerExtension = containerExtension; + } + + public void ShowDialogHost(string name, IDialogParameters parameters, Action callback) => + ShowDialogHost(name, null, parameters, callback); + + public void ShowDialogHost(string name, string dialogHostName, IDialogParameters parameters, Action callback) + { + if (parameters == null) + parameters = new DialogParameters(); + + var content = _containerExtension.Resolve(name); + if (!(content is FrameworkElement dialogContent)) + { + throw new NullReferenceException("A dialog's content must be a FrameworkElement"); + } + + AutowireViewModel(dialogContent); + + if (!(dialogContent.DataContext is IDialogAware dialogAware)) + { + throw new ArgumentException("A dialog's ViewModel must implement IDialogAware interface"); + } + + var openedEventHandler = new DialogOpenedEventHandler((sender, args) => + { + dialogAware.OnDialogOpened(parameters); + }); + var closedEventHandler = new DialogClosedEventHandler((sender, args) => + { + dialogAware.OnDialogClosed(); + }); + + dialogAware.RequestClose += res => + { + if (DialogHost.IsDialogOpen(dialogHostName)) + { + callback(res); + DialogHost.Close(dialogHostName); + } + }; + + var dispatcherFrame = new DispatcherFrame(); + if (dialogHostName == null) + { + _ = DialogHost.Show(dialogContent, openedEventHandler, null, closedEventHandler) + .ContinueWith(_ => dispatcherFrame.Continue = false); ; + } + else + { + _ = DialogHost.Show(dialogContent, dialogHostName, openedEventHandler, null, closedEventHandler) + .ContinueWith(_ => dispatcherFrame.Continue = false); + } + + try + { + // tell users we're going modal + ComponentDispatcher.PushModal(); + + Dispatcher.PushFrame(dispatcherFrame); + } + finally + { + // tell users we're going non-modal + ComponentDispatcher.PopModal(); + } + + dialogAware.RequestClose -= callback; + } + + private static void AutowireViewModel(object viewOrViewModel) + { + if (viewOrViewModel is FrameworkElement view && view.DataContext is null && ViewModelLocator.GetAutoWireViewModel(view) is null) + { + ViewModelLocator.SetAutoWireViewModel(view, true); + } + } + + public void ShowDialogHost(string name, IDialogParameters parameters, string dialogHostName) + { + var content = _containerExtension.Resolve(name); + if (!(content is FrameworkElement dialogContent)) + { + throw new NullReferenceException("A dialog's content must be a FrameworkElement"); + } + + AutowireViewModel(dialogContent); + + if (!(dialogContent.DataContext is IDialogAware dialogAware)) + { + throw new ArgumentException("A dialog's ViewModel must implement IDialogAware interface"); + } + + var openedEventHandler = new DialogOpenedEventHandler((sender, args) => + { + dialogAware.OnDialogOpened(parameters); + }); + var closedEventHandler = new DialogClosedEventHandler((sender, args) => + { + dialogAware.OnDialogClosed(); + }); + + dialogAware.RequestClose += res => + { + if (DialogHost.IsDialogOpen(dialogHostName)) + { + DialogHost.Close(dialogHostName); + } + }; + + var dispatcherFrame = new DispatcherFrame(); + if (dialogHostName == null) + { + _ = DialogHost.Show(dialogContent, openedEventHandler, null, closedEventHandler) + .ContinueWith(_ => dispatcherFrame.Continue = false); ; + } + else + { + _ = DialogHost.Show(dialogContent, dialogHostName, openedEventHandler, null, closedEventHandler) + .ContinueWith(_ => dispatcherFrame.Continue = false); + } + + try + { + // tell users we're going modal + ComponentDispatcher.PushModal(); + + Dispatcher.PushFrame(dispatcherFrame); + } + finally + { + // tell users we're going non-modal + ComponentDispatcher.PopModal(); + } + + } + } +} diff --git a/DM_Weight/util/MsgType.cs b/DM_Weight/util/MsgType.cs new file mode 100644 index 0000000..318b5b0 --- /dev/null +++ b/DM_Weight/util/MsgType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + enum MsgType + { + ERROR = 1, + INFO = 2, + SUCCESS = 3, + WARING = 4 + } +} diff --git a/DM_Weight/util/ReadApp.cs b/DM_Weight/util/ReadApp.cs new file mode 100644 index 0000000..bebb2df --- /dev/null +++ b/DM_Weight/util/ReadApp.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace DM_Weight.util +{ + internal class ReadApp + { + //手动实现调用配置的逻辑 规避修改配置文件后不起作用的问题 + public static string ReadAppSetting(string key) + { + string xPath = "/configuration/appSettings//add[@key='" + key + "']"; + XmlDocument doc = new XmlDocument(); + string exeFileName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + doc.Load(exeFileName + ".dll.config"); + XmlNode node = doc.SelectSingleNode(xPath); + return node.Attributes["value"].Value.ToString(); + } + } +} diff --git a/DM_Weight/util/SqlSugarHelper.cs b/DM_Weight/util/SqlSugarHelper.cs new file mode 100644 index 0000000..3a0351a --- /dev/null +++ b/DM_Weight/util/SqlSugarHelper.cs @@ -0,0 +1,26 @@ +using System; +using System.Configuration; +using SqlSugar; + +namespace DM_Weight.util +{ + public class SqlSugarHelper + { + public static SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig() + { + + ConnectionString = ConfigurationManager.ConnectionStrings["database"].ToString(), + DbType = DbType.MySql, + IsAutoCloseConnection = true + + }, + db => + { + db.Aop.OnLogExecuting = (sql, pars) => + { + Console.WriteLine(sql); + }; + } + ); + } +} \ No newline at end of file diff --git a/DM_Weight/util/TabTip/AnimationHelper.cs b/DM_Weight/util/TabTip/AnimationHelper.cs new file mode 100644 index 0000000..bd81c30 --- /dev/null +++ b/DM_Weight/util/TabTip/AnimationHelper.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Animation; +using Point = System.Windows.Point; + +namespace DM_Weight.util.TabTip +{ + internal static class AnimationHelper + { + private static readonly Dictionary MoveRootVisualStoryboards = new Dictionary(); + + private static Point GetCurrentUIElementPoint(Visual element) => element.PointToScreen(new Point(0, 0)).ToPointInLogicalUnits(element); + + internal static event Action ExceptionCatched; + private static Rectangle ToRectangleInLogicalUnits(this Rectangle rectangleToConvert, DependencyObject element) + { + const float logicalUnitDpi = 96.0f; + // ReSharper disable once AssignNullToNotNullAttribute + IntPtr windowHandle = new WindowInteropHelper(Window.GetWindow(element)).EnsureHandle(); + + using (Graphics graphics = Graphics.FromHwnd(windowHandle)) + return Rectangle.FromLTRB( + left: (int) (rectangleToConvert.Left * logicalUnitDpi / graphics.DpiX), + top: (int) (rectangleToConvert.Top * logicalUnitDpi / graphics.DpiY), + right: (int) (rectangleToConvert.Right * logicalUnitDpi / graphics.DpiX), + bottom: (int) (rectangleToConvert.Bottom * logicalUnitDpi / graphics.DpiY)); + } + + private static Point ToPointInLogicalUnits(this Point point, DependencyObject element) + { + const float logicalUnitDpi = 96.0f; + + // ReSharper disable once AssignNullToNotNullAttribute + IntPtr windowHandle = new WindowInteropHelper(Window.GetWindow(element)).EnsureHandle(); + + using (Graphics graphics = Graphics.FromHwnd(windowHandle)) + return new Point(x: point.X * logicalUnitDpi / graphics.DpiX, y: point.Y * logicalUnitDpi / graphics.DpiY); + } + + // ReSharper disable once UnusedMember.Local + private static Point GetCurrentUIElementPointRelativeToRoot(UIElement element) + { + return element.TransformToAncestor(GetRootVisualForAnimation(element)).Transform(new Point(0, 0)); + } + + private static Rectangle GetUIElementRect(UIElement element) + { + Rect rect = element.RenderTransform.TransformBounds(new Rect(GetCurrentUIElementPoint(element), element.RenderSize)); + + return Rectangle.FromLTRB( + left: (int)rect.Left, + top: (int)rect.Top, + right: (int)rect.Right, + bottom: (int)rect.Bottom); + } + + private static Rectangle GetCurrentScreenBounds(DependencyObject element) => + new Screen(Window.GetWindow(element)).Bounds.ToRectangleInLogicalUnits(element); + + private static Rectangle GetWorkAreaWithTabTipOpened(DependencyObject element) + { + Rectangle workAreaWithTabTipClosed = GetWorkAreaWithTabTipClosed(element); + + int tabTipRectangleTop = TabTip.GetWouldBeTabTipRectangle().ToRectangleInLogicalUnits(element).Top; + + int bottom = (tabTipRectangleTop == 0) ? workAreaWithTabTipClosed.Bottom / 2 : tabTipRectangleTop; // in case TabTip is not yet opened + + return Rectangle.FromLTRB( + left: workAreaWithTabTipClosed.Left, + top: workAreaWithTabTipClosed.Top, + right: workAreaWithTabTipClosed.Right, + bottom: bottom); + } + + private static Rectangle GetWorkAreaWithTabTipClosed(DependencyObject element) + { + Rectangle currentScreenBounds = GetCurrentScreenBounds(element); + Taskbar taskbar = new Taskbar(); + Rectangle taskbarBounds = taskbar.Bounds.ToRectangleInLogicalUnits(element); + + switch (taskbar.Position) + { + case TaskbarPosition.Bottom: + return Rectangle.FromLTRB( + left: currentScreenBounds.Left, + top: currentScreenBounds.Top, + right: currentScreenBounds.Right, + bottom: taskbarBounds.Top); + case TaskbarPosition.Top: + return Rectangle.FromLTRB( + left: currentScreenBounds.Left, + top: taskbarBounds.Bottom, + right: currentScreenBounds.Right, + bottom: currentScreenBounds.Bottom); + default: + return currentScreenBounds; + } + } + + // ReSharper disable once UnusedMember.Local + private static bool IsUIElementInWorkAreaWithTabTipOpened(UIElement element) + { + return GetWorkAreaWithTabTipOpened(element).Contains(GetUIElementRect(element)); + } + + // ReSharper disable once UnusedMember.Local + private static bool IsUIElementInWorkArea(UIElement element, Rectangle workAreaRectangle) + { + return workAreaRectangle.Contains(GetUIElementRect(element)); + } + + private static FrameworkElement GetRootVisualForAnimation(DependencyObject element) + { + Window rootWindow = Window.GetWindow(element); + + if (rootWindow?.WindowState != WindowState.Maximized) + return rootWindow; + else + return VisualTreeHelper.GetChild(rootWindow, 0) as FrameworkElement; + } + + private static double GetYOffsetToMoveUIElementInToWorkArea(Rectangle uiElementRectangle, Rectangle workAreaRectangle) + { + const double noOffset = 0; + const int paddingTop = 30; + const int paddingBottom = 10; + + if (uiElementRectangle.Top >= workAreaRectangle.Top && + uiElementRectangle.Bottom <= workAreaRectangle.Bottom) // UIElement is in work area + return noOffset; + + if (uiElementRectangle.Top < workAreaRectangle.Top) // Top of UIElement higher than work area + return workAreaRectangle.Top - uiElementRectangle.Top + paddingTop; // positive value to move down + else // Botom of UIElement lower than work area + { + int offset = workAreaRectangle.Bottom - uiElementRectangle.Bottom - paddingBottom; // negative value to move up + if (uiElementRectangle.Top > (workAreaRectangle.Top - offset)) // will Top of UIElement be in work area if offset applied? + return offset; // negative value to move up + else + return workAreaRectangle.Top - uiElementRectangle.Top + paddingTop; // negative value to move up, but only to the point, where top + // of UIElement is just below top bound of work area + } + } + + private static Storyboard GetOrCreateMoveRootVisualStoryboard(FrameworkElement visualRoot) + { + if (MoveRootVisualStoryboards.ContainsKey(visualRoot)) + return MoveRootVisualStoryboards[visualRoot]; + else + return CreateMoveRootVisualStoryboard(visualRoot); + } + + private static Storyboard CreateMoveRootVisualStoryboard(FrameworkElement visualRoot) + { + Storyboard moveRootVisualStoryboard = new Storyboard + { + Duration = new Duration(TimeSpan.FromSeconds(0.35)) + }; + + DoubleAnimation moveAnimation = new DoubleAnimation + { + EasingFunction = new CircleEase {EasingMode = EasingMode.EaseOut}, + Duration = new Duration(TimeSpan.FromSeconds(0.35)), + FillBehavior = (visualRoot is Window) ? FillBehavior.Stop : FillBehavior.HoldEnd + }; + + moveRootVisualStoryboard.Children.Add(moveAnimation); + + if (!(visualRoot is Window)) + visualRoot.RenderTransform = new TranslateTransform(); + + Storyboard.SetTarget(moveAnimation, visualRoot); + Storyboard.SetTargetProperty( + element: moveAnimation, + path: (visualRoot is Window) ? new PropertyPath("Top") : new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); + + MoveRootVisualStoryboards.Add(visualRoot, moveRootVisualStoryboard); + SubscribeToWindowStateChangedToMoveRootVisual(visualRoot); + + return moveRootVisualStoryboard; + } + + private static void SubscribeToWindowStateChangedToMoveRootVisual(FrameworkElement visualRoot) + { + // ReSharper disable once CanBeReplacedWithTryCastAndCheckForNull + if (visualRoot is Window) + { + Window window = (Window)visualRoot; + + window.StateChanged += (sender, args) => + { + if (window.WindowState == WindowState.Normal) + MoveRootVisualBy( + rootVisual: window, + moveBy: GetYOffsetToMoveUIElementInToWorkArea( + uiElementRectangle: GetWindowRectangle(window), + workAreaRectangle: GetWorkAreaWithTabTipClosed(window))); + }; + } + else + { + Window window = Window.GetWindow(visualRoot); + if (window != null) + window.StateChanged += (sender, args) => + { + if (window.WindowState == WindowState.Normal) + MoveRootVisualTo(visualRoot, 0); + }; + } + } + + private static void MoveRootVisualBy(FrameworkElement rootVisual, double moveBy) + { + if (moveBy == 0) + return; + + Storyboard moveRootVisualStoryboard = GetOrCreateMoveRootVisualStoryboard(rootVisual); + + DoubleAnimation doubleAnimation = moveRootVisualStoryboard.Children.First() as DoubleAnimation; + + if (doubleAnimation != null) + { + Window window = rootVisual as Window; + if (window != null) + { + doubleAnimation.From = window.Top; + doubleAnimation.To = window.Top + moveBy; + } + else + { + doubleAnimation.From = doubleAnimation.To ?? 0; + doubleAnimation.To = (doubleAnimation.To ?? 0) + moveBy; + } + } + + moveRootVisualStoryboard.Begin(); + } + + private static void MoveRootVisualTo(FrameworkElement rootVisual, double moveTo) + { + Storyboard moveRootVisualStoryboard = GetOrCreateMoveRootVisualStoryboard(rootVisual); + + DoubleAnimation doubleAnimation = moveRootVisualStoryboard.Children.First() as DoubleAnimation; + + if (doubleAnimation != null) + { + Window window = rootVisual as Window; + if (window != null) + { + doubleAnimation.From = window.Top; + doubleAnimation.To = moveTo; + } + else + { + doubleAnimation.From = doubleAnimation.To ?? 0; + doubleAnimation.To = moveTo; + } + } + + moveRootVisualStoryboard.Begin(); + } + + internal static void GetUIElementInToWorkAreaWithTabTipOpened(UIElement element) + { + try + { + FrameworkElement rootVisualForAnimation = GetRootVisualForAnimation(element); + Rectangle workAreaWithTabTipOpened = GetWorkAreaWithTabTipOpened(element); + + Rectangle uiElementRectangle; + Window window = rootVisualForAnimation as Window; + if (window != null && workAreaWithTabTipOpened.Height >= window.Height) + uiElementRectangle = GetWindowRectangle(window); + else + uiElementRectangle = GetUIElementRect(element); + + MoveRootVisualBy( + rootVisual: rootVisualForAnimation, + moveBy: GetYOffsetToMoveUIElementInToWorkArea( + uiElementRectangle: uiElementRectangle, + workAreaRectangle: workAreaWithTabTipOpened)); + } + catch (Exception ex) + { + ExceptionCatched?.Invoke(ex); + } + } + + private static Rectangle GetWindowRectangle(Window window) + { + return Rectangle.FromLTRB( + left: (int)window.Left, + top: (int)window.Top, + right: (int)(window.Left + window.Width), + bottom: (int)(window.Top + window.Height)); + } + + internal static void GetEverythingInToWorkAreaWithTabTipClosed() + { + try + { + foreach (KeyValuePair moveRootVisualStoryboard in MoveRootVisualStoryboards) + { + Window window = moveRootVisualStoryboard.Key as Window; + // if window exist also check if it has not been closed + if (window != null && new WindowInteropHelper(window).Handle != IntPtr.Zero) + MoveRootVisualBy( + rootVisual: window, + moveBy: GetYOffsetToMoveUIElementInToWorkArea( + uiElementRectangle: GetWindowRectangle(window), + workAreaRectangle: GetWorkAreaWithTabTipClosed(window))); + else + MoveRootVisualTo(rootVisual: moveRootVisualStoryboard.Key, moveTo: 0); + } + } + catch (Exception ex) + { + ExceptionCatched?.Invoke(ex); + } + } + + } +} diff --git a/DM_Weight/util/TabTip/EnvironmentEx.cs b/DM_Weight/util/TabTip/EnvironmentEx.cs new file mode 100644 index 0000000..49839d9 --- /dev/null +++ b/DM_Weight/util/TabTip/EnvironmentEx.cs @@ -0,0 +1,40 @@ +using Microsoft.Win32; + +namespace DM_Weight.util.TabTip +{ + internal enum OSVersion + { + Undefined, + Win7, + Win8Or81, + Win10 + } + internal static class EnvironmentEx + { + private static OSVersion OSVersion = OSVersion.Undefined; + + internal static OSVersion GetOSVersion() + { + if (OSVersion != OSVersion.Undefined) + return OSVersion; + + string OSName = GetOSName(); + + if (OSName.Contains("7")) + OSVersion = OSVersion.Win7; + else if (OSName.Contains("8")) + OSVersion = OSVersion.Win8Or81; + else if (OSName.Contains("10")) + OSVersion = OSVersion.Win10; + + return OSVersion; + } + + private static string GetOSName() + { + RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + if (rk == null) return ""; + return (string) rk.GetValue("ProductName"); + } + } +} diff --git a/DM_Weight/util/TabTip/HardwareKeyboard.cs b/DM_Weight/util/TabTip/HardwareKeyboard.cs new file mode 100644 index 0000000..ce6c567 --- /dev/null +++ b/DM_Weight/util/TabTip/HardwareKeyboard.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Threading.Tasks; + +namespace DM_Weight.util.TabTip +{ + public enum HardwareKeyboardIgnoreOptions + { + /// + /// Do not ignore any keyboard. + /// + DoNotIgnore, + + /// + /// Ignore keyboard, if there is only one, and it's description + /// can be found in ListOfKeyboardsToIgnore. + /// + IgnoreIfSingleInstanceOnList, + + /// + /// Ignore keyboard, if there is only one. + /// + IgnoreIfSingleInstance, + + /// + /// Ignore all keyboards for which the description + /// can be found in ListOfKeyboardsToIgnore + /// + IgnoreIfOnList, + + /// + /// Ignore all keyboards + /// + IgnoreAll + } + + internal static class HardwareKeyboard + { + private static bool? _isConnected; + + /// + /// Checks if Hardware Keyboard is Connected + /// + /// + internal static async Task IsConnectedAsync() + { + Task KeyboardConnectedCheckTask = Task.Run(() => + { + SelectQuery SelectKeyboardsQuery = new SelectQuery("Win32_Keyboard"); + using (ManagementObjectSearcher Searcher = new ManagementObjectSearcher(SelectKeyboardsQuery)) + using (ManagementObjectCollection Keyboards = Searcher.Get()) + { + if (Keyboards.Count == 0) + return false; + + switch (IgnoreOptions) + { + case HardwareKeyboardIgnoreOptions.IgnoreAll: + return false; + + case HardwareKeyboardIgnoreOptions.DoNotIgnore: + return Keyboards.Count > 0; + + case HardwareKeyboardIgnoreOptions.IgnoreIfSingleInstance: + return Keyboards.Count > 1; + + case HardwareKeyboardIgnoreOptions.IgnoreIfSingleInstanceOnList: + return (Keyboards.Count > 1) || + (Keyboards.Count == 1 && + !IsIgnoredKeyboard(Keyboards.Cast().First())); + + case HardwareKeyboardIgnoreOptions.IgnoreIfOnList: + return Keyboards.Cast().Any(k => !IsIgnoredKeyboard(k)); + + default: + return true; + } + } + }); + +#pragma warning disable 4014 + KeyboardConnectedCheckTask.ContinueWith(t => _isConnected = t.Result); +#pragma warning restore 4014 + + if (_isConnected != null) + return _isConnected.Value; + else + return await KeyboardConnectedCheckTask; + } + + private static bool IsIgnoredKeyboard(ManagementBaseObject keyboard) + { + string description = keyboard.Properties.Cast() + .Where(k => k.Name == "Description") + .Select(k => k.Value) + .First() + .ToString(); + + return ListOfKeyboardsToIgnore.Contains(description); + } + + internal static HardwareKeyboardIgnoreOptions IgnoreOptions = HardwareKeyboardIgnoreOptions.DoNotIgnore; + + /// + /// Description of keyboards to ignore if there is only one instance of given keyboard. + /// If you want to ignore some ghost keyboard, add it's description to this list + /// + internal static List ListOfKeyboardsToIgnore { get; } = new List(); + } +} diff --git a/DM_Weight/util/TabTip/PoolingTimer.cs b/DM_Weight/util/TabTip/PoolingTimer.cs new file mode 100644 index 0000000..5e7e137 --- /dev/null +++ b/DM_Weight/util/TabTip/PoolingTimer.cs @@ -0,0 +1,26 @@ +using System; +using System.Reactive.Linq; + +namespace DM_Weight.util.TabTip +{ + internal static class PoolingTimer + { + private static bool Pooling; + + internal static void PoolUntilTrue(Func PoolingFunc, Action Callback, TimeSpan dueTime, TimeSpan period) + { + if (Pooling) return; + + Pooling = true; + + Observable.Timer(dueTime, period) + .Select(_ => PoolingFunc()) + .TakeWhile(stop => stop != true) + .Where(stop => stop == true) + .Finally(() => Pooling = false) + .Subscribe( + onNext: _ => { }, + onCompleted: Callback); + } + } +} diff --git a/DM_Weight/util/TabTip/Screen.cs b/DM_Weight/util/TabTip/Screen.cs new file mode 100644 index 0000000..c92c0d6 --- /dev/null +++ b/DM_Weight/util/TabTip/Screen.cs @@ -0,0 +1,101 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; + +namespace DM_Weight.util.TabTip +{ + internal class Screen + { + + public Rectangle Bounds { get; } + + public Screen(Window window) + { + IntPtr windowHandle = window != null ? new WindowInteropHelper(window).Handle : IntPtr.Zero; + + IntPtr monitor = window != null ? NativeMethods.MonitorFromWindow(windowHandle, NativeMethods.MONITOR_DEFAULTTONEAREST) : NativeMethods.MonitorFromPoint(new NativeMethods.POINT(0, 0), NativeMethods.MonitorOptions.MONITOR_DEFAULTTOPRIMARY); + + NativeMethods.NativeMonitorInfo monitorInfo = new NativeMethods.NativeMonitorInfo(); + NativeMethods.GetMonitorInfo(monitor, monitorInfo); + + Bounds = Rectangle.FromLTRB(monitorInfo.Monitor.Left, monitorInfo.Monitor.Top, monitorInfo.Monitor.Right, monitorInfo.Monitor.Bottom); + + } + + private static class NativeMethods + { + public const Int32 MONITOR_DEFAULTTONEAREST = 0x00000002; + + + [DllImport("user32.dll")] + public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags); + + + [DllImport("user32.dll")] + public static extern bool GetMonitorInfo(IntPtr hMonitor, NativeMonitorInfo lpmi); + + + [Serializable, StructLayout(LayoutKind.Sequential)] + public struct NativeRectangle + { + public readonly int Left; + public readonly int Top; + public readonly int Right; + public readonly int Bottom; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int X; + public int Y; + + public POINT(int x, int y) + { + this.X = x; + this.Y = y; + } + + public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { } + + public static implicit operator System.Drawing.Point(POINT p) + { + return new System.Drawing.Point(p.X, p.Y); + } + + public static implicit operator POINT(System.Drawing.Point p) + { + return new POINT(p.X, p.Y); + } + } + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr MonitorFromPoint(POINT pt, MonitorOptions dwFlags); + + internal enum MonitorOptions : uint + { + MONITOR_DEFAULTTONULL = 0x00000000, + MONITOR_DEFAULTTOPRIMARY = 0x00000001, + MONITOR_DEFAULTTONEAREST = 0x00000002 + } + +#pragma warning disable 169 + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public sealed class NativeMonitorInfo + { + // ReSharper disable once UnusedMember.Local + public Int32 Size = Marshal.SizeOf(typeof(NativeMonitorInfo)); +#pragma warning disable 649 + public NativeRectangle Monitor; +#pragma warning restore 649 + public NativeRectangle Work; + public Int32 Flags; + } +#pragma warning restore 169 + + } + } +} diff --git a/DM_Weight/util/TabTip/TabTip.cs b/DM_Weight/util/TabTip/TabTip.cs new file mode 100644 index 0000000..32ea9b0 --- /dev/null +++ b/DM_Weight/util/TabTip/TabTip.cs @@ -0,0 +1,166 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.Win32; + +namespace DM_Weight.util.TabTip +{ + public static class TabTip + { + private const string TabTipWindowClassName = "IPTip_Main_Window"; + private const string TabTipExecPath = @"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe"; + private const string TabTipRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7"; + + [DllImport("user32.dll")] + private static extern int SendMessage(int hWnd, uint msg, int wParam, int lParam); + + [DllImport("user32.dll")] + private static extern IntPtr FindWindow(String sClassName, String sAppName); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect); + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public readonly int Left; // x position of upper-left corner + public readonly int Top; // y position of upper-left corner + public readonly int Right; // x position of lower-right corner + public readonly int Bottom; // y position of lower-right corner + } + + [DllImport("user32.dll", SetLastError = true)] + private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); + + /// + /// Signals that TabTip was closed after it was opened + /// with a call to StartPoolingForTabTipClosedEvent method + /// + internal static event Action Closed; + + private static IntPtr GetTabTipWindowHandle() => FindWindow(TabTipWindowClassName, null); + + internal static void OpenUndockedAndStartPoolingForClosedEvent() + { + OpenUndocked(); + StartPoolingForTabTipClosedEvent(); + } + + /// + /// Open TabTip + /// + public static void Open() + { + if (EnvironmentEx.GetOSVersion() == OSVersion.Win10) + EnableTabTipOpenInDesctopModeOnWin10(); + + Process.Start(new ProcessStartInfo(TabTipExecPath) { UseShellExecute = true}); + } + + private static void EnableTabTipOpenInDesctopModeOnWin10() + { + const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke"; + + int EnableDesktopModeAutoInvoke = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1); + if (EnableDesktopModeAutoInvoke != 1) + Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1); + } + + /// + /// Open TabTip in undocked state + /// + public static void OpenUndocked() + { + const string TabTipDockedKey = "EdgeTargetDockedState"; + const string TabTipProcessName = "TabTip"; + + int docked = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipDockedKey, 1) ?? 1); + if (docked == 1) + { + Registry.SetValue(TabTipRegistryKeyName, TabTipDockedKey, 0); + foreach (Process tabTipProcess in Process.GetProcessesByName(TabTipProcessName)) + tabTipProcess.Kill(); + } + Open(); + } + + /// + /// Close TabTip + /// + public static void Close() + { + const int WM_SYSCOMMAND = 274; + const int SC_CLOSE = 61536; + SendMessage(GetTabTipWindowHandle().ToInt32(), WM_SYSCOMMAND, SC_CLOSE, 0); + } + + private static void StartPoolingForTabTipClosedEvent() + { + PoolingTimer.PoolUntilTrue( + PoolingFunc: TabTipClosed, + Callback: () => Closed?.Invoke(), + dueTime: TimeSpan.FromMilliseconds(700), + period: TimeSpan.FromMilliseconds(50)); + } + + private static bool TabTipClosed() + { + const int GWL_STYLE = -16; // Specifies we wish to retrieve window styles. + const uint KeyboardClosedStyle = 2617245696; + IntPtr KeyboardWnd = GetTabTipWindowHandle(); + return (KeyboardWnd.ToInt32() == 0 || GetWindowLong(KeyboardWnd, GWL_STYLE) == KeyboardClosedStyle); + } + + // ReSharper disable once UnusedMember.Local + private static bool IsTabTipProcessRunning => GetTabTipWindowHandle() != IntPtr.Zero; + + /// + /// Gets TabTip Window Rectangle + /// + /// + [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] + public static Rectangle GetTabTipRectangle() + { + if (TabTipClosed()) + return new Rectangle(); + + return GetWouldBeTabTipRectangle(); + } + + private static Rectangle previousTabTipRectangle; + + /// + /// Gets Window Rectangle which would be occupied by TabTip if TabTip was opened. + /// + /// + [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] + internal static Rectangle GetWouldBeTabTipRectangle() + { + RECT rect; + + if (!GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect)) + { + if (previousTabTipRectangle.Equals(new Rectangle())) //in case TabTip was closed and previousTabTipRectangle was not set + Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(TryGetTabTipRectangleToСache); + + return previousTabTipRectangle; + } + + Rectangle wouldBeTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1); + previousTabTipRectangle = wouldBeTabTipRectangle; + + return wouldBeTabTipRectangle; + } + + private static void TryGetTabTipRectangleToСache(Task task) + { + RECT rect; + if (GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect)) + previousTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1); + } + } +} diff --git a/DM_Weight/util/TabTip/TabTipAutomation.cs b/DM_Weight/util/TabTip/TabTipAutomation.cs new file mode 100644 index 0000000..fef867e --- /dev/null +++ b/DM_Weight/util/TabTip/TabTipAutomation.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Windows; + +namespace DM_Weight.util.TabTip +{ + public static class TabTipAutomation + { + static TabTipAutomation() + { + if (EnvironmentEx.GetOSVersion() == OSVersion.Win7) + return; + + TabTip.Closed += () => TabTipClosedSubject.OnNext(true); + + AutomateTabTipOpen(FocusSubject.AsObservable()); + AutomateTabTipClose(FocusSubject.AsObservable(), TabTipClosedSubject); + + AnimationHelper.ExceptionCatched += exception => ExceptionCatched?.Invoke(exception); + } + + private static readonly Subject> FocusSubject = new Subject>(); + private static readonly Subject TabTipClosedSubject = new Subject(); + + private static readonly List BindedUIElements = new List(); + + /// + /// By default TabTip automation happens only when no keyboard is connected to device. + /// Change IgnoreHardwareKeyboard if you want to automate + /// TabTip even if keyboard is connected. + /// + public static HardwareKeyboardIgnoreOptions IgnoreHardwareKeyboard + { + get { return HardwareKeyboard.IgnoreOptions; } + set { HardwareKeyboard.IgnoreOptions = value; } + } + + /// + /// Subscribe to this event if you want to know about exceptions (errors) in this library + /// + public static event Action ExceptionCatched; + + /// + /// Description of keyboards to ignore if there is only one instance of given keyboard. + /// If you want to ignore some ghost keyboard, add it's description to this list + /// + public static List ListOfHardwareKeyboardsToIgnoreIfSingleInstance => HardwareKeyboard.ListOfKeyboardsToIgnore; + + /// + /// Description of keyboards to ignore. + /// If you want to ignore some ghost keyboard, add it's description to this list + /// + public static List ListOfKeyboardsToIgnore => HardwareKeyboard.ListOfKeyboardsToIgnore; + + private static void AutomateTabTipClose(IObservable> focusObservable, Subject tabTipClosedSubject) + { + focusObservable + .ObserveOn(Scheduler.Default) + .Where(_ => IgnoreHardwareKeyboard == HardwareKeyboardIgnoreOptions.IgnoreAll || !HardwareKeyboard.IsConnectedAsync().Result) + .Throttle(TimeSpan.FromMilliseconds(100)) // Close only if no other UIElement got focus in 100 ms + .Where(tuple => tuple.Item2 == false) + .Do(_ => TabTip.Close()) + .Subscribe(_ => tabTipClosedSubject.OnNext(true)); + + tabTipClosedSubject + .Subscribe(_ => AnimationHelper.GetEverythingInToWorkAreaWithTabTipClosed()); + } + + private static void AutomateTabTipOpen(IObservable> focusObservable) + { + focusObservable + .ObserveOn(Scheduler.Default) + .Where(_ => IgnoreHardwareKeyboard == HardwareKeyboardIgnoreOptions.IgnoreAll || !HardwareKeyboard.IsConnectedAsync().Result) + .Where(tuple => tuple.Item2 == true) + .Do(_ => TabTip.OpenUndockedAndStartPoolingForClosedEvent()) + .Subscribe(tuple => AnimationHelper.GetUIElementInToWorkAreaWithTabTipOpened(tuple.Item1)); + } + + /// + /// Automate TabTip for given UIElement. + /// Keyboard opens on GotFocusEvent or TouchDownEvent (if focused already) + /// and closes on LostFocusEvent. + /// + /// + public static void BindTo() where T : UIElement + { + if (EnvironmentEx.GetOSVersion() == OSVersion.Win7) + return; + + if (BindedUIElements.Contains(typeof(T))) + return; + + EventManager.RegisterClassHandler( + classType: typeof(T), + routedEvent: UIElement.TouchDownEvent, + handler: new RoutedEventHandler((s, e) => + { + if (((UIElement)s).IsFocused) + FocusSubject.OnNext(new Tuple((UIElement)s, true)); + }), + handledEventsToo: true); + + EventManager.RegisterClassHandler( + classType: typeof(T), + routedEvent: UIElement.GotFocusEvent, + handler: new RoutedEventHandler((s, e) => FocusSubject.OnNext(new Tuple((UIElement) s, true))), + handledEventsToo: true); + + EventManager.RegisterClassHandler( + classType: typeof(T), + routedEvent: UIElement.LostFocusEvent, + handler: new RoutedEventHandler((s, e) => FocusSubject.OnNext(new Tuple((UIElement) s, false))), + handledEventsToo: true); + + BindedUIElements.Add(typeof(T)); + } + } +} diff --git a/DM_Weight/util/TabTip/Taskbar.cs b/DM_Weight/util/TabTip/Taskbar.cs new file mode 100644 index 0000000..f5950c7 --- /dev/null +++ b/DM_Weight/util/TabTip/Taskbar.cs @@ -0,0 +1,132 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace DM_Weight.util.TabTip +{ + internal enum TaskbarPosition + { + Unknown = -1, + Left, + Top, + Right, + Bottom, + } + + internal sealed class Taskbar + { + private const string ClassName = "Shell_TrayWnd"; + + public Rectangle Bounds { get; } + + public TaskbarPosition Position + { + get; + private set; + } + + public Point Location => Bounds.Location; + + public Size Size => Bounds.Size; + + //Always returns false under Windows 7 + public bool AlwaysOnTop + { + get; + private set; + } + public bool AutoHide + { + get; + private set; + } + + public Taskbar() + { + IntPtr taskbarHandle = User32.FindWindow(ClassName, null); + + APPBARDATA data = new APPBARDATA + { + cbSize = (uint) Marshal.SizeOf(typeof (APPBARDATA)), + hWnd = taskbarHandle + }; + IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data); + if (result != IntPtr.Zero) + { + Position = (TaskbarPosition) data.uEdge; + Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom); + + data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA)); + result = Shell32.SHAppBarMessage(ABM.GetState, ref data); + int state = result.ToInt32(); + AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop; + AutoHide = (state & ABS.Autohide) == ABS.Autohide; + } + else + { + Position = TaskbarPosition.Unknown; + } + } + + private enum ABM : uint + { + New = 0x00000000, + Remove = 0x00000001, + QueryPos = 0x00000002, + SetPos = 0x00000003, + GetState = 0x00000004, + GetTaskbarPos = 0x00000005, + Activate = 0x00000006, + GetAutoHideBar = 0x00000007, + SetAutoHideBar = 0x00000008, + WindowPosChanged = 0x00000009, + SetState = 0x0000000A, + } + + private enum ABE : uint + { + Left = 0, + Top = 1, + Right = 2, + Bottom = 3 + } + + private static class ABS + { + public const int Autohide = 0x0000001; + public const int AlwaysOnTop = 0x0000002; + } + + private static class Shell32 + { + [DllImport("shell32.dll", SetLastError = true)] + public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData); + } + + private static class User32 + { + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + } + + [StructLayout(LayoutKind.Sequential)] + private struct APPBARDATA + { + public uint cbSize; + public IntPtr hWnd; + public uint uCallbackMessage; + public ABE uEdge; + public RECT rc; + public int lParam; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + } + } +} diff --git a/DM_Weight/util/TransExpV2.cs b/DM_Weight/util/TransExpV2.cs new file mode 100644 index 0000000..e7a2a7a --- /dev/null +++ b/DM_Weight/util/TransExpV2.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace DM_Weight.util +{ + public static class TransExpV2 + { + + private static readonly Func cache = GetFunc(); + private static Func GetFunc() + { + ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); + List memberBindingList = new List(); + + foreach (var item in typeof(TOut).GetProperties()) + { + if (!item.CanWrite) + continue; + + MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); + MemberBinding memberBinding = Expression.Bind(item, property); + memberBindingList.Add(memberBinding); + } + + MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); + Expression> lambda = Expression.Lambda>(memberInitExpression, new ParameterExpression[] { parameterExpression }); + + return lambda.Compile(); + } + + public static TOut Trans(TIn tIn) + { + return cache(tIn); + } + + } +} diff --git a/DM_WeightTests/DM_WeightTests.csproj b/DM_WeightTests/DM_WeightTests.csproj new file mode 100644 index 0000000..ab187c8 --- /dev/null +++ b/DM_WeightTests/DM_WeightTests.csproj @@ -0,0 +1,23 @@ + + + + net6.0-windows + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/DM_WeightTests/Port/ScreenUtilTests.cs b/DM_WeightTests/Port/ScreenUtilTests.cs new file mode 100644 index 0000000..43c28d2 --- /dev/null +++ b/DM_WeightTests/Port/ScreenUtilTests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DM_WeightTests.Port +{ + internal class ScreenUtilTests + { + } +} diff --git a/说明.txt b/说明.txt new file mode 100644 index 0000000..cdbd48f --- /dev/null +++ b/说明.txt @@ -0,0 +1 @@ +2)麻醉科(手术室):共8个抽屉,其中4个抽屉为九分区管控式抽屉(无按钮、取药进行亮灯提示),剩下4个抽屉每个抽屉面板上方有3个电子标签(有按钮、取药进行亮灯提示),显示对应储药区的药品名称、数量、批次等信息。计数回收毒麻药品,固定在华康毒麻柜侧面,抽屉式。一套有3个回收口,最多回收3种空瓶。