UWP开发之StreamSocket聊天室(四)

Cle****-he UID.1073626
2015-11-12 发表

本帖最后由 entty 于 2015-11-19 00:49 编辑

本节知识点:
[list]
[*]x:Bind 的使用
[*] ItemTemplateSelector 的使用
[/list]


上篇我们完成了 UWP StreamSocket 客户端的所有ViewModel的编码工作,今天我们继续完成客户端的UI页面。

由于我们的业务逻辑都抽离到了VM里面,所以前端UI的逻辑代码基本没几行,来我们先看下设置界面的实现

一、ClientSetting界面

Pages/ClientSetting.xaml
[mw_shl_code=csharp,true]<Page.BottomAppBar>
<CommandBar Background="#00E6E6E6">
<CommandBar.Content>
<Grid/>
</CommandBar.Content>
<!--确认按钮-->
<AppBarButton Icon="Accept" Label="确定" Click="{x:Bind _vm.ConnectionToServicer}"/>
</CommandBar>
</Page.BottomAppBar>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="12">
<!--配置区 Start-->
<StackPanel Margin="0,6" Orientation="Horizontal">
<TextBlock Text="服务器IP:"/>
<TextBox Style="{StaticResource EditTextStyle}" Text="{x:Bind _vm.ServicerIp,Mode = TwoWay}" />
</StackPanel>
<StackPanel Margin="0,6" Orientation="Horizontal">
<TextBlock Text="端 口 号:" />
<TextBox Style="{StaticResource EditTextStyle}" Text="{x:Bind _vm.ServicerPort, Mode= TwoWay}" />
</StackPanel>
<StackPanel Margin="0,6" Orientation="Horizontal">
<TextBlock Text="昵 称:" />
<TextBox Style="{StaticResource EditTextStyle}" Text="{x:Bind _vm.UserModel.UserName, Mode= TwoWay}" />
</StackPanel>

<StackPanel Margin="0,6" Orientation="Horizontal">
<TextBlock Text="连接状态:" />
<TextBlock TextWrapping="WrapWholeWords" Text="{x:Bind _vm.SocketStateTxt, Mode = OneWay}"/>
</StackPanel>
<!--配置区End-->
</StackPanel>
</Grid>[/mw_shl_code]


预览如下:
***附件停止解析***

UI很简单,输入服务端配置信息以及自己的昵称即可,xaml中使用的是最新的x:Bind绑定,至于好处我就不赘述了……

Pages/ClientSetting.xaml.cs

[mw_shl_code=csharp,true]public sealed partial class ClientSetting : Page
{
private readonly SettingPageViewModel _vm = ViewModelLocator.Default.SettingPageViewModel;

public ClientSetting()
{
InitializeComponent();
}
}[/mw_shl_code]

后台代码很简单,就只需声明一个VM变量即可。


二、ClientMessage界面



ClientMessage界面更简单,一个ListView来显示消息列表,一个输入框填写消息,一个发送按钮 o(^▽^)o
[mw_shl_code=csharp,true]……
xmlns:toolkit="using:SocketClientSample.Toolkit"
……
<Page.Resources>
<!--消息模板 - 别人发来的消息 Start -->
<DataTemplate x:Key="OtherMsgDataTemplate" x:DataType="models: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>
<!--End-->

<!--消息模板 - 自己发出的消息 Start -->
<DataTemplate x:Key="MyMsgDataTemplate" x:DataType="models: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 Text="{x:Bind Message}" TextWrapping="Wrap"/>
</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>
<!--End-->
</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>

<!--模板选择器 Start-->
<ListView.ItemTemplateSelector>
<toolkit:MsgStyleSelector MyMsgStyle="{StaticResource MyMsgDataTemplate}" OtherMsgStyle="{StaticResource OtherMsgDataTemplate}" />
</ListView.ItemTemplateSelector>
<!--End-->
</ListView>
<Grid Margin="0,8" Grid.Row="1">
<StackPanel>
<TextBox Text="{x:Bind _vm.TextMsg,Mode=TwoWay}" KeyUp="{x:Bind _vm.MsgTextBoxKeyUp}" >
</TextBox>
<Button Margin="0,4"
Content="发送" HorizontalAlignment="Right" Click="{x:Bind _vm.SendMsg}" VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
</Grid>
</Grid>[/mw_shl_code]


运行后是这样子的:
***附件停止解析***


上面的xaml代码中我们用到了项目模板选择器ItemTemplateSelector,用来使自己发送的消息与别人的消息显示为不同的样式,比如上面自己发送的消息是靠右的,而且自己发送的消息也不用显示昵称。

我们来看看这个模板选择器怎么使用的,新建一个Toolkit文件夹,在里面新建一个类文件名叫:MsgStyleSelector.cs编写以下代码:
[mw_shl_code=csharp,true]public class MsgStyleSelector : DataTemplateSelector
{
public DataTemplate OtherMsgStyle { get; set; }

public DataTemplate MyMsgStyle { get; set; }

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var msg = item as MessageModel;
if (msg == null) return OtherMsgStyle;
return msg.User == ViewModelLocator.Default.SettingPageViewModel.UserModel ? MyMsgStyle : OtherMsgStyle;
}
}[/mw_shl_code]

上面我们继承DataTemplateSelector类,然后声明两个DataTemplate属性,一个用来存放 接收到的消息 的样式模板,另一个用来存放自己发送的消息的模板,然后重写父类SelectTemplateCore方法,该方法就是当有新的项被添加到集合并在UI上渲染时,就会通过该方法返回给新项要使用的样式。我们在该方法中判断新项是自己发送的消息还是别人发来的消息然后返回不同的模板就可以了。

界面上使用如下:
[mw_shl_code=csharp,true]xmlns:toolkit="using:SocketClientSample.Toolkit"

<ListView.ItemTemplateSelector>
<toolkit:MsgStyleSelector MyMsgStyle="{StaticResource MyMsgDataTemplate}"
OtherMsgStyle="{StaticResource OtherMsgDataTemplate}" />
</ListView.ItemTemplateSelector>[/mw_shl_code]


分别对MyMsgStyle、OtherMsgStyle属性赋值即可

类似的模板选择器还有其他的,比如:

[list]
[*]GroupStyleSelector:获取或设置对自定义 GroupStyleSelector 逻辑类的引用。GroupStyleSelector 返回其他 GroupStyle 值,以便用于基于内容特征的内容。
[*] ItemContainerStyleSelector:获取或设置对自定义 StyleSelector 逻辑类的引用。StyleSelector 返回不同的 Style 值以用于根据正在显示对象的特征的项容器。
[/list]

大家可以在开发中酌情选择使用

接下来是ClientMessage的后台代码:
[mw_shl_code=csharp,true]public sealed partial class ClientMessage : Page
{
private MessagePageViewModel _vm = ViewModelLocator.Default.MessagePageViewModel;

public ClientMessage()
{
InitializeComponent();
}
private async void SendedMsgAction(MessageModel obj)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// 新消息到达后 滚动到新消息项
MsgListView.ScrollIntoView(obj);
});
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
//注册新消息到达的 Messeng
Messenger.Default.Register<MessageModel>(this, "NewMsgAction", SendedMsgAction);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
//取消注册的消息
Messenger.Default.Unregister(this);
}
}[/mw_shl_code]

后台代码也没啥好说的,主要逻辑都在VM里,这里就仅仅注册了一个新消息到达的 Messeng,当新消息到达后让ListView滚动到新消息那一项,我们在VM中Send出去的这个NewMsgAction大家明白是怎么回事了吧,Messenger的作用就是VM 与 View之间的一个信使,当VM要处理View元素的时候就可以通过Send Messeng 告诉View 。

有些较真的童鞋该说了:"我有强迫症,使用了MvvM我就想把Page的后台逻辑都放到VM中。"然后把所有View上的UI元素都在VM中又映射了一份,各种痛苦。

其实这种想法是不科学的,我们把逻辑放到VM中是为了解耦,是为了项目以后的好维护 、好扩展、好移植,我们要根据具体的情况去判断把哪些逻辑放到VM中,比如上面这个情况,我就认为不应该放到VM中,为什么?因为让滚动条滚动到指定项本来就是View 的本质工作,控制View元素的行为本来就是属于View的职责,何必把它放到VM中呢?假设你真的放到VM中,日后有新的需求,比如不仅仅是滚动到最后一行,还要播放一个View的动画什么的,你还要把View动画元素再做份映射到VM中,假设以后你的项目要迁移到其他平台上,IoT模块上,上面根本就没有什么滚动条(假设)或者滚动条变了,你咋弄?改VM?所以该是谁的事情就让谁去做,至于沟通上的事情就放心的交给Messenger吧…

//为了方便智友查看,附上其它目录地址
***链接停止解析***
***链接停止解析***
***链接停止解析***
***链接停止解析***
***链接停止解析***


本文出自:53078485群大咖Aran

敬告:
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
本站使用Golang构建,点击此处申请开源鄂ICP备18029942号-4联系站长投诉/举报