文章目录
- 前言
- 1、页面展示
- 2、主页面UI代码
- 2、TCP client的UI代码
- 3、Tcp client后台代码实现
- 4、UI与后台代码的关联
前言
在该篇的Demo中,您可以找到以下内容:
1、TouchSocket的使用;
2、CommunityToolkit.Mvvm的使用;
3、AvalonDock可拖拽停靠控件的使用;
4、Visual Studio的Dark主题页面的实现;
5、MVVM框架开发使用;
6、RichTextBox控件的使用;
Demo下载地址: WPF Demo项目代码
1、页面展示
2、主页面UI代码
<Window x:Class="WPF_Demo_V2.MainWindow"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:WPF_Demo_V2"mc:Ignorable="d"Background="#1F1F1F"MouseLeftButtonDown="Window_MouseLeftButtonDown"WindowStartupLocation="CenterScreen"WindowState="Normal"xmlns:avalon="https://github.com/Dirkster99/AvalonDock"Title="WPF Demo V0.0.1" Height="768" Width="1024" ><Window.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="./Resource/Dictionary/DarkStyle.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Window.Resources><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><!--<TextBlock Text="" FontFamily="./Resource/Font/#iconfont" TextAlignment="Center" Foreground="{DynamicResource Foreground}" FontSize="30" Background="{DynamicResource WindowBrush}"/>--><Menu Grid.Column="1" Background="{DynamicResource WindowBrush}" Foreground="{DynamicResource Foreground}" HorizontalAlignment="Left" VerticalAlignment="Top"><MenuItem Header="File" ><MenuItem Header="New"/><MenuItem Header="Open" /></MenuItem><MenuItem Header="Edit" ><MenuItem Header="Undo"/><MenuItem Header="Cut"/><MenuItem Header="Copy"/></MenuItem><MenuItem Header="View" ><MenuItem Header="Explorer"/><MenuItem Header="Open"/></MenuItem><MenuItem Header="Tool" ><MenuItem Header="Extentions"/><MenuItem Header="Options"/></MenuItem><MenuItem Header="Help" ><MenuItem Header="Welcome"/><MenuItem Header="Support"/><MenuItem Header="About"/></MenuItem></Menu></Grid><avalon:DockingManager Grid.Row="1"><avalon:DockingManager.Theme><avalon:Vs2013DarkTheme/></avalon:DockingManager.Theme><avalon:LayoutRoot><avalon:LayoutPanel Orientation="Horizontal"><avalon:LayoutAnchorablePaneGroup DockWidth="150" FloatingWidth="240"><avalon:LayoutAnchorablePane x:Name="panelLeft"><avalon:LayoutAnchorable Title="Tool"></avalon:LayoutAnchorable><avalon:LayoutAnchorable Title="Team"></avalon:LayoutAnchorable></avalon:LayoutAnchorablePane></avalon:LayoutAnchorablePaneGroup><avalon:LayoutPanel Orientation="Vertical"><avalon:LayoutDocumentPaneGroup><avalon:LayoutDocumentPane x:Name="panelTop"><avalon:LayoutDocument Title="TCP Server"><Frame Source="./View/TcpServerPage.xaml"/></avalon:LayoutDocument><avalon:LayoutDocument Title="TCP Client"><Frame Source="./View/TcpClientPage.xaml"/></avalon:LayoutDocument><avalon:LayoutDocument Title="MainWindow.xaml"></avalon:LayoutDocument><avalon:LayoutDocument Title="MainWindow.xaml.cs"></avalon:LayoutDocument></avalon:LayoutDocumentPane></avalon:LayoutDocumentPaneGroup><avalon:LayoutAnchorablePaneGroup DockMinHeight="30" DockHeight="100" FloatingHeight="180"><avalon:LayoutAnchorablePane x:Name="panelBottom"><avalon:LayoutAnchorable Title="Output"></avalon:LayoutAnchorable><avalon:LayoutAnchorable Title="Exception"></avalon:LayoutAnchorable><avalon:LayoutAnchorable Title="Error"></avalon:LayoutAnchorable></avalon:LayoutAnchorablePane></avalon:LayoutAnchorablePaneGroup></avalon:LayoutPanel><avalon:LayoutAnchorablePaneGroup DockMinWidth="50" DockWidth="150" FloatingWidth="270"><avalon:LayoutAnchorablePane x:Name="panelRight" DockWidth="240" FloatingWidth="240"><avalon:LayoutAnchorable Title="Solution"></avalon:LayoutAnchorable><avalon:LayoutAnchorable Title="Property"></avalon:LayoutAnchorable></avalon:LayoutAnchorablePane></avalon:LayoutAnchorablePaneGroup></avalon:LayoutPanel></avalon:LayoutRoot></avalon:DockingManager><Border Grid.Row="2" Background="{DynamicResource SliderBackground}" Height="20"></Border></Grid>
</Window>
2、TCP client的UI代码
<Page x:Class="WPF_Demo_V2.View.TcpClientPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WPF_Demo_V2.View"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="TcpClientPage"><Page.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="../Resource/Dictionary/DarkStyle.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Page.Resources><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="5*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><Grid Grid.Row="0"><Grid.ColumnDefinitions><ColumnDefinition Width="0.5*"/><ColumnDefinition Width="*"/><ColumnDefinition Width="0.5*"/><ColumnDefinition Width="*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><Label Grid.Column="0" Margin="5" Content="服务器IP:" HorizontalContentAlignment="Right" Foreground="{DynamicResource Foreground}"/><TextBox Grid.Column="1" Margin="5" Text="{Binding Ip}" Foreground="{DynamicResource Foreground}" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" Background="{DynamicResource SliderBackground}"/><Label Grid.Column="2" Margin="5" Content="Port:" HorizontalContentAlignment="Right" Foreground="{DynamicResource Foreground}" /><TextBox Grid.Column="3" Margin="5" Text="{Binding Port}" Foreground="{DynamicResource Foreground}" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" Background="{DynamicResource SliderBackground}"/><Button Grid.Column="4" Margin="40,5" BorderBrush="{DynamicResource Green}" Content="{Binding BtnStartOrStop}" Command="{Binding StartOrStopClientCommand}" Background="{DynamicResource SliderBackground}" Foreground="{DynamicResource Foreground}"/></Grid><RichTextBox Grid.Row="1" x:Name="myRichTextBox" Background="{DynamicResource WindowBackground}" IsReadOnly="True" Foreground="{DynamicResource Foreground}"><RichTextBox.ContextMenu><ContextMenu><MenuItem Header="清空" Command="{Binding ClearRichTextData}"/></ContextMenu></RichTextBox.ContextMenu><FlowDocument><Paragraph x:Name="progressPara"></Paragraph></FlowDocument></RichTextBox><!--<TextBox Text="{Binding ServerLog}" IsReadOnly="True" Name="txtLog" AcceptsReturn="True" TextWrapping="Wrap" Background="{DynamicResource WindowBackground}" Foreground="{DynamicResource Foreground}"/>--><Grid Grid.Row="2" Margin="0,2" ><Grid.ColumnDefinitions><ColumnDefinition Width="3.5*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBox Grid.Column="0" Text="{Binding ServerSendMsg}" x:Name="txtMsg" AcceptsReturn="True" TextWrapping="Wrap" Background="{DynamicResource WindowBackground}" Foreground="{DynamicResource Foreground}" IsEnabled="{Binding IsEnable}"/><Grid Grid.Column="1" ><Grid.RowDefinitions><RowDefinition /><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Button Grid.Row="0" Content="发送消息" Margin="40,5" Command="{Binding SendMsgCommand}" BorderBrush="{DynamicResource Green}" Background="{DynamicResource SliderBackground}" Foreground="{DynamicResource Foreground}" IsEnabled="{Binding SendMagIsEnable}"/><StackPanel Grid.Row="1" Orientation="Horizontal"><TextBox Width="60" Margin="2" Text="{Binding AutoSendTimes}" VerticalAlignment="Center" HorizontalContentAlignment="Right" Background="{DynamicResource WindowBackground}" Foreground="{DynamicResource Foreground}" IsEnabled="{Binding IsEnable}"/><TextBlock Text="ms" VerticalAlignment="Center" Background="{DynamicResource WindowBackground}" Foreground="{DynamicResource Foreground}"/><CheckBox VerticalAlignment="Center" Style="{DynamicResource CheckBoxStyle}" Content="定时发送" Width="80" Background="{DynamicResource WindowBackground}" Foreground="{DynamicResource Foreground}" IsChecked="{Binding AutoSendIsCheck}"/></StackPanel></Grid></Grid></Grid>
</Page>
3、Tcp client后台代码实现
using System;
using System.Collections.Generic;
using System.DirectoryServices.ActiveDirectory;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using TouchSocket.Core;
using TouchSocket.Sockets;namespace WPF_Demo_V2.ViewModel
{partial class TcpClientViewModel:ObservableObject{#region 变量private TcpClient tcpClient;private System.Timers.Timer timer;public Action<Brush, string> UpdataRichText { get; set; }public Action<Brush, string, Brush, string> UpdataRichTextLine { get; set; }public Action<bool> ClearRichText { get; set; }bool isStart = false;bool isAuto = false;#endregion#region 构造函数public TcpClientViewModel(){tcpClient = new TcpClient();}#endregion#region 属性[ObservableProperty]//[NotifyCanExecuteChangedFor(nameof())]public string? ip = "192.168.2.120";//CommunityToolkit.MVVM 属性用法1 小写首字母[ObservableProperty]public string? _Port = "2300"; //CommunityToolkit.MVVM 属性用法2 下划线+大写首字母[ObservableProperty]public string _BtnStartOrStop = "连接";[ObservableProperty]public string? _ServerSendMsg;[ObservableProperty]public string? _AutoSendTimes = "1000";//100ms[ObservableProperty]public bool? _SendMagIsEnable = false;[ObservableProperty]public bool? _IsEnable = true;[ObservableProperty]private bool _AutoSendIsCheck = false;/// <summary>/// AutoSendIsCheck属性已修改触发的事件,使用partial关键字自动补全该事件,可选/// </summary>/// <param name="value"></param>partial void OnAutoSendIsCheckChanged(bool value){isAuto = value;IsEnable = !value;if (isAuto) {timer?.Dispose();timer = new System.Timers.Timer { Interval = Convert.ToUInt32(AutoSendTimes), AutoReset = true, Enabled = true };timer.Elapsed += (s, e) => Application.Current.Dispatcher.BeginInvoke(new Action(() =>{SendMsg();}));}else{if (timer != null){timer?.Dispose();}}}/// <summary>/// AutoSendIsCheck属性将要修改触发的事件,使用partial关键字自动补全该事件,可选/// </summary>/// <param name="value"></param>partial void OnAutoSendIsCheckChanging(bool value){}#endregion#region 方法#region 开始/停止服务[RelayCommand]private void StartOrStopClient() //CommunityToolkit.MVVM 命令command用法:Binding部分对应StartOrStopServerCommand 必须包含<MethodName>Command{if (!isStart){BtnStartOrStop = "断开";isStart = true;if (string.IsNullOrWhiteSpace(Ip) || string.IsNullOrWhiteSpace(Port)){MessageBox.Show("IP 和 Port 不能为空");return;}try{if (tcpClient != null){tcpClient.Setup(new TouchSocket.Core.TouchSocketConfig().SetRemoteIPHost($"{Ip}:{Port}").ConfigureContainer(a => {a.AddConsoleLogger();//添加一个日志注入}));tcpClient.Connect();//连接,连接不成功抛出异常SendMagIsEnable = true;tcpClient.Logger.Info("客户端成功连接");tcpClient.Received = (client, e) => {//从服务器收到信息string mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);string ServerLog = $"接收到信息:{mes}";DateTime dateTime = DateTime.Now;string times = dateTime.ToString("yyyy-MM-dd HH:mm:ss");Application.Current.Dispatcher.Invoke(() =>{UpdataRichTextLine.Invoke((Brush)new BrushConverter().ConvertFromString("#FFD9ECFF"), $"【{times} Client】", (Brush)new BrushConverter().ConvertFromString("#55B155"), $"{ServerLog}");//UpdataRichText.Invoke((Brush)new BrushConverter().ConvertFromString("#FFD9ECFF"), $"【{times} client】:");//UpdataRichText.Invoke((Brush)new BrushConverter().ConvertFromString("#55B155"), $"{ServerLog}");});Console.WriteLine($"接收到信息:{mes}");return EasyTask.CompletedTask;};}}catch (Exception ex){MessageBox.Show($"连接服务器失败: {ex.Message}");BtnStartOrStop = "连接";isStart = false;SendMagIsEnable = false;}}else{BtnStartOrStop = "连接";isStart = false;SendMagIsEnable = false;// 关闭Socketif (tcpClient != null){tcpClient.Close();}}}#endregion#region 发送信息按钮事件[RelayCommand]private void SendMsg(){if(!string.IsNullOrEmpty(ServerSendMsg)){byte[] data = Encoding.Default.GetBytes(ServerSendMsg);tcpClient.Send(data);}}#endregion#endregion}
}
4、UI与后台代码的关联
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using WPF_Demo_V2.ViewModel;namespace WPF_Demo_V2.View
{/// <summary>/// TcpClientPage.xaml 的交互逻辑/// </summary>public partial class TcpClientPage : Page{FlowDocument Doc = new FlowDocument();public TcpClientPage(){InitializeComponent();this.DataContext = new TcpClientViewModel() { UpdataRichText = ShowRichText, ClearRichText = ClearText, UpdataRichTextLine = ShowRichTextLine };}private void ShowRichText(Brush brush, string txt){//this.Dispatcher.Invoke(() => {var p = new Paragraph(); // Paragraph 类似于 html 的 P 标签var r = new Run(txt); // Run 是一个 Inline 的标签p.Inlines.Add(r);p.LineHeight = 1;p.Foreground = brush;//设置字体颜色Doc.Blocks.Add(p);myRichTextBox.Document = Doc;myRichTextBox.ScrollToEnd();//});}private void ShowRichTextLine(Brush fbrush, string ftxt, Brush bbrush, string btxt){//this.Dispatcher.Invoke(() => {var p = new Paragraph(); // Paragraph 类似于 html 的 P 标签var r1 = new Run(ftxt){Foreground = fbrush};p.Inlines.Add(r1);var r2 = new Run(btxt){Foreground = bbrush};p.Inlines.Add(r2);p.LineHeight = 1;Doc.Blocks.Add(p);myRichTextBox.Document = Doc;myRichTextBox.ScrollToEnd();//});}private void ClearText(bool obj){if (obj){System.Windows.Documents.FlowDocument doc = myRichTextBox.Document;doc.Blocks.Clear();}}}
}