UWP开发之StreamSocket聊天室(五)

来源:互联网 发布:地籍数据标准 编辑:程序博客网 时间:2024/06/01 07:33
这篇文章是"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
0 0
原创粉丝点击