본문 바로가기
wpf/c#, wpf 채팅프로그램

c#,wpf 채팅프로그램 만들기 - 10 - 소켓통신(TcpListener, TcpClient)을 이용한 채팅프로그램을 만들어보자(wpf를 사용하여 채팅상대 선택 화면 만들기)

by devjh 2020. 10. 9.
반응형

채팅프로그램 만들기는 총 12개의 게시글로 구성되어있습니다.

 

첫번째 게시글 : 1:1단발성통신(동기서버 동기클라)

 

두번째 게시글 : 1:1지속성통신(동기서버 완성본 동기 클라이언트)

 

세번째 게시글 : 1:1통신(비동기서버)

 

네번째 게시글: 1:N통신(여기서부터는 여러명을 받아야하므로 당연히 비동기서버입니다.)

 

다섯번째 게시글 : 채팅프로그램 콘솔 서버

 

여섯번째 게시글 : 채팅프로그램 콘솔 클라이언트

 

일곱번째 게시글 : wpf를 통해 View를 구현한 서버

 

여덟번째 게시글 : wpf를 통해 View를 구현한 클라이언트(메인화면 만들기)

 

아홉번째 게시글 : wpf를 통해 View를 구현한 클라이언트(로그인화면 만들기)

 

열번째 게시글 : wpf를 통해 View를 구현한 클라이언트(채팅상대 선택화면 만들기)

 

열한번째 게시글 : wpf를 통해 View를 구현한 클라이언트(채팅화면 만들기)

 

열두번째 게시글 : wpf를 통해 View를 구현한 클라이언트(로직 구현)

 


 

 

 

이번게시글에서는 채팅상대를 선택하는 화면을 포스팅하겠습니다.

 

먼저 저의 채팅클라이언트의 로직은 카카오톡의 형태가 아닌 버디버디의 형태로 돌아갑니다.

 

(메시지가 오면 알림을 띄워주고 사용자가 채팅화면을 키는 형태가 아니라 채팅요청을 하면 상대방의 채팅화면이 켜지도록 구현하였습니다.)

 

즉 사용자가 직접 채팅화면을 열 수는 없고 서버에 채팅을 요청하면

 

서버가 사용자들을 연결해 채팅화면을 열어주는 방식입니다.

 

1. Mainwnidow.xaml

<Window x:Class="ChattingClient.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:ChattingClient"
        mc:Ignorable="d"
        Title="ChattingServiceClient" Height="450" Width="800" Background="#BCBCBC">
    <Border BorderThickness="0" CornerRadius="10" Margin="10,30,10,10" Background="White">
        <StackPanel Orientation="Vertical" Margin="40,15,40,5">
            <TextBlock Foreground="Gray" FontSize="50" FontFamily="Ebrima" Text="Chatting Program" TextAlignment="Center" Margin="0,0,0,20"></TextBlock>
            <TextBlock x:Name="Info" Foreground="Gray" FontSize="20" Text="채팅프로그램을 이용려면 먼저 로그인해주세요" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,30"></TextBlock>
            <Button x:Name="Login_Btn" FontSize="20" Content ="로그인" Margin="0,0,0,25" Height="45" Click="Login_Btn_Click"/>
            <Button x:Name="OneOnOneChatting_Btn" FontSize="20" Content ="1:1채팅" Margin="0,0,0,25" Height="45" Click="OneOnOneChatting_Btn_Click"/>
            <Button x:Name="GroupChatting_Btn" FontSize="20" Content ="그룹채팅" Margin="0,0,0,25" Height="45" Click="GroupChatting_Btn_Click"/>
        </StackPanel>
    </Border>
</Window>

 

 

2. MainWindow.cs

namespace ChattingClient
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        TcpClient client = null;
        public static string myName = null;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Login_Btn_Click(object sender, RoutedEventArgs e)
        {
            if (client != null)
            {
                MessageBox.Show("이미 로그인되었습니다.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            Login login = new Login();
            if (login.ShowDialog() == true)
            {
                try
                {
                    string ip = login.IpTextBox.Text;
                    string parsedName = "%^&";
                    parsedName += login.NameTextBox.Text;

                    client = new TcpClient();
                    client.Connect(ip, 9999);

                    byte[] byteData = new byte[parsedName.Length];
                    byteData = Encoding.Default.GetBytes(parsedName);
                    client.GetStream().Write(byteData, 0, byteData.Length);

                    Info.Text = string.Format("{0} 님 반갑습니다 ", login.NameTextBox.Text);
                    myName = login.NameTextBox.Text;
                }

                catch
                {
                    MessageBox.Show("서버연결에 실패하였습니다.", "Server Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    client = null;
                }
            }
        }

        private void OneOnOneChatting_Btn_Click(object sender, RoutedEventArgs e)
        {
            if (client == null)
            {
                MessageBox.Show("먼저 로그인해주세요.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }


            string getUserProtocol = myName + "<GiveMeUserList>";
            byte[] byteData = new byte[getUserProtocol.Length];
            byteData = Encoding.Default.GetBytes(getUserProtocol);

            client.GetStream().Write(byteData, 0, byteData.Length);

            UserListWindow userListWindow = new UserListWindow(StaticDefine.ONE_ON_ONE_CHATTING);

            if (userListWindow.ShowDialog() == true)
            {
                string chattingStartMessage = string.Format("{0}<ChattingStart>", userListWindow.OneOnOneReceiver);
                byte[] chattingStartByte = Encoding.Default.GetBytes(chattingStartMessage);

                client.GetStream().Write(chattingStartByte, 0, chattingStartByte.Length);
            }
        }

        private void GroupChatting_Btn_Click(object sender, RoutedEventArgs e)
        {
            if (client == null)
            {
                MessageBox.Show("먼저 로그인해주세요.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            string getUserProtocol = myName + "<GiveMeUserList>";
            byte[] byteData = new byte[getUserProtocol.Length];
            byteData = Encoding.Default.GetBytes(getUserProtocol);

            client.GetStream().Write(byteData, 0, byteData.Length);

            UserListWindow userListWindow = new UserListWindow(StaticDefine.GROUP_CHATTING);

            if (userListWindow.ShowDialog() == true)
            {
                string groupChattingUserStrData = MainWindow.myName;
                foreach (var item in userListWindow.GroupChattingReceivers)
                {
                    groupChattingUserStrData += "#";
                    groupChattingUserStrData += item.userName;
                }


                string chattingStartMessage = string.Format("{0}<GroupChattingStart>", groupChattingUserStrData);
                byte[] chattingStartByte = Encoding.Default.GetBytes(chattingStartMessage);

                client.GetStream().Write(chattingStartByte, 0, chattingStartByte.Length);
            }
        }
    }
}

1:1채팅과 그룹채팅 버튼을 눌러 유저리스트를 보여주는 윈도우를 열때

 

1:1 채팅인지 그룹채팅인지 확인하기 위한 변수를 생성자에 넣어줍니다.

 

서버에게 채팅을 요청하는 프로토콜은

 

1:1 채팅의 경우  : 상대방<ChattingStart>

그룹채팅의 경우 : 본인#상대방2#상대방3<GroupChattingStart>

 

로 정의하였습니다. 

 

3. UserListWindow.xaml

<Window x:Class="ChattingClient.UserListWindow"
        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:ChattingClient"
        mc:Ignorable="d"
        Title="UserListWindow" Height="400" Width="250" Background="#BCBCBC">
    <Grid Margin="10,10,10,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="10*"></RowDefinition>
            <RowDefinition Height="1*"></RowDefinition>
        </Grid.RowDefinitions>
        <ListView x:Name="UserListView" Grid.Row="0" BorderBrush="White">
            <ListView.Resources>
                <DataTemplate x:Key="UserCellTemplate">
                    <Grid Width="190">
                        <TextBlock  HorizontalAlignment="Center" Text="{Binding userName}"/>
                    </Grid>
                </DataTemplate>

                <DataTemplate x:Key="UserHeaderTemplate">
                    <Grid Width="190">
                        <TextBlock HorizontalAlignment="Center" Text="User"/>
                    </Grid>
                </DataTemplate>
            </ListView.Resources>

            <ListView.View>
                <GridView>
                    <GridViewColumn HeaderTemplate="{StaticResource UserHeaderTemplate}" CellTemplate="{StaticResource UserCellTemplate}"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Grid Grid.Row="1" Margin="0,5,0,5">
            <Button x:Name="Chatting_btn" Click="Chatting_btn_Click"></Button>
        </Grid>
    </Grid>
</Window>

4. UserListWindow.cs

namespace ChattingClient
{
    /// <summary>
    /// UserListWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class UserListWindow : Window
    {
        private static ObservableCollection<User> currentUserList = new ObservableCollection<User>();
        private int chattingType = -1;

        private string oneOnOneReceiver { get; set; }
        public string OneOnOneReceiver
        {
            get
            {
                return oneOnOneReceiver;
            }
            private set
            {
                oneOnOneReceiver = value;
            }
        }

        private List<User> groupChattingReceivers { get; set; }
        public List<User> GroupChattingReceivers
        {
            get
            {
                return groupChattingReceivers;
            }
            set
            {
                groupChattingReceivers = value;
            }
        }


        public UserListWindow(int chattingType)
        {
            InitializeComponent();
            UserListView.ItemsSource = currentUserList;

            currentUserList.Add(new User("Mike"));
            currentUserList.Add(new User("John"));
            currentUserList.Add(new User("Tom"));

            if (chattingType == StaticDefine.ONE_ON_ONE_CHATTING)
            {
                this.chattingType = chattingType;
                Chatting_btn.Content = "1:1채팅";
                UserListView.SelectionMode = SelectionMode.Single;
            }

            else if (chattingType == StaticDefine.GROUP_CHATTING)
            {
                this.chattingType = chattingType;
                Chatting_btn.Content = "그룹채팅";
                UserListView.SelectionMode = SelectionMode.Extended;
            }

        }

        private void Chatting_btn_Click(object sender, RoutedEventArgs e)
        {
            if (UserListView.SelectedItem == null)
            {
                MessageBox.Show("채팅상대를 선택해주세요.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (chattingType == StaticDefine.ONE_ON_ONE_CHATTING)
            {
                List<User> dummyChattingUser = UserListView.SelectedItems.Cast<User>().ToList();

                User selectedUser = (User)UserListView.SelectedItem;
                if (MainWindow.myName == selectedUser.userName)
                {
                    MessageBox.Show("자기 자신과는 채팅할 수 없습니다.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
                string msg = string.Format("{0}님에게 {1}요청을 하시겠습니까?", selectedUser.userName, Chatting_btn.Content);
                MessageBoxResult messageBoxResult = MessageBox.Show(msg, "Question", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (messageBoxResult == MessageBoxResult.No)
                {
                    return;
                }
                this.OneOnOneReceiver = selectedUser.userName;

                this.DialogResult = true;

            }
            else if (chattingType == StaticDefine.GROUP_CHATTING)
            {
                List<User> groupChattingUser = UserListView.SelectedItems.Cast<User>().ToList();
                foreach (var item in groupChattingUser)
                {
                    if (item.userName == MainWindow.myName)
                    {
                        MessageBox.Show("자기 자신과는 채팅할 수 없습니다.", "information", MessageBoxButton.OK, MessageBoxImage.Information);
                        return;
                    }
                }

                if (groupChattingUser.Count < 2)
                {
                    MessageBox.Show("그룹채팅의 최소 인원은 2명입니다.", "information", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }

                string msg = string.Format("선택유저과 {0}을 하시겠습니까?", Chatting_btn.Content);
                MessageBoxResult messageBoxResult = MessageBox.Show(msg, "Question", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (messageBoxResult == MessageBoxResult.No)
                {
                    return;
                }

                this.GroupChattingReceivers = groupChattingUser;

                this.DialogResult = true;

            }
        }
    }
}

생성자로 들어오는 채팅유형에 따라 채팅하기 버튼의 이름 및 기능, 채팅상대 다중 선택가능여부 등을 결정해줍니다.

 

아직 서버에서 보낸 유저를 받아오는 기능이 구현되지 않았기에

 

currentUserList.Add(new User("Mike"));
currentUserList.Add(new User("John"));
currentUserList.Add(new User("Tom"));

해당구문을 생성자에 추가하여 유저리스트를 보여주도록 하였습니다.

 

 

5. User.cs

namespace ChattingClient
{
    public class User
    {
        public string userName { get; set; }

        public User(string name)
        {
            this.userName = name;
        }
    }
}

UserWindow.UserListView와 바인딩할 User 클래스입니다.

 

6. StaticDefine

namespace ChattingClient
{
    class StaticDefine
    {
        public const int ONE_ON_ONE_CHATTING = 1;
        public const int GROUP_CHATTING = 2;
    }
}

스위치문의 가독성을 위해 추가하였습니다.

 

 

7. 실행화면

임시로 Mike John Tom을 추가하였습니다..

 

채팅요청 확인창

 

반응형

댓글