这篇文章是"UWP开发之StreamSocket聊天室"系列的最后一篇文章,这篇文章中我们来实现聊天室服务端View的实现。
由于很多View 、ViewModel和客户端的是基本一致的所以本篇内容会比较少,很多技术重合点这里也不会再做讲解。
其实在日常的开发中我们的服务端不应该是以UWP形式来开发的,通常情况下是在服务器使用Socket技术来搭建一个IM服务端,我们这里仅仅是为了探索StreamSocket Service在UWP上如何使用才如此去做。
首先我们还是先看设置界面的Xaml布局
SettingPage
Pages/SettingPage.xaml
放上一个TextBlock来显示本机IP和服务开放的端口号,然后用ToggleSwitch来控制服务的开启与关闭,其中ToggleSwitch控件的Toggled事件绑定到了后端的ListenSocket方法上。
来看看后端代码Pages/SettingPage.xaml.cs
后台代码也很简单,ListenSocket方法根据SocketSwitch的闭合去决定是开启服务还是关闭服务。开启和关闭服务的具体操作在该界面对应的SettingViewModel中。
看下该界面的ViewModel是什么样子的
ViewModel/SettingViewModel.cs
Ok,配置界面的工作到此就完成了
MessagePage
来看看MessagePage的UI以及后台代码,和客户端的也是一毛一样的,还是贴一下代码吧,代码就不解释了,想要了解的可以看这篇文章:UWP开发之StreamSocket聊天室(四)
Pages/MessagePage.xaml
后台代码:
来看下该界面的ViewModel,ViewModel中的工作也和客户端的ViewModel工作一样:
本篇中不再介绍ViewModel在ViewModelLocator.cs里面的注册,ViewModel的注册上篇已经介绍过,这里的和上篇一毛一样,传送门:UWP开发之StreamSocket聊天室(三)
至此该系列文章已全部讲述完毕,贴一下最终的效果图:
本文出自:53078485群大咖Aran
由于很多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