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

 

GitHub - kei-soft/KJunBlog

Contribute to kei-soft/KJunBlog development by creating an account on GitHub.

github.com


참고
https://learn.microsoft.com/ko-kr/dotnet/desktop/wpf/data/how-to-implement-a-compositecollection?view=netframeworkdesktop-4.8

728x90
그리드형