转载

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聊天室(三)

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

UWP开发之StreamSocket聊天室(五)

正文到此结束
Loading...