Compare commits

..

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

9 changed files with 80 additions and 313 deletions

View File

@ -12,7 +12,6 @@
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.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:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.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.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>

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? status { get; set; }
public string? message { get; set; } public string? message { get; set; }
public string? token { get; set; } public string? token { get; set; }
public string? username { get; set; }
} }
internal class SignData internal class SignData
{ {

View File

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

View File

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

View File

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

View File

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