C#/WPF
[WPF] CompositeCollection 사용하여 채팅 방 구현하는 방법
kjun.kr
2023. 3. 17. 21:20
728x90
728x170
위 그림처럼 CompositeCollection 사용하여 채팅 방 구현하는 방법입니다.
CompositeCollection 을 사용하면 여러 컬렉션 및 항목을 하나의 목록으로 표시할수 있습니다.
1. CompositeCollection 에 사용될 Class 선언
InboundMessage.cs
namespace Wpf.CompositeTest.Models
{
public class InboundMessage
{
public int MessageId { set; get; }
public string? Message { set; get; }
public string? ReceivedTime { set; get; }
}
}
OutboundMessage.cs
namespace Wpf.CompositeTest.Models
{
public class OutboundMessage
{
public int MessageId { set; get; }
public string? Message { set; get; }
public string? SentTime { set; get; }
}
}
2. 채팅 말풍선 UserControl 정의
InboundMessageBubble.xaml
<UserControl
x:Class="Wpf.CompositeTest.Controls.InboundMessageBubble"
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:local="clr-namespace:Wpf.CompositeTest.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Background="CadetBlue">
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Margin="5,5,5,0"
Padding="10"
HorizontalAlignment="Left"
Background="White"
CornerRadius="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock
Name="tbTextMessage"
FontSize="{Binding InboundMessageBubbleFontSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
Text="{Binding TextMessage, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
TextWrapping="Wrap" />
<TextBlock
Name="lblTimeStamp"
Grid.Row="2"
Padding="0"
HorizontalAlignment="Right"
Text="{Binding TimeStamp, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
</Grid>
</Border>
<Path
Grid.Row="1"
Data="M 12,0 L 18,10 L 25,0"
Fill="White"
Stroke="Black"
StrokeThickness="0" />
</Grid>
</UserControl>
InboundMessageBubble.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace Wpf.CompositeTest.Controls
{
/// <summary>
/// InboundMessageBubble.xaml에 대한 상호 작용 논리
/// </summary>
public partial class InboundMessageBubble : UserControl
{
public InboundMessageBubble()
{
InitializeComponent();
}
public static readonly DependencyProperty TextMessageProperty = DependencyProperty.Register("TextMessage", typeof(string), typeof(InboundMessageBubble), new UIPropertyMetadata(string.Empty));
public string TextMessage
{
get { return (string)GetValue(TextMessageProperty); }
set { SetValue(TextMessageProperty, value); }
}
public static readonly DependencyProperty TimeStampProperty = DependencyProperty.Register("TimeStamp", typeof(string), typeof(InboundMessageBubble), new UIPropertyMetadata(string.Empty));
public string TimeStamp
{
get { return (string)GetValue(TimeStampProperty); }
set { SetValue(TimeStampProperty, value); }
}
public static readonly DependencyProperty InboundMessageBubbleFontSizeProperty = DependencyProperty.Register("InboundMessageBubbleFontSize", typeof(string), typeof(InboundMessageBubble), new PropertyMetadata("", InboundMessageBubbleFontSizeChanged));
public string InboundMessageBubbleFontSize
{
get { return (string)GetValue(InboundMessageBubbleFontSizeProperty); }
set
{
SetValue(InboundMessageBubbleFontSizeProperty, value);
}
}
private static void InboundMessageBubbleFontSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is InboundMessageBubble instance)
{
instance.lblTimeStamp.Height = (Convert.ToDouble(instance.InboundMessageBubbleFontSize) / 3) + 4;
instance.lblTimeStamp.FontSize = (Convert.ToDouble(instance.InboundMessageBubbleFontSize) / 3) + 2;
}
}
}
}
OutboundMessageBubble.xaml
<UserControl
x:Class="Wpf.CompositeTest.Controls.OutboundMessageBubble"
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:local="clr-namespace:Wpf.CompositeTest.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Background="CadetBlue">
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Margin="5,5,5,0"
Padding="10"
HorizontalAlignment="Right"
Background="White"
CornerRadius="10">
<StackPanel>
<TextBlock
Name="tbTextMessage"
FontSize="{Binding OutboundMessageBubbleFontSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
Text="{Binding TextMessage, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
TextWrapping="Wrap" />
<TextBlock
Name="lblTimeStamp"
Grid.Row="2"
Padding="0"
HorizontalAlignment="Right"
Text="{Binding TimeStamp, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
</StackPanel>
</Border>
<Path
Grid.Row="1"
Margin="0,0,12,0"
HorizontalAlignment="Right"
Data="M 12,0 L 18,10 L 25,0"
Fill="White"
Stroke="Black"
StrokeThickness="0" />
</Grid>
</UserControl>
OutboundMessageBubble.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace Wpf.CompositeTest.Controls
{
/// <summary>
/// OutboundMessageBubble.xaml에 대한 상호 작용 논리
/// </summary>
public partial class OutboundMessageBubble : UserControl
{
public OutboundMessageBubble()
{
InitializeComponent();
}
public static readonly DependencyProperty TextMessageProperty = DependencyProperty.Register("TextMessage", typeof(string), typeof(OutboundMessageBubble), new UIPropertyMetadata(string.Empty));
public string TextMessage
{
get { return (string)GetValue(TextMessageProperty); }
set { SetValue(TextMessageProperty, value); }
}
public static readonly DependencyProperty TimeStampProperty = DependencyProperty.Register("TimeStamp", typeof(string), typeof(OutboundMessageBubble), new UIPropertyMetadata(string.Empty));
public string TimeStamp
{
get { return (string)GetValue(TimeStampProperty); }
set { SetValue(TimeStampProperty, value); }
}
public static readonly DependencyProperty OutboundMessageBubbleFontSizeProperty = DependencyProperty.Register("OutboundMessageBubbleFontSize", typeof(string), typeof(OutboundMessageBubble), new PropertyMetadata("", OutboundMessageBubbleFontSizeChanged));
public string OutboundMessageBubbleFontSize
{
get { return (string)GetValue(OutboundMessageBubbleFontSizeProperty); }
set
{
SetValue(OutboundMessageBubbleFontSizeProperty, value);
}
}
private static void OutboundMessageBubbleFontSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is OutboundMessageBubble instance)
{
instance.lblTimeStamp.Height = (Convert.ToDouble(instance.OutboundMessageBubbleFontSize) / 3) + 2;
instance.lblTimeStamp.FontSize = (Convert.ToDouble(instance.OutboundMessageBubbleFontSize) / 3);
}
}
}
}
3. 화면에서 사용
MainWindow.xaml
<Window
x:Class="Wpf.CompositeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.CompositeTest.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wpf.CompositeTest"
xmlns:localmodel="clr-namespace:Wpf.CompositeTest.Models"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="500"
Height="850"
mc:Ignorable="d">
<Grid>
<ScrollViewer Background="CadetBlue" VerticalScrollBarVisibility="Auto">
<ItemsControl Name="conversationList">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type localmodel:InboundMessage}">
<controls:InboundMessageBubble
Margin="0,0,100,0"
HorizontalAlignment="Left"
InboundMessageBubbleFontSize="24"
TextMessage="{Binding Message}"
TimeStamp="{Binding ReceivedTime}" />
</DataTemplate>
<DataTemplate DataType="{x:Type localmodel:OutboundMessage}">
<controls:OutboundMessageBubble
Margin="100,0,0,0"
HorizontalAlignment="Right"
OutboundMessageBubbleFontSize="24"
TextMessage="{Binding Message}"
TimeStamp="{Binding SentTime}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;
using Wpf.CompositeTest.Models;
namespace Wpf.CompositeTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
CompositeCollection compositeCollection = new CompositeCollection();
public MainWindow()
{
InitializeComponent();
ObservableCollection<InboundMessage> inboundMessages = new ObservableCollection<InboundMessage>();
inboundMessages.Add(new InboundMessage { MessageId = 1, Message = "오늘 약속 장소는", ReceivedTime = new DateTime(2023, 12, 10, 12, 50, 12).ToString("yyyy-MM-dd HH:mm:ss") });
inboundMessages.Add(new InboundMessage { MessageId = 2, Message = "강남역 10번 출구야", ReceivedTime = new DateTime(2023, 12, 10, 12, 50, 15).ToString("yyyy-MM-dd HH:mm:ss") });
ObservableCollection<OutboundMessage> outboundMessages = new ObservableCollection<OutboundMessage>();
outboundMessages.Add(new OutboundMessage { MessageId = 1, Message = "그래 알았어", SentTime = new DateTime(2023, 12, 10, 12, 51, 30).ToString("yyyy-MM-dd HH:mm:ss") });
outboundMessages.Add(new OutboundMessage { MessageId = 2, Message = "몇 시쯤 볼까?", SentTime = new DateTime(2023, 12, 10, 12, 51, 45).ToString("yyyy-MM-dd HH:mm:ss") });
this.compositeCollection.Add(new CollectionContainer() { Collection = inboundMessages });
this.compositeCollection.Add(new CollectionContainer() { Collection = outboundMessages });
this.compositeCollection.Add(new InboundMessage { MessageId = 1, Message = "7시", ReceivedTime = new DateTime(2023, 12, 10, 12, 53, 02).ToString("yyyy-MM-dd HH:mm:ss") });
this.compositeCollection.Add(new OutboundMessage { MessageId = 1, Message = "ㅇㅇ", SentTime = new DateTime(2023, 12, 10, 12, 54, 55).ToString("yyyy-MM-dd HH:mm:ss") });
conversationList.ItemsSource = compositeCollection;
}
}
}
[Source]
https://github.com/kei-soft/KJunBlog/tree/master/Wpf.CompositeTest
참고
https://learn.microsoft.com/ko-kr/dotnet/desktop/wpf/data/how-to-implement-a-compositecollection?view=netframeworkdesktop-4.8
728x90
그리드형