WPF显示富文本emoji表情+文本(类似微信)
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF显示富文本emoji表情+文本(类似微信)相关的知识,希望对你有一定的参考价值。
祝大家端午节安康!
WPF开发者QQ群: 340500857
前言
有小伙伴需要实现类似微信一样的气泡聊天emoji表情+文本。
欢迎转发、分享、点赞,谢谢大家~。
效果预览(更多效果请下载源码体验):
一、EmojiAndTextControl.cs代码如下:
/**
Github https://github.com/yanjinhuagood/WPFDevelopers.git
码云 https://gitee.com/yanjinhua/WPFDevelopers.git
**/
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfChatEmojiText
{
[TemplatePart(Name = TextBlockTemplateName, Type = typeof(TextBlock))]
[TemplatePart(Name = WrapPanelLeftTemplateName, Type = typeof(WrapPanel))]
[TemplatePart(Name = BorderTemplateName, Type = typeof(Border))]
[TemplatePart(Name = WrapPanelRightTemplateName, Type = typeof(WrapPanel))]
[TemplatePart(Name = ImageBrushLeftTemplateName, Type = typeof(ImageBrush))]
[TemplatePart(Name = ImageBrushRightTemplateName, Type = typeof(ImageBrush))]
public class EmojiAndTextControl: Control
{
private static readonly Type _typeofSelf = typeof(EmojiAndTextControl);
private const string TextBlockTemplateName = "PART_TextBlock";
private const string WrapPanelLeftTemplateName = "PART_Left";
private const string BorderTemplateName = "PART_Border";
private const string WrapPanelRightTemplateName = "PART_Right";
private const string ImageBrushLeftTemplateName = "LeftUser";
private const string ImageBrushRightTemplateName = "RightUser";
private TextBlock m_textBlock;
private WrapPanel leftWrapPanel;
private Border border;
private WrapPanel rightWrapPanel;
private ImageBrush leftImageBrush;
private ImageBrush rightImageBrush;
private bool m_IgnoreChanges = false;
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
static EmojiAndTextControl()
{
DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
m_textBlock = GetTemplateChild(TextBlockTemplateName) as TextBlock;
leftWrapPanel = GetTemplateChild(WrapPanelLeftTemplateName) as WrapPanel;
border = GetTemplateChild(BorderTemplateName) as Border;
rightWrapPanel = GetTemplateChild(WrapPanelRightTemplateName) as WrapPanel;
leftImageBrush = GetTemplateChild(ImageBrushLeftTemplateName) as ImageBrush;
rightImageBrush = GetTemplateChild(ImageBrushRightTemplateName) as ImageBrush;
UpdateEmoji();
UpdateIsRight();
UpdateRightImageSource();
UpdateLeftImageSource();
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(EmojiAndTextControl),
new UIPropertyMetadata(string.Empty, OnTextChanged));
static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
EmojiAndTextControl emoji = (EmojiAndTextControl)d;
emoji.UpdateEmoji();
}
void UpdateEmoji()
{
if (m_textBlock == null) return;
if (!m_IgnoreChanges)
{
m_IgnoreChanges = true;
//EmojiHelper.ParseText(m_textBlock);
EmojiHelperLibrary.EmojiHelper.Instance.ParseText(m_textBlock);
m_IgnoreChanges = false;
}
}
public bool IsRight
{
get { return (bool)GetValue(IsRightProperty); }
set { SetValue(IsRightProperty, value); }
}
public static readonly DependencyProperty IsRightProperty =
DependencyProperty.Register("IsRight", typeof(bool), typeof(EmojiAndTextControl), new UIPropertyMetadata(OnIsRightChanged));
static void OnIsRightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
EmojiAndTextControl control = (EmojiAndTextControl)d;
control.UpdateIsRight();
}
void UpdateIsRight()
{
if (leftWrapPanel == null
||
border == null
||
rightWrapPanel == null) return;
if (!IsRight)
{
leftWrapPanel.Visibility = Visibility.Visible;
border.Background = Brushes.White;
rightWrapPanel.Visibility = Visibility.Collapsed;
}
}
public ImageSource LeftImageSource
{
get { return (ImageSource)GetValue(LeftImageSourceProperty); }
set { SetValue(LeftImageSourceProperty, value); }
}
public static readonly DependencyProperty LeftImageSourceProperty =
DependencyProperty.Register("LeftImageSource", typeof(ImageSource), typeof(EmojiAndTextControl), new UIPropertyMetadata(OnLeftImageSourceChanged));
private static void OnLeftImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
EmojiAndTextControl control = (EmojiAndTextControl)d;
control.UpdateLeftImageSource();
}
void UpdateLeftImageSource()
{
if (leftImageBrush == null) return;
leftImageBrush.ImageSource = LeftImageSource;
}
public ImageSource RightImageSource
{
get { return (ImageSource)GetValue(RightImageSourceProperty); }
set { SetValue(RightImageSourceProperty, value); }
}
public static readonly DependencyProperty RightImageSourceProperty =
DependencyProperty.Register("RightImageSource", typeof(ImageSource), typeof(EmojiAndTextControl), new UIPropertyMetadata(OnRightImageSourceChanged));
private static void OnRightImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
EmojiAndTextControl control = (EmojiAndTextControl)d;
control.UpdateRightImageSource();
}
void UpdateRightImageSource()
{
if (rightImageBrush == null) return;
rightImageBrush.ImageSource = RightImageSource;
}
}
}
二、EmojiAndTextControlStyle.xaml代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfChatEmojiText">
<Style TargetType="local:EmojiAndTextControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:EmojiAndTextControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<WrapPanel Grid.Column="0" Name="PART_Left"
Visibility="Collapsed">
<Border Width="35" Height="35"
VerticalAlignment="Top"
Margin="0,10"
CornerRadius="4"
UseLayoutRounding="True">
<Border.Background>
<ImageBrush x:Name="LeftUser"
ImageSource="{TemplateBinding LeftImageSource}" RenderOptions.BitmapScalingMode="LowQuality " Stretch="Fill"/>
</Border.Background>
</Border>
<Path Data="M365.714 256v512q0 14.857-10.857 25.714t-25.714 10.857-25.714-10.857l-256-256q-10.857-10.857-10.857-25.714t10.857-25.714l256-256q10.857-10.857 25.714-10.857t25.714 10.857 10.857 25.714z"
Fill="White" Width="10" Height="10" Stretch="Fill"
StrokeThickness="0" Grid.Column="1"
VerticalAlignment="Top" Margin="0,20,-14,0"
UseLayoutRounding="True" SnapsToDevicePixels="True"/>
</WrapPanel>
<Border CornerRadius="4" Background="#9EEA6A"
UseLayoutRounding="True" SnapsToDevicePixels="True"
Grid.Column="1" Margin="10"
Name="PART_Border">
<TextBlock Name="PART_TextBlock" FontSize="15" Text="{TemplateBinding Text}"
Padding="6" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Border>
<WrapPanel Grid.Column="2" Name="PART_Right">
<Path Data="M329.143 512q0 14.857-10.857 25.714l-256 256q-10.857 10.857-25.714 10.857t-25.714-10.857-10.857-25.714v-512q0-14.857 10.857-25.714t25.714-10.857 25.714 10.857l256 256q10.857 10.857 10.857 25.714z"
Fill="#9EEA6A" Width="10" Height="10" Stretch="Fill"
StrokeThickness="0"
VerticalAlignment="Top" Margin="-14,20,0,0"
UseLayoutRounding="True" SnapsToDevicePixels="True"/>
<Border Width="35" Height="35"
VerticalAlignment="Top"
Margin="0,10"
CornerRadius="4"
UseLayoutRounding="True">
<Border.Background>
<ImageBrush x:Name="RightUser"
ImageSource="{TemplateBinding RightImageSource}" RenderOptions.BitmapScalingMode="LowQuality" Stretch="Fill"/>
</Border.Background>
</Border>
</WrapPanel>
</Grid>
<!--<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsRight}" Value="false">
<Setter Property="Visibility" TargetName="PART_Left" Value="Visible"/>
<Setter Property="Background" TargetName="PART_Border" Value="White"/>
<Setter Property="Visibility" TargetName="PART_Right" Value="Collapsed"/>
</DataTrigger>
</ControlTemplate.Triggers>-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
三、MainWindow.xaml代码如下
<Window x:Class="WpfChatEmojiText.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:WpfChatEmojiText"
mc:Ignorable="d"
Title="WPFDevelopers" Height="800" Width="800"
TextOptions.TextFormattingMode="Display" UseLayoutRounding="True"
SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="NearestNeighbor">
<UniformGrid Columns="2">
<Grid Margin="10,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<RichTextBox x:Name="_LeftChat" IsReadOnly="True">
<RichTextBox.Background>
<ImageBrush ImageSource="Images\\Left.jpg" Stretch="Fill"/>
</RichTextBox.Background>
</RichTextBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="LeftButtonEmoji" Margin="10,0" Content="Emoji" Width="40" HorizontalAlignment="Left"/>
<Popup Placement="Top" PlacementTarget="{Binding ElementName=LeftButtonEmoji}"
IsOpen="{Binding ElementName=LeftButtonEmoji,Path=IsChecked}" AllowsTransparency="True"
VerticalOffset="-4">
<Border Margin="10" Background="White" CornerRadius="4"
Width="76">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" Color="#80000000"/>
</Border.Effect>
<ItemsControl ItemsSource="{Binding EmojiArray,RelativeSource={RelativeSource AncestorType=local:MainWindow}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="PART_Border" CornerRadius="2"
PreviewMouseLeftButtonDown="PART_Border_PreviewMouseLeftButtonDown"
Tag="{Binding Key}">
<Image Source="{Binding Value}" ToolTip="{Binding Name}"
Width="30" Height="30" Margin="4"
IsHitTestVisible="True"/>
</Border>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="PART_Border" Value="#FFD8D1D1"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Popup>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="LeftInput"/>
<Button Grid.Column="1" Content="发送" x:Name="LeftSend" Click="LeftSend_Click"></Button>
</Grid>
</Grid>
</Grid>
<Grid Margin="10,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<RichTextBox x:Name="_RightChat" IsReadOnly="True">
<RichTextBox.Background>
<ImageBrush ImageSource="Images\\Right.jpg" Stretch="Fill"/>
</RichTextBox.Background>
</RichTextBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="RightButtonEmoji" Margin="10,0" Content="Emoji" Width="40" HorizontalAlignment="Left"/>
<Popup Placement="Top" PlacementTarget="{Binding ElementName=RightButtonEmoji}"
IsOpen="{Binding ElementName=RightButtonEmoji,Path=IsChecked}" AllowsTransparency="True"
VerticalOffset="-4">
<Border Margin="10" Background="White" CornerRadius="4"
Width="76">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" Color="#80000000"/>
</Border.Effect>
<ItemsControl ItemsSource="{Binding EmojiArray,RelativeSource={RelativeSource AncestorType=local:MainWindow}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="PART_Border" CornerRadius="2"
PreviewMouseLeftButtonDown="PART_Border_RightPreviewMouseLeftButtonDown"
Tag="{Binding Key}">
<Image Source="{Binding Value}" ToolTip="{Binding Name}"
Width="30" Height="30" Margin="4"
IsHitTestVisible="True"/>
</Border>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="PART_Border" Value="#FFD8D1D1"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Popup>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="RightInput"/>
<Button Grid.Column="1" Content="发送" x:Name="RightSend" Click="RightSend_Click"></Button>
</Grid>
</Grid>
</Grid>
</UniformGrid>
</Window>
四、MainWindow.cs.xaml代码如下
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
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;
namespace WpfChatEmojiText
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public IEnumerable EmojiArray
{
get { return (IEnumerable)GetValue(EmojiArrayProperty); }
set { SetValue(EmojiArrayProperty, value); }
}
public static readonly DependencyProperty EmojiArrayProperty =
DependencyProperty.Register("EmojiArray", typeof(IEnumerable), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
var emojiModels = new List<EmojiModel>();
EmojiHelperLibrary.EmojiHelper.Instance._emojiHeight = 30;
EmojiHelperLibrary.EmojiHelper.Instance._emojiWidth = 30;
var m_Emojis = new Dictionary<string, string>();
var emojiPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "emoji");
var directory = new DirectoryInfo(emojiPath);
foreach (var item in directory.GetFiles())
{
var _key = $"[{Path.GetFileNameWithoutExtension(item.Name)}]";
m_Emojis.Add(_key, item.FullName);
emojiModels.Add(new EmojiModel { Name = Path.GetFileNameWithoutExtension(item.Name), Key = _key, Value = item.FullName });
}
EmojiHelperLibrary.EmojiHelper.Instance.m_Emojis = m_Emojis;
EmojiArray = emojiModels;
}
private void PART_Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
LeftButtonEmoji.IsChecked = false;
var send = sender as Border;
LeftInput.Text += send.Tag.ToString();
LeftInput.Focus();
LeftInput.SelectionStart = LeftInput.Text.Length;
}
private void LeftSend_Click(object sender, RoutedEventArgs e)
{
LeftChat();
}
void LeftChat()
{
var leftText = new EmojiAndTextControl();
leftText.IsRight = true;
leftText.RightImageSource = new BitmapImage(new Uri("pack://application:,,,/Images/UserImages/jingtao.png"));
leftText.Text = LeftInput.Text;
var leftPara = new Paragraph();
leftPara.TextAlignment = TextAlignment.Right;
leftPara.Inlines.Add(leftText);
_LeftChat.Document.Blocks.Add(leftPara);
var rightText = new EmojiAndTextControl();
rightText.IsRight = false;
rightText.LeftImageSource = new BitmapImage(new Uri("pack://application:,,,/Images/UserImages/jingtao.png"));
rightText.Text = LeftInput.Text;
var rightPara = new Paragraph();
rightPara.TextAlignment = TextAlignment.Left;
rightPara.Inlines.Add(rightText);
_RightChat.Document.Blocks.Add(rightPara);
LeftInput.Text = string.Empty;
LeftInput.Focus();
}
void RightChat()
{
var leftText = new EmojiAndTextControl();
leftText.IsRight = true;
leftText.RightImageSource = new BitmapImage(new Uri("pack://application:,,,/Images/UserImages/yanjinhua.png"));
leftText.Text = RightInput.Text;
var leftPara = new Paragraph();
leftPara.TextAlignment = TextAlignment.Right;
leftPara.Inlines.Add(leftText);
_RightChat.Document.Blocks.Add(leftPara);
var rightText = new EmojiAndTextControl();
rightText.IsRight = false;
rightText.LeftImageSource = new BitmapImage(new Uri("pack://application:,,,/Images/UserImages/yanjinhua.png"));
rightText.Text = RightInput.Text;
var rightPara = new Paragraph();
rightPara.TextAlignment = TextAlignment.Left;
rightPara.Inlines.Add(rightText);
_LeftChat.Document.Blocks.Add(rightPara);
RightInput.Text = string.Empty;
RightInput.Focus();
}
private void RightSend_Click(object sender, RoutedEventArgs e)
{
RightChat();
}
private void PART_Border_RightPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
RightButtonEmoji.IsChecked = false;
var send = sender as Border;
RightInput.Text += send.Tag.ToString();
RightInput.Focus();
RightInput.SelectionStart = RightInput.Text.Length;
}
}
public class EmojiModel
{
public string Name { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
}
源码地址
github:https://github.com/yanjinhuagood/WPFDevelopers.git
gitee:https://gitee.com/yanjinhua/WPFDevelopers.git
WPF开发者QQ群: 340500857
blogs: https://www.cnblogs.com/yanjinhua
Github:https://github.com/yanjinhuagood
出处:https://www.cnblogs.com/yanjinhua
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载请著名作者 出处 https://github.com/yanjinhuagood
以上是关于WPF显示富文本emoji表情+文本(类似微信)的主要内容,如果未能解决你的问题,请参考以下文章
AndroidRichText 让Textview轻松的支持富文本(图像ImageSpan点击效果等等类似QQ微信聊天)