1、获取当前程序的目录,可以直接知道exe程序所在位置。
/// <summary> /// 获取当前程序所在位置 /// </summary> private string appPath = AppDomain.CurrentDomain.BaseDirectory;//获得当前程序运行目录 string AppPath = Environment.CurrentDirectory;2、拖动窗体,在Window将WindowStyle="None" 窗体会被设置为无边框,ResizeMode="NoResize"来禁止用户调整窗口大小。在窗体添加事件MouseDown="Win_MouseDown",事件代码如下。注意DragMove(),是Window.DragMove()函数。
private void Win_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) { DragMove(); } }3、App.Current.Shutdown()方法用于关闭当前应用程序。
注意事项
- 调用
App.Current.Shutdown()会立即关闭整个应用程序,因此在调用之前需要确保所有必要的清理工作已经完成。 - 如果应用程序中有多个窗口,可能需要考虑关闭所有窗口后再调用
Shutdown方法。 - 如果有未保存的数据或其他重要状态,建议在关闭前提示用户进行确认。
4、WPF C#代码获取资源文件内容的方法。
App.Current.FindResource("PleaseEnterYourAccount").ToString() FindResource("Prompt").ToString()5、移除资源字典和合并新的资源字典
// 移除当前的资源字典 Application.Current.Resources.MergedDictionaries.Clear(); // 根据主题名称加载新的资源字典 ResourceDictionary newTheme = new ResourceDictionary(); newTheme.Source = new Uri($"Themes/{themeName}.xaml", UriKind.Relative); // 合并新的资源字典 Application.Current.Resources.MergedDictionaries.Add(newTheme);6、选择Txt文件。
OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "Text files (*.txt)|*.txt|所有文件 (*.*)|*.*"; openFileDialog.Title = "标题内容"; openFileDialog.Multiselect = false; if (openFileDialog.ShowDialog() == true)7、选择文件保存路径。
// 创建一个SaveFileDialog对象 SaveFileDialog saveFileDialog = new SaveFileDialog(); // 可选:设置对话框的一些属性,如文件过滤器、默认文件名、标题等 saveFileDialog.Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"; saveFileDialog.DefaultExt = "txt"; // 默认文件扩展名 saveFileDialog.FileName = "默认文件名"; // 默认文件名(不带扩展名) saveFileDialog.Title = "选择文件保存路径"; // 显示对话框,并检查用户是否指定了保存路径 if (saveFileDialog.ShowDialog() == true)8、创建向Txt文档写入内容。
// 使用StreamWriter创建或覆盖一个TXT文件,并写入内容 using (StreamWriter writer = new StreamWriter(filePath)) { writer.WriteLine(content); }9、定时器DispatcherTimer的使用。
DispatcherTimer dispatcherTimer = new DispatcherTimer(); dispatcherTimer.Tick += DispatcherTimer_Tick; dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1000); dispatcherTimer.IsEnabled = true; dispatcherTimer.Start(); private void DispatcherTimer_Tick(object sender, EventArgs e) { dispatcherTimer.IsEnabled = false; dispatcherTimer.Stop(); }10、设置窗体透明。
WindowStyle="None" WindowStartupLocation="CenterScreen" Opacity="0.8" AllowsTransparency="True" Background="Transparent" ResizeMode="NoResize"11、WPF项目中引用了第三方的库文件,库文件又不支持嵌入到程序中时,如何生成单exe的应用程序。
11.1、通过Nuget添加Costura.Fody库,我使用的是.Net Framework 4.5 如下图。
11.2、选择项目->清理,这时bin文件夹中的内容会被清理掉。
11.3、重新生成项目后就生成了单独的exe程序。这时可能还有些如.xml或.pdb之类的文件,基本上删除掉就可以了。
12、通常在项目中添加一个文件夹例如Views,将MainWindow.xaml移动到Views文件夹下以后,可能会报错
System.IO.IOException 找不到资源“mainwindow.xaml”。
可以在App.xaml修改StartupUri解决。App.xaml如下。
<Application x:Class="FreeSqlDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FreeSqlDemo" StartupUri="Views/MainWindow.xaml"> <Application.Resources> </Application.Resources> </Application>13、WPF在ViewModel中调用 Dispatcher。
Application.Current.Dispatcher.Invoke(new Action(() => { XXX = "XXX"; }));14、使用Mutex,防止WPF应用程序多开。在App.xaml.cs中修改代码如下。
public partial class App : Application { private bool isAppRunning; private static Mutex mutex; protected override void OnStartup(StartupEventArgs e) { mutex = new Mutex(true, Process.GetCurrentProcess().ProcessName, out isAppRunning); if (!isAppRunning) { Application.Current.Shutdown(); return; } base.OnStartup(e); } protected override void OnExit(ExitEventArgs e) { //mutex.ReleaseMutex(); mutex = null; base.OnExit(e); } }15、启动另一个应用程序。
使用 Process.Start
这是最简单的方法,直接使用Process.Start启动另一个应用程序。
using System.Diagnostics; namespace YourNamespace { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void LaunchAnotherApp_Click(object sender, RoutedEventArgs e) { Process.Start("notepad.exe"); } } }使用 ProcessStartInfo
如果你需要更多的控制选项,比如设置工作目录、传递命令行参数等,可以使用ProcessStartInfo。
using System.Diagnostics; namespace YourNamespace { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void LaunchAnotherApp_Click(object sender, RoutedEventArgs e) { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "notepad.exe", Arguments = "", WorkingDirectory = @"C:\Windows\System32", WindowStyle = ProcessWindowStyle.Hidden }; try { Process.Start(startInfo); } catch (Exception ex) { MessageBox.Show($"Error starting application: {ex.Message}"); } } } }等待进程完成
如果你需要等待启动的应用程序完成后再继续执行,可以使用Process.WaitForExit。
using System.Diagnostics; namespace YourNamespace { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void LaunchAnotherApp_Click(object sender, RoutedEventArgs e) { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "notepad.exe" }; using (Process process = Process.Start(startInfo)) { process.WaitForExit(); MessageBox.Show("Notepad has exited."); } } } }捕获进程输出
如果你需要捕获启动的应用程序的输出,可以设置ProcessStartInfo的RedirectStandardOutput和RedirectStandardError属性。
using System.Diagnostics; using System.IO; namespace YourNamespace { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void LaunchAnotherApp_Click(object sender, RoutedEventArgs e) { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "ping.exe", Arguments = "google.com", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (Process process = Process.Start(startInfo)) { using (StreamReader reader = process.StandardOutput) { string result = reader.ReadToEnd(); MessageBox.Show(result); } } } } }16、自定义App.cs启动类,将App.xaml从项目中排除。添加一个类命名为App.cs,代码如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace WpfApp { class App { [STAThread] static void Main() { //应用程序入口 Application app = new Application(); //调用主界面 MainWindow mainWindow = new MainWindow(); app.Run(mainWindow); } } }17、TextBox 设置只能输入数字。代码如下。
1.使用 PreviewTextInput 事件(推荐) <!-- XAML 中设置 TextBox --> <TextBox PreviewTextInput="NumberValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> // C# 代码 private void NumberValidationTextBox(object sender, TextCompositionEventArgs e) { Regex regex = new Regex("[^0-9.]+"); // 只允许数字和小数点 e.Handled = regex.IsMatch(e.Text); // 额外验证:防止多个小数点 TextBox textBox = sender as TextBox; if (e.Text == "." && textBox.Text.Contains(".")) { e.Handled = true; } } 2.使用 DataObject.Pasting 事件(处理粘贴操作) <TextBox PreviewTextInput="NumberValidationTextBox" DataObject.Pasting="TextBox_Pasting" InputMethod.IsInputMethodEnabled="False"/> private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(typeof(string))) { string pasteText = (string)e.DataObject.GetData(typeof(string)); Regex regex = new Regex("[^0-9.]+"); if (regex.IsMatch(pasteText)) { e.CancelCommand(); // 阻止粘贴非数字内容 } } else { e.CancelCommand(); } }18、通过Windows消息获取串口是否被拔出。
#region Windows消息 public const int WM_DEVICECHANGE = 0x219; //Windows消息编号 public const int DBT_DEVICEARRIVAL = 0x8000; public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; #endregion HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;//窗口过程 if (hwndSource != null) hwndSource.AddHook(new HwndSourceHook(DeveiceChanged)); //挂钩 /// <summary> /// 设备插拔改变函数 /// </summary> /// <param name="hwnd"></param> /// <param name="msg"></param> /// <param name="wParam"></param> /// <param name="lParam"></param> /// <param name="handled"></param> /// <returns></returns> private IntPtr DeveiceChanged(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { string[] PortNames; if (msg == WM_DEVICECHANGE) { switch (wParam.ToInt32()) { case DBT_DEVICEARRIVAL://设备插入 // 模型扫描 GetSocketCom(); break; case DBT_DEVICEREMOVECOMPLETE: //设备卸载 // 模型扫描 GetSocketCom(); break; default: // 模型扫描 GetSocketCom(); break; } } return IntPtr.Zero; }19、类的序列化。
/// <summary> /// 获得当前程序运行目录 /// </summary> public string AppPath = Environment.CurrentDirectory; /// <summary> /// 序列化 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static byte[] SerializeObject(object obj) { if (obj == null) return null; //内存实例 MemoryStream ms = new MemoryStream(); //创建序列化的实例 BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, obj);//序列化对象,写入ms流中 byte[] bytes = ms.GetBuffer(); return bytes; } /// <summary> /// 反序列化 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static object DeserializeObject(byte[] bytes) { object obj = null; if (bytes == null) return obj; //利用传来的byte[]创建一个内存流 MemoryStream ms = new MemoryStream(bytes); ms.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); obj = formatter.Deserialize(ms);//把内存流反序列成对象 ms.Close(); return obj; } #region IO操作 /// <summary> /// 保存数据 /// </summary> /// <param name="FilePath"></param> /// <param name="data"></param> public void SaveReciveData(string FilePath, byte[] data) { if (FilePath == null) { return; } FileInfo f = new FileInfo(FilePath); if (!File.Exists(f.Directory.ToString())) { Directory.CreateDirectory(f.Directory.ToString()); } using (FileStream fs = new FileStream(FilePath, FileMode.Create, FileAccess.Write)) { fs.Write(data, 0, data.Length); } } /// <summary> /// 读取数据 /// </summary> /// <param name="FilePath"></param> /// <returns></returns> public byte[] GetReciveData(string FilePath) { byte[] result = new byte[0]; if (File.Exists(FilePath)) { using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { result = new byte[fs.Length]; fs.Read(result, 0, (int)fs.Length); } } return result; } #endregion调用方法:
// 获得当前程序运行目录 string AppPath = Environment.CurrentDirectory + "/XXX.xx";.xx为后缀的文件 // 将类序列化到XXX.xx文件中 SaveReciveData(AppPath, SerializeObject(obj)); // 读取XXX.xx文件并反序列化为对象 DeserializeObject(GetReciveData(AppPath))20、TextBox 设置禁止输入非法字符。代码如下。
// 定义禁止输入的正则表达式,可根据业务需要调整规则 private static readonly Regex invalidPattern = new Regex(@"['"";\-/*%_=()#&|]", RegexOptions.Compiled); // 实时拦截输入字符 private void tb_PreviewTextInput(object sender, TextCompositionEventArgs e) { try { if (invalidPattern.IsMatch(e.Text)) { e.Handled = true; // 取消输入 } } catch(Exception ex) { Debug.WriteLine(ex.Message); } } // 拦截空格和特殊按键(如退格之外的控制键可按需添加) private void tb_PreviewKeyDown(object sender, KeyEventArgs e) { // 禁止输入空格(如果需要允许空格可移除此判断) if (e.Key == Key.Space) { e.Handled = true; } } private string previousText = string.Empty; private void tb_TextChanged(object sender, TextChangedEventArgs e) { try { TextBox tbx = (TextBox)sender; if(tbx == null) return; if (invalidPattern.IsMatch(tbx.Text)) { tbx.Text = previousText; tbx.SelectionStart = previousText.Length; } else { // 保存当前有效文本 previousText = tbx.Text; } } catch (Exception ex) { Debug.WriteLine(ex.Message); } }21、CustomMessageBox 自定义消息框。代码如下。
<Window x:Class="UC.CustomMessageBox" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:UC" Title="CustomMessageBox" Height="200" Width="400" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Topmost="True" WindowStyle="ToolWindow"> <Grid Margin="20"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock x:Name="MessageTextBlock" Grid.Row="0" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center"/> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0,0"> <Button x:Name="Button1" Content="确定" Width="75" Height="25" Margin="5" Click="Button1_Click"/> <Button x:Name="Button2" Content="取消" Width="75" Height="25" Margin="5" Click="Button2_Click"/> <Button x:Name="Button3" Content="是" Width="75" Height="25" Margin="5" Click="Button3_Click"/> </StackPanel> </Grid> </Window>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; namespace UC { /// <summary> /// CustomMessageBox.xaml 的交互逻辑 /// </summary> public partial class CustomMessageBox : Window { public enum CustomMessageBoxResult { None = 0, OK = 1, Cancel = 2, Yes = 6, No = 7 } public enum CustomMessageBoxButton { OK = 0, OKCancel = 1, YesNo = 2, YesNoCancel = 3 } public enum CustomMessageBoxImage { None = 0, Information = 1, Warning = 2, Error = 3 } private CustomMessageBoxResult result = CustomMessageBoxResult.None; public CustomMessageBoxResult Result { get { return result; } } public CustomMessageBox(string message, string title, CustomMessageBoxButton buttons, CustomMessageBoxImage icon) { InitializeComponent(); this.Title = title; this.MessageTextBlock.Text = message; // 根据按钮类型设置按钮可见性 switch (buttons) { case CustomMessageBoxButton.OK: Button1.Content = "OK"; Button1.Visibility = Visibility.Visible; Button2.Visibility = Visibility.Collapsed; Button3.Visibility = Visibility.Collapsed; break; case CustomMessageBoxButton.OKCancel: Button1.Content = "OK"; Button2.Content = "Cancel"; Button1.Visibility = Visibility.Visible; Button2.Visibility = Visibility.Visible; Button3.Visibility = Visibility.Collapsed; break; case CustomMessageBoxButton.YesNo: Button1.Content = "Yes"; Button2.Content = "No"; Button1.Visibility = Visibility.Visible; Button2.Visibility = Visibility.Visible; Button3.Visibility = Visibility.Collapsed; break; case CustomMessageBoxButton.YesNoCancel: Button1.Content = "Yes"; Button2.Content = "No"; Button3.Content = "Cancel"; Button1.Visibility = Visibility.Visible; Button2.Visibility = Visibility.Visible; Button3.Visibility = Visibility.Visible; break; } } private void Button1_Click(object sender, RoutedEventArgs e) { switch (Button1.Content.ToString()) { case "OK": result = CustomMessageBoxResult.OK; break; case "Yes": result = CustomMessageBoxResult.Yes; break; } this.DialogResult = true; this.Close(); } private void Button2_Click(object sender, RoutedEventArgs e) { switch (Button2.Content.ToString()) { case "Cancel": result = CustomMessageBoxResult.Cancel; break; case "No": result = CustomMessageBoxResult.No; break; } this.DialogResult = true; this.Close(); } private void Button3_Click(object sender, RoutedEventArgs e) { result = CustomMessageBoxResult.Cancel; this.DialogResult = true; this.Close(); } public static CustomMessageBoxResult Show(string messageBoxText, string caption, CustomMessageBoxButton messageBoxButton, CustomMessageBoxImage messageBoxImage) { var window = new CustomMessageBox(messageBoxText, caption, messageBoxButton, messageBoxImage); window.ShowDialog(); return window.Result; } } }22、全局异常捕获
using System; using System.Windows; using System.Windows.Threading; using System.Threading.Tasks; namespace WpfGlobalExceptionDemo { /// <summary> /// App.xaml 的交互逻辑 /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { // 1. 注册 UI 线程未捕获异常处理 this.DispatcherUnhandledException += App_DispatcherUnhandledException; // 2. 注册非 UI 线程(如 Thread.Start 创建的线程)未捕获异常处理 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; // 3. 注册 Task 异步任务中未观察到的异常处理 TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; base.OnStartup(e); } /// <summary> /// 处理 UI 线程上的未捕获异常 /// </summary> private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { // 记录异常日志 LogException(e.Exception, "UI Thread"); // 显示友好提示 MessageBox.Show($"界面操作发生错误: {e.Exception.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); // 标记为已处理,防止程序默认崩溃退出 e.Handled = true; } /// <summary> /// 处理非 UI 线程上的未捕获异常 /// 注意:此事件触发时,程序通常即将终止,无法阻止关闭,只能做最后的日志记录 /// </summary> private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception ex = e.ExceptionObject as Exception; LogException(ex, "Non-UI Thread"); // 如果是致命异常,可以尝试记录最后的状态 if (e.IsTerminating) { MessageBox.Show($"程序遇到严重错误即将关闭: {ex?.Message}", "致命错误", MessageBoxButton.OK, MessageBoxImage.Stop); } } /// <summary> /// 处理 Task 中未观察到的异常 /// </summary> private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { // 遍历所有内部异常 foreach (var innerEx in e.Exception.InnerExceptions) { LogException(innerEx, "Task Thread"); } // 标记为已观察,防止 CLR 终止进程(在 .NET 4.5+ 中默认行为可能不同,但显式设置更安全) e.SetObserved(); } /// <summary> /// 简单的日志记录方法(实际项目中应替换为 NLog/Serilog 等) /// </summary> private void LogException(Exception ex, string threadType) { if (ex == null) return; string logMessage = $"[{DateTime.Now}] [{threadType}] Exception: {ex.Message}\nStackTrace: {ex.StackTrace}"; // 这里可以写入文件、数据库或发送到远程服务器 System.Diagnostics.Debug.WriteLine(logMessage); // 示例:写入本地文本文件 try { System.IO.File.AppendAllText("error.log", logMessage + Environment.NewLine + Environment.NewLine); } catch { /* 忽略日志写入失败 */ } } } }代码说明:
- DispatcherUnhandledException:专门捕获运行在 WPF UI 线程(Dispatcher)上且未被 try-catch 处理的异常。通过设置
e.Handled = true,可以阻止应用程序崩溃,保持程序继续运行。 - AppDomain.CurrentDomain.UnhandledException:捕获所有 AppDomain 中未处理的异常,主要针对手动创建的后台线程(
new Thread())。需要注意的是,当此事件触发时,应用程序通常处于不可恢复状态,即将终止,因此主要用于记录最后的错误日志。 - TaskScheduler.UnobservedTaskException:专门捕获
Task或async/await异步操作中产生但未被“观察”(即没有调用.Wait(),.Result或在 async 方法中 await)的异常。调用e.SetObserved()可防止 .NET 运行时因未观察到的异常而终止进程。 - 注册时机:建议在
App类的OnStartup方法或构造函数中尽早注册这些事件,以确保应用程序启动阶段发生的异常也能被捕获。
23、程序退出
- Close():关闭当前窗口。在WPF应用程序关闭时ShutdownMode(在App.xaml的Application节点中设置)属性设置,具有3种枚举类型的值:
- OnLastWindowClose (默认值)--- 应用程序最后一个窗体关闭时关闭应用程序。
- OnMainWindowClose --- 应用程序主窗体关闭时关闭应用程序。
- OnExplicitShutdown --- 显示调用关闭。
- Application.Current.Shutdown() 只关掉了UI而后台进程没有关掉,直到所有的后台线程结束才算真的结束。
- Environment.Exit(0) 强制退出,即使有其他的线程没有结束。不过它不会执行代码块的finally块(如果有的话),但资源清理还是要进行的。它是最常见的退出当前进程的方法之一。
- Process 对象实例.Kill()
var currentProcess = Process.GetCurrentProcess(); currentProcess.Kill();Kill 方法会直接结束整个进程,不进行常规资源清理。
5.Environment 类的 FailFast :不向操作系统返回进程代码(ExitCode),直接结束当前进程并在应用程序事件薄中写入信息,用于程序出现致命错误需要立即停止。一般不建议使用。