UWP开发之StreamSocket聊天室(五)

这篇文章是"UWP开发之StreamSocket聊天室"系列的最后一篇文章,这篇文章中我们来实现聊天室服务端View的实现。

由于很多View 、ViewModel和客户端的是基本一致的所以本篇内容会比较少,很多技术重合点这里也不会再做讲解。

其实在日常的开发中我们的服务端不应该是以UWP形式来开发的,通常情况下是在服务器使用Socket技术来搭建一个IM服务端,我们这里仅仅是为了探索StreamSocket Service在UWP上如何使用才如此去做。

首先我们还是先看设置界面的Xaml布局


SettingPage


Pages/SettingPage.xaml


<Grid  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel Margin="8">
        <TextBlock>
            <Run Text="本机IP地址:"/>
            <Run Text="{x:Bind _vm.LocalHostName}"/>
            <LineBreak/>
            <Run Text="端口:"/>
            <Run Text="{x:Bind _vm.LocalServiceName}"/>
        </TextBlock>
        <ToggleSwitch x:Name="SocketSwitch" IsOn="{x:Bind _vm.SocketState,Mode = TwoWay}" Toggled="{x:Bind ListenSocket}" Header="开启服务" />
    </StackPanel>
</Grid>



放上一个TextBlock来显示本机IP和服务开放的端口号,然后用ToggleSwitch来控制服务的开启与关闭,其中ToggleSwitch控件的Toggled事件绑定到了后端的ListenSocket方法上。

来看看后端代码Pages/SettingPage.xaml.cs


public sealed partial class SettingPage : Page
{
    private readonly SettingViewModel _vm = ViewModelLocator.Default.SettingViewModel;
 
    public SettingPage()
    {
        InitializeComponent();
    }
 
    private void ListenSocket()
    {
        if (SocketSwitch.IsOn)
        {
            _vm.ServerSocket.Start();
        }
        else
        {
            _vm.ServerSocket.Dispose();
        }
    }
}




后台代码也很简单,ListenSocket方法根据SocketSwitch的闭合去决定是开启服务还是关闭服务。开启和关闭服务的具体操作在该界面对应的SettingViewModel中。

看下该界面的ViewModel是什么样子的

ViewModel/SettingViewModel.cs


public class SettingViewModel : ViewModelBase
{
    public UserModel UserModel { get; set; } = new UserModel {UserName = "服务器"};
 
    /// <summary>
    ///     Socket服务端
    /// </summary>
    public SocketBase ServerSocket { get; set; }
 
    /// <summary>
    ///     监听状态文本描述
    /// </summary>
    public string ListeningStateTxt { get; set; }
 
    /// <summary>
    ///     监听TCP链接的端口号
    /// </summary>
    public string LocalServiceName { get; set; }
 
    /// <summary>
    ///     本地IP
    /// </summary>
    public string LocalHostName { get; set; }
 
    /// <summary>
    ///     是否已开启 Socket 服务
    /// </summary>
    public bool SocketState { get; set; }
 
    /// <summary>
    /// 消息集合
    /// </summary>
    public ObservableCollection<MessageModel> MessageCollection { get; set; } =
        new ObservableCollection<MessageModel>();
 
    public SettingViewModel()
    {
        LocalHostName = GetLocalIp();
        LocalServiceName = "22233";
 
        //创建服务端Socket
        //(方法名忘记改了 就这样吧 CreatInkSocket 是创建Ink墨迹的服务端,前段时间做的Ink墨迹同步。大家如果看着不爽就自行改吧)
        ServerSocket = SocketFactory.CreatInkSocket(true, LocalHostName, LocalServiceName);
        //新消息到达通知
        ServerSocket.MsgReceivedAction += data =>
        {
            DispatcherHelper.CheckBeginInvokeOnUI(() => { MessageCollection.Add(data); });
            Messenger.Default.Send(data, "NewMsgAction");
        };
    }
 
    /// <summary>
    /// 获取本地ip地址
    /// </summary>
    /// <returns>ip</returns>
    private string GetLocalIp()
    {
        var icp = NetworkInformation.GetInternetConnectionProfile();
 
        if (icp?.NetworkAdapter == null) return null;
        var hostname =
            NetworkInformation.GetHostNames()
                .SingleOrDefault(
                    hn =>
                        hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                        == icp.NetworkAdapter.NetworkAdapterId);
 
        // the ip address
        return hostname?.CanonicalName;
    }
}



Ok,配置界面的工作到此就完成了

 
MessagePage

来看看MessagePage的UI以及后台代码,和客户端的也是一毛一样的,还是贴一下代码吧,代码就不解释了,想要了解的可以看这篇文章:UWP开发之StreamSocket聊天室(四)

Pages/MessagePage.xaml

<Page.Resources>
    <DataTemplate x:Key="OtherMsgDataTemplate" x:DataType="model:MessageModel">
        <Grid  Margin="0,8">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock  Grid.Row="0" Foreground="Red" VerticalAlignment="Center" >
                            <Run Text="{x:Bind User.UserName}"/>
                            <Run Text=":  "/>
                            <Run Text="{x:Bind SetDateTime}"/>
            </TextBlock>
            <Grid Grid.Row="1">
                <Border  Margin="24,0"  Padding="16,4" Background="White" CornerRadius="12" HorizontalAlignment="Left"   >
                    <TextBlock TextWrapping="Wrap" Text="{x:Bind Message}"/>
                </Border>
                <Viewbox HorizontalAlignment="Left" Margin="16,0,0,0" Height="19" VerticalAlignment="Top" Width="13.5">
                    <Path Data="M32.4762,3.74901 C28.1542,4.60015 20.7241,2.92959 13.75,0.75 C15.5005,7.13589 28.4124,17.9116 29.5357,17.4874" Fill="White"  Stretch="Fill" Stroke="White" UseLayoutRounding="False"  d:LayoutOverrides="VerticalAlignment" />
                </Viewbox>
            </Grid>
 
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="MyMsgDataTemplate" x:DataType="model:MessageModel">
        <Grid  Margin="0,8" HorizontalAlignment="Right">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock  Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Right" Foreground="Blue" >
                            <Run/>
                            <Run Text="{x:Bind SetDateTime}"/>
            </TextBlock>
            <Grid Grid.Row="1">
                <Border  Margin="24,0"  Padding="16,4" Background="White" CornerRadius="12" HorizontalAlignment="Right"   >
                    <TextBlock TextWrapping="Wrap" Text="{x:Bind Message}"/>
                </Border>
                <Viewbox HorizontalAlignment="Right" Margin="16,0" Height="19" VerticalAlignment="Top" Width="13.5" RenderTransformOrigin="0.5,0.5">
                    <Viewbox.RenderTransform>
                        <CompositeTransform ScaleX="-1"/>
                    </Viewbox.RenderTransform>
                    <Path Data="M32.4762,3.74901 C28.1542,4.60015 20.7241,2.92959 13.75,0.75 C15.5005,7.13589 28.4124,17.9116 29.5357,17.4874" Fill="White"  Stretch="Fill" Stroke="White" UseLayoutRounding="False"  d:LayoutOverrides="VerticalAlignment" />
                </Viewbox>
            </Grid>
        </Grid>
    </DataTemplate>
</Page.Resources>
 
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListView x:Name="MsgListView" Background="#FFE6E6E6" ItemsSource="{x:Bind _vm.MessageCollection}"  SelectionMode="None" >
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.ItemTemplateSelector>
                <toolkit:MsgStyleSelector MyMsgStyle="{StaticResource MyMsgDataTemplate}" OtherMsgStyle="{StaticResource OtherMsgDataTemplate}" />
            </ListView.ItemTemplateSelector>
        </ListView>
        <Grid Margin="0,8" Grid.Row="1">
            <StackPanel>
                <TextBox Text="{x:Bind _vm.TxtMsg,Mode= TwoWay}" KeyDown="{x:Bind _vm.MsgTextBoxKeyUp}"  />
                <Button Margin="0,4" Content="发送" HorizontalAlignment="Right" Click="{x:Bind _vm.SendTxtMsg}" VerticalAlignment="Bottom"/>
            </StackPanel>
        </Grid>
    </Grid>
</Grid>



后台代码:


public sealed partial class MessagePage : Page
{
    private MessageViewModel _vm = ViewModelLocator.Default.MessageViewModel;
 
    public MessagePage()
    {
        InitializeComponent();
    }
 
    private async void SendedMsgAction(MessageModel obj)
    {
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            MsgListView.ScrollIntoView(obj);
        });
    }
 
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        Messenger.Default.Register<MessageModel>(this, "NewMsgAction", SendedMsgAction);
    }
 
    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        base.OnNavigatedFrom(e);
        Messenger.Default.Unregister(this);
    }
}



来看下该界面的ViewModel,ViewModel中的工作也和客户端的ViewModel工作一样:


public class MessageViewModel : ViewModelBase
{
    private string _txtMsg;
 
    /// <summary>
    ///     要发送的文本
    /// </summary>
    public string TxtMsg
    {
        get { return _txtMsg; }
        set
        {
            _txtMsg = value;
            RaisePropertyChanged();
        }
    }
 
    /// <summary>
    /// 消息集合
    /// </summary>
    public ObservableCollection<MessageModel> MessageCollection { get;  } =
         ViewModelLocator.Default.SettingViewModel.MessageCollection;
 
 
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <returns></returns>
    public async Task SendTxtMsg()
    {
        if (string.IsNullOrEmpty(TxtMsg)) return;
        var msg = new MessageModel
        {
            MessageType = MessageType.TextMessage,
            Message = TxtMsg,
            SetDateTime = DateTime.Now,
            User = ViewModelLocator.Default.SettingViewModel.UserModel
        };
        var socket = ViewModelLocator.Default.SettingViewModel.ServerSocket;
        await socket.SendMsg(msg);
 
        msg.Horizontal = HorizontalAlignment.Right;
        DispatcherHelper.CheckBeginInvokeOnUI(() => { MessageCollection.Add(msg); });
        Messenger.Default.Send(msg, "NewMsgAction");
        TxtMsg = null;
    }
 
 
    public async void MsgTextBoxKeyUp(object sender, KeyRoutedEventArgs key)
    {
        var textBox = sender as TextBox;
        if (textBox != null) TxtMsg = textBox.Text;
        if (key.Key != VirtualKey.Enter) return;
        if (string.IsNullOrEmpty(TxtMsg))
            return;
        await SendTxtMsg();
    }
}



本篇中不再介绍ViewModel在ViewModelLocator.cs里面的注册,ViewModel的注册上篇已经介绍过,这里的和上篇一毛一样,传送门:UWP开发之StreamSocket聊天室(三)

至此该系列文章已全部讲述完毕,贴一下最终的效果图:

本文出自:53078485群大咖Aran
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值