Compare commits

..

No commits in common. "15147b88a6562b2ce547d8380c16f4e7955d4428" and "c3496de067dadf4564601ced99f6e394bd50f5bc" have entirely different histories.

9 changed files with 80 additions and 313 deletions

View File

@ -5,15 +5,14 @@
xmlns:local="clr-namespace:chatclient"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary Source="pack://application:,,,/chatclient;component/Data/MaterialTrayMenuItem.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -1,45 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<!-- Material Design 托盘菜单样式 -->
<Style x:Key="MaterialTrayMenuItem" TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="12,8"/>
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Border x:Name="Border"
Background="Transparent"
CornerRadius="4"
SnapsToDevicePixels="True">
<Grid>
<materialDesign:Ripple Content="{TemplateBinding Header}" Background="Transparent" Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" Padding="{TemplateBinding Padding}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaterialDesignSelection}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MaterialTrayMenu" TargetType="ContextMenu">
<Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}"/>
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignDivider}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="16" ShadowDepth="4" Color="#40000000"/>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -1,122 +0,0 @@
using System.Windows;
using Hardcodet.Wpf.TaskbarNotification;
using System.Windows.Controls;
using MaterialDesignThemes.Wpf;
using System.Windows.Input;
using System.Drawing;
using System.Diagnostics;
namespace chatclient.Data
{
public class TrayIconManager
{
private readonly TaskbarIcon _trayIcon;
private readonly Window _mainWindow;
public TrayIconManager(Window mainWindow)
{
_mainWindow = mainWindow;
// 创建托盘图标
_trayIcon = new TaskbarIcon
{
Icon = new System.Drawing.Icon("resource/chat.ico"),
ToolTipText = "Material Design Application",
ContextMenu = CreateContextMenu()
};
// 注册事件
_trayIcon.TrayMouseDoubleClick += TrayIcon_TrayMouseDoubleClick;
_mainWindow.Closing += MainWindow_ClosingHandler;
}
private void MainWindow_ClosingHandler(object? sender, System.ComponentModel.CancelEventArgs e)
{
// 取消关闭操作,改为最小化到托盘
e.Cancel = true;
_mainWindow.Hide();
// 显示通知
//_trayIcon.ShowBalloonTip("Application minimized",
// "The application is running in the system tray",
// BalloonIcon.Info);
}
private ContextMenu CreateContextMenu()
{
// 创建Material Design风格的上下文菜单
var contextMenu = new ContextMenu
{
Style = (Style)Application.Current.Resources["MaterialTrayMenu"]
};
// 添加菜单项
contextMenu.Items.Add(CreateMenuItem("Open Application", PackIconKind.WindowRestore, OpenApp_Click));
contextMenu.Items.Add(CreateMenuItem("Settings", PackIconKind.Cog, Settings_Click));
contextMenu.Items.Add(new Separator { Style = (Style)Application.Current.Resources["MaterialDesignLightSeparator"] });
contextMenu.Items.Add(CreateMenuItem("Check for Updates", PackIconKind.Update, Updates_Click));
contextMenu.Items.Add(CreateMenuItem("Help", PackIconKind.HelpCircle, Help_Click));
contextMenu.Items.Add(new Separator { Style = (Style)Application.Current.Resources["MaterialDesignLightSeparator"] });
contextMenu.Items.Add(CreateMenuItem("Exit", PackIconKind.Power, Exit_Click));
return contextMenu;
}
private MenuItem CreateMenuItem(string header, PackIconKind iconKind, RoutedEventHandler clickHandler)
{
var menuItem = new MenuItem
{
Header = header,
Style = (Style)Application.Current.Resources["MaterialTrayMenuItem"],
Icon = new PackIcon { Kind = iconKind, Width = 20, Height = 20 }
};
menuItem.Click += clickHandler;
return menuItem;
}
private void TrayIcon_TrayMouseDoubleClick(object sender, RoutedEventArgs e)
{
RestoreApplication();
}
private void OpenApp_Click(object sender, RoutedEventArgs e)
{
RestoreApplication();
}
private void RestoreApplication()
{
_mainWindow.Show();
_mainWindow.WindowState = WindowState.Normal;
_mainWindow.Activate();
}
private void Settings_Click(object sender, RoutedEventArgs e)
{
// 实现设置逻辑
}
private void Updates_Click(object sender, RoutedEventArgs e)
{
// 实现更新检查逻辑
}
private void Help_Click(object sender, RoutedEventArgs e)
{
// 实现帮助逻辑
}
private void Exit_Click(object sender, RoutedEventArgs e)
{
// 释放托盘图标资源
Dispose();
Application.Current.Shutdown();
}
public void Dispose()
{
_trayIcon.Dispose();
}
}
}

View File

@ -20,7 +20,6 @@ namespace chatclient.Data
public string? status { get; set; }
public string? message { get; set; }
public string? token { get; set; }
public string? username { get; set; }
}
internal class SignData
{

View File

@ -8,7 +8,7 @@
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" x:Class="chatclient.LoginWindow"
mc:Ignorable="d"
Title="LoginWindow" Height="590" Width="360" MinHeight="590" MinWidth="360" MaxHeight="590" MaxWidth="360"
ResizeMode="NoResize" Closing="Window_Closing">
ResizeMode="NoResize">
<TabControl>
<TabItem Header="登录账号" Cursor="Hand" Height="40">

View File

@ -213,10 +213,6 @@ namespace chatclient
// 例如UserName = LoadSavedUsername();
// Update("UserName");
}
public void Window_Closing(object sender, CancelEventArgs e)
{
if(MainWindow.token == null) Application.Current.Shutdown();
}
}
}

View File

@ -8,16 +8,16 @@
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" x:Class="chatclient.MainWindow"
mc:Ignorable="d"
Title="ChatWindow" Height="450" Width="800" MinHeight="240" MinWidth="380"
Style="{StaticResource MaterialDesignWindow}" Closed="MainWindow_Closed" Loaded="MainWindow_Loaded">
Style="{StaticResource MaterialDesignWindow}">
<Grid>
<materialDesign:Card>
<TabControl VerticalContentAlignment="Bottom" materialDesign:ColorZoneAssist.Mode="PrimaryMid" Style="{StaticResource MaterialDesignNavigationRailTabControl}">
<materialDesign:NavigationRailAssist.FloatingContent>
<StackPanel>
<Button Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Margin="12">
<Button Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Margin="12" >
<Image Source="/resource/user.png" />
</Button>
<TextBlock Text="{Binding UserName}" TextAlignment="Center" MaxWidth="64" MaxHeight="64" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Username}"/>
</StackPanel>
</materialDesign:NavigationRailAssist.FloatingContent>
<TabItem>
@ -69,7 +69,7 @@
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧头像仅当Alignment=Left时显示 -->
<Border x:Name="leftAvatar" Grid.Column="0" Width="40" Height="40" Margin="10,0,5,0" CornerRadius="20" VerticalAlignment="Top">
<Border x:Name="leftAvatar" Grid.Column="0" Width="40" Height="40" Margin="10,0,10,0" CornerRadius="20" VerticalAlignment="Top">
<!-- 图片头像 -->
<Image Source="{Binding Image}" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Clip>
@ -86,7 +86,7 @@
</StackPanel>
</materialDesign:Card>
<!-- 右侧头像仅当Alignment=Right时显示 -->
<Border x:Name="rightAvatar" Grid.Column="2" Width="40" Height="40" Margin="5,0,10,0" CornerRadius="20" VerticalAlignment="Top">
<Border x:Name="rightAvatar" Grid.Column="2" Width="40" Height="40" Margin="10,0,10,0" CornerRadius="20" VerticalAlignment="Top">
<Image Source="{Binding Image}" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Clip>
<EllipseGeometry Center="20,20" RadiusX="20" RadiusY="20"/>
@ -112,18 +112,21 @@
</ItemsControl>
</ScrollViewer>
<materialDesign:Card materialDesign:ElevationAssist.Elevation="Dp8" Grid.Row="2">
<Grid Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="txtMessage" Grid.Row="0" materialDesign:HintAssist.Hint="输入消息..." AcceptsReturn="True" VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap" MinHeight="50" MaxHeight="100" Margin="5" BorderBrush="#00000000"/>
<Button x:Name="btnSend" Grid.Row="1" Content="发送" MinWidth="80" Style="{StaticResource MaterialDesignRaisedButton}"
Click="SendMessage_Click" Width="20" HorizontalAlignment="Right" Margin="2,2,4,4"/>
</Grid>
</materialDesign:Card>
<Grid Grid.Row="2" Background="{DynamicResource MaterialDesign.Brush.Primary.Foreground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="txtMessage" Grid.Column="0"
materialDesign:HintAssist.Hint="输入消息..."
AcceptsReturn="True" VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap" MinHeight="60" MaxHeight="120" Margin="5"/>
<Button x:Name="btnSend" Grid.Column="1" Content="发送" MinWidth="80"
Style="{StaticResource MaterialDesignRaisedButton}"
Click="SendMessage_Click"/>
</Grid>
</Grid>
</Grid>
</TabItem>
@ -140,7 +143,5 @@
</TabItem>
</TabControl>
</materialDesign:Card>
<!--信息框-->
<materialDesign:Snackbar x:Name="SnackbarThree" MessageQueue="{materialDesign:MessageQueue}" />
</Grid>
</Window>

View File

@ -56,7 +56,6 @@ namespace chatclient
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(UpdateName));
}
private TrayIconManager? _trayManager;
public MainWindow()
{
InitializeComponent();
@ -85,8 +84,6 @@ namespace chatclient
MessageScroller.ScrollToEnd();
}), DispatcherPriority.ContextIdle);
};
//Loaded += MainWindow_Loaded;
//Closed += MainWindow_Closed;
}
public static void StartReceive()
{
@ -138,7 +135,6 @@ namespace chatclient
if (LoginResponse!.status == "success" && LoginResponse != null)
{
token = LoginResponse.token;
UserName = LoginResponse.username ?? "Unnamed";
Application.Current.Dispatcher.Invoke(() =>
{
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
@ -185,37 +181,18 @@ namespace chatclient
Application.Current.Dispatcher.Invoke(() =>
{
// 处理聊天消息
if (chat.user == UserName)
var chatmessage = new ChatMessage
{
var chatmessage = new ChatMessage
{
Sender = chat.user ?? "未知用户",
Type = MessageType.Text,
Image = new BitmapImage(new Uri(chat.image ?? "pack://application:,,,/resource/user.png", UriKind.Absolute)),
Content = chat.message ?? "(无内容)",
Timestamp = DateTime.Now,
Alignment = HorizontalAlignment.Right,
SenderColor = new SolidColorBrush(Colors.Blue)
};
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
mainWindow?.Messages.Add(chatmessage);
}
else
{
var chatmessage = new ChatMessage
{
Sender = chat.user ?? "未知用户",
Type = MessageType.Text,
Image = new BitmapImage(new Uri(chat.image ?? "pack://application:,,,/resource/user.png", UriKind.Absolute)),
Content = chat.message ?? "(无内容)",
Timestamp = chat.timestamp,
Alignment = HorizontalAlignment.Left,
SenderColor = new SolidColorBrush(Colors.Black)
};
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
mainWindow?.Messages.Add(chatmessage);
}
Sender = chat.user ?? "未知用户",
Type = MessageType.Text,
Image = new BitmapImage(new Uri(chat.image ?? "pack://application:,,,/resource/user.png", UriKind.Absolute)),
Content = chat.message ?? "无内容",
Timestamp = chat.timestamp,
Alignment = HorizontalAlignment.Left,
SenderColor = new SolidColorBrush(Colors.Black)
};
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
mainWindow?.Messages.Add(chatmessage);
});
}
else
@ -243,11 +220,11 @@ namespace chatclient
log.Error("处理响应时发生错误", ex);
}
}
private async void SendMessage_Click(object sender, RoutedEventArgs e)
private void SendMessage_Click(object sender, RoutedEventArgs e)
{
await SendMessage();
SendMessage();
}
private async Task SendMessage()
private void SendMessage()
{
if (string.IsNullOrWhiteSpace(txtMessage.Text))
return;
@ -257,90 +234,54 @@ namespace chatclient
// 判断是否为群组,若是则收件人设为“所有人”,否则为联系人显示名
//string recipient = contact?.IsGroup == true ? "所有人" : contact?.DisplayName;
// 弃用的方法
// 创建新消息
//var newMessage = new ChatMessage
//{
// Sender = "我",
// Type = MessageType.Text,
// Image = new BitmapImage(new Uri("pack://application:,,,/resource/user.png", UriKind.Absolute)), // 默认头像
// Content = txtMessage.Text,
// Timestamp = DateTime.Now,
// Alignment = HorizontalAlignment.Right, // 自己发送的消息靠右
// SenderColor = new SolidColorBrush(Colors.Blue)
//};
var newMessage = new ChatMessage
{
Sender = "我",
Type = MessageType.Text,
Image = new BitmapImage(new Uri("pack://application:,,,/resource/user.png", UriKind.Absolute)), // 默认头像
Content = txtMessage.Text,
Timestamp = DateTime.Now,
Alignment = HorizontalAlignment.Right, // 自己发送的消息靠右
SenderColor = new SolidColorBrush(Colors.Blue)
};
var newChatMessage = new ChatData
{
type = "chat",
message = txtMessage.Text
message = newMessage.Content
};
string ChatJsonData = JsonSerializer.Serialize(newChatMessage);
byte[] dataBytes = Encoding.UTF8.GetBytes(ChatJsonData);
log.Info($"向服务器聊天信息(长度:{dataBytes.Length})");
// 检查Socket是否可用
if (Client?.Connected == true)
if (Client != null)
{
Client.Send(dataBytes);
Application.Current.Dispatcher.Invoke(() =>
if (Client.Connected)
{
txtMessage.Clear();
});
return;
Client.Send(dataBytes);
}
else
{
try
{
log.Info("未连接服务器,尝试连接");
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Client.Connect(IPAddress.Parse(Server.ServerIP), Server.ServerPort);
}
catch (Exception ex)
{
log.Error(ex);
Client.Close();
}
//finally
//{
//
//}
}
}
log.Info("未连接服务器,尝试异步连接");
// 异步连接操作
await Task.Run(() =>
{
try
{
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Client?.Connect(IPAddress.Parse(Server.ServerIP), Server.ServerPort);
StartReceive();
Client?.Send(dataBytes);
Application.Current.Dispatcher.Invoke(() =>
{
txtMessage.Clear();
});
}
catch (Exception ex)
{
log.Error($"连接失败: {ex.Message}");
Client?.Close();
Application.Current.Dispatcher.Invoke(() =>
{
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
{
QueueMessage("连接失败,请检查网络设置或服务器状态。");
}
});
}
});
// 添加到消息列表
//Messages.Add(newMessage);
}
private void QueueMessage(string message)
{
if (SnackbarThree.MessageQueue is { } messageQueue)
{
//use the message queue to send a message.
//the message queue can be called from any thread
Task.Factory.StartNew(() => messageQueue.Enqueue(message));
}
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// 初始化托盘管理器
_trayManager = new TrayIconManager(this);
}
private void MainWindow_Closed(object sender, System.EventArgs e)
{
// 清理资源
Client?.Shutdown(SocketShutdown.Both);
Client?.Close();
Client?.Dispose();
token = null;
_trayManager?.Dispose();
Messages.Add(newMessage);
// 清空输入框
txtMessage.Clear();
}
}
}

View File

@ -18,9 +18,7 @@
</PropertyGroup>
<ItemGroup>
<Content Include="resource\chat.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resource\chat.ico" />
</ItemGroup>
<ItemGroup>