更新 App.xaml 以添加 MaterialDesignColors 主题支持,并调整资源字典结构。重命名 chatapi.cs 中的 ChatData 类为 ChatRegisterData,并新增 ChatData 类以支持聊天消息格式。在 MainWindow.xaml 中更新用户头像路径并重构消息显示布局,使用 Grid 以改善对齐效果。修改 MainWindow.xaml.cs 以适应新的数据结构,并在 chatclient.csproj 中添加应用程序图标及更新资源路径。新增 chat.ico 作为应用程序图标。

This commit is contained in:
绪山七寻 2025-06-07 01:13:39 +08:00
parent 778958042d
commit 34d79871dd
6 changed files with 139 additions and 59 deletions

View File

@ -1,18 +1,18 @@
<Application x:Class="chatclient.App" <Application x:Class="chatclient.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:chatclient" xmlns:local="clr-namespace:chatclient"
StartupUri="MainWindow.xaml"> StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" /> <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/MaterialDesign3.Defaults.xaml" />
<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.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -1,4 +1,6 @@
namespace chatclient.Data using System.Security.RightsManagement;
namespace chatclient.Data
{ {
internal class Server internal class Server
{ {
@ -32,11 +34,16 @@
{ {
public string? type { get; set; } public string? type { get; set; }
} }
internal class ChatData internal class ChatRegisterData
{ {
public string? user { get; set; } = "Unnamed"; public string? user { get; set; } = "Unnamed";
public string? message { get; set; } = null; public string? message { get; set; } = null;
public string? image { get; set; } = null; public string? image { get; set; } = null;
public DateTime timestamp { get; set; } = DateTime.Now; public DateTime timestamp { get; set; } = DateTime.Now;
} }
internal class ChatData
{
public required string type { get; set; } = "chat";
public required string message { get; set; } = "message";
}
} }

View File

@ -1,10 +1,11 @@
<Window x:Class="chatclient.MainWindow" <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:chatclient" xmlns:local="clr-namespace:chatclient"
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}"> Style="{StaticResource MaterialDesignWindow}">
@ -14,7 +15,7 @@
<materialDesign:NavigationRailAssist.FloatingContent> <materialDesign:NavigationRailAssist.FloatingContent>
<StackPanel> <StackPanel>
<Button Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Margin="12" > <Button Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}" Margin="12" >
<Image Source="/user.png" /> <Image Source="/resource/user.png" />
</Button> </Button>
<TextBlock Text="{Binding Username}"/> <TextBlock Text="{Binding Username}"/>
</StackPanel> </StackPanel>
@ -54,39 +55,73 @@
<!-- 消息区域 --> <!-- 消息区域 -->
<ScrollViewer x:Name="messageScroller" Grid.Row="1" VerticalScrollBarVisibility="Auto" <ScrollViewer x:Name="messageScroller" Grid.Row="1" VerticalScrollBarVisibility="Auto"
Padding="10" Background="{DynamicResource MaterialDesignPaper}"> Padding="10" Background="{DynamicResource MaterialDesignPaper}">
<ItemsControl x:Name="messageList"> <ItemsControl x:Name="messageList">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<materialDesign:Card Margin="0,5" Padding="10" <Grid HorizontalAlignment="{Binding Alignment}" Margin="0,5">
HorizontalAlignment="{Binding Alignment}"> <Grid.ColumnDefinitions>
<StackPanel> <!-- 左侧列:头像或空白 -->
<TextBlock Text="{Binding Sender}" FontWeight="Bold" <ColumnDefinition Width="Auto"/>
Foreground="{Binding SenderColor}"/> <!-- 中间列:消息卡片 -->
<TextBlock Text="{Binding Content}" TextWrapping="Wrap" <ColumnDefinition Width="*"/>
Margin="0,5,0,0"/> <!-- 右侧列:头像或空白 -->
<TextBlock Text="{Binding Timestamp, StringFormat='HH:mm:ss'}" <ColumnDefinition Width="Auto"/>
Foreground="Gray" FontSize="10" </Grid.ColumnDefinitions>
HorizontalAlignment="Right" Margin="0,5,0,0"/> <!-- 左侧头像仅当Alignment=Left时显示 -->
</StackPanel> <Border x:Name="leftAvatar" Grid.Column="0" Width="40" Height="40" Margin="10,0,10,0" CornerRadius="20" VerticalAlignment="Top">
</materialDesign:Card> <!-- 图片头像 -->
<Image Source="{Binding Image}" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Clip>
<EllipseGeometry Center="20,20" RadiusX="20" RadiusY="20"/>
</Image.Clip>
</Image>
</Border>
<!-- 消息卡片(始终在中间列) -->
<materialDesign:Card Grid.Column="1" Padding="10" HorizontalAlignment="{Binding Alignment}">
<StackPanel>
<TextBlock Text="{Binding Sender}" FontWeight="Bold" Foreground="{Binding SenderColor}"/>
<TextBlock Text="{Binding Content}" TextWrapping="Wrap" Margin="0,5,0,0"/>
<TextBlock Text="{Binding Timestamp, StringFormat='HH:mm:ss'}" Foreground="Gray" FontSize="10" HorizontalAlignment="Right" Margin="0,5,0,0"/>
</StackPanel>
</materialDesign:Card>
<!-- 右侧头像仅当Alignment=Right时显示 -->
<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"/>
</Image.Clip>
</Image>
</Border>
</Grid>
<!-- 根据Alignment显示正确的头像 -->
<DataTemplate.Triggers>
<!-- 左对齐消息:显示左侧头像 -->
<DataTrigger Binding="{Binding Alignment}" Value="Left">
<Setter TargetName="leftAvatar" Property="Visibility" Value="Visible"/>
<Setter TargetName="rightAvatar" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<!-- 右对齐消息:显示右侧头像 -->
<DataTrigger Binding="{Binding Alignment}" Value="Right">
<Setter TargetName="leftAvatar" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="rightAvatar" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</ScrollViewer> </ScrollViewer>
<!-- 输入区域 --> <Grid Grid.Row="2" Background="{DynamicResource MaterialDesign.Brush.Primary.Foreground}">
<Grid Grid.Row="2" Margin="0,5,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="txtMessage" Grid.Column="0" Margin="0,0,5,0" <TextBox x:Name="txtMessage" Grid.Column="0"
materialDesign:HintAssist.Hint="输入消息..." materialDesign:HintAssist.Hint="输入消息..."
AcceptsReturn="True" VerticalScrollBarVisibility="Auto" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap" MinHeight="60" MaxHeight="120" TextWrapping="Wrap" MinHeight="60" MaxHeight="120" Margin="5"/>
KeyDown="MessageInput_KeyDown"/>
<Button x:Name="btnSend" Grid.Column="1" Content="发送" MinWidth="80" <Button x:Name="btnSend" Grid.Column="1" Content="发送" MinWidth="80"
Style="{StaticResource MaterialDesignRaisedButton}" Style="{StaticResource MaterialDesignRaisedButton}"

View File

@ -165,22 +165,22 @@ namespace chatclient
} }
else if (Type.type == "chat") else if (Type.type == "chat")
{ {
var chat = JsonSerializer.Deserialize<ChatData>(msg); var chat = JsonSerializer.Deserialize<ChatRegisterData>(msg);
if (chat != null) if (chat != null)
{ {
// 处理聊天消息
var chatmessage = new ChatMessage
{
Sender = chat.user ?? "未知用户",
Type = MessageType.Text,
Image = new BitmapImage(new Uri(chat.image ?? "pack://application:,,,/user.png", UriKind.Absolute)),
Content = chat.message ?? "无内容",
Timestamp = chat.timestamp,
Alignment = HorizontalAlignment.Left,
SenderColor = new SolidColorBrush(Colors.Black)
};
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
// 处理聊天消息
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(); var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
mainWindow?.Messages.Add(chatmessage); mainWindow?.Messages.Add(chatmessage);
}); });
@ -224,25 +224,49 @@ namespace chatclient
{ {
Sender = "我", Sender = "我",
Type = MessageType.Text, Type = MessageType.Text,
Image = new BitmapImage(new Uri("pack://application:,,,/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
{
type = "chat",
message = newMessage.Content
};
string ChatJsonData = JsonSerializer.Serialize(newChatMessage);
byte[] dataBytes = Encoding.UTF8.GetBytes(ChatJsonData);
log.Info($"向服务器聊天信息(长度:{dataBytes.Length})");
if (Client != null)
{
if (Client.Connected)
{
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
//{
//
//}
}
}
// 添加到消息列表 // 添加到消息列表
Messages.Add(newMessage); Messages.Add(newMessage);
// 清空输入框 // 清空输入框
txtMessage.Clear(); txtMessage.Clear();
} }
// 消息输入框回车事件
private void MessageInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && Keyboard.Modifiers != ModifierKeys.Shift)
{
SendMessage();
e.Handled = true; // 阻止换行
}
}
} }
} }

View File

@ -6,9 +6,23 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>resource\chat.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="resource\chat.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="2.0.1" />
<PackageReference Include="log4net" Version="3.1.0" /> <PackageReference Include="log4net" Version="3.1.0" />
<PackageReference Include="MaterialDesignThemes" Version="5.2.1" /> <PackageReference Include="MaterialDesignThemes" Version="5.2.1" />
<PackageReference Include="MaterialDesignThemes.MahApps" Version="5.2.1" /> <PackageReference Include="MaterialDesignThemes.MahApps" Version="5.2.1" />
@ -16,7 +30,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="user.png" /> <Resource Include="resource\user.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB