2016-06-08 34 views
0

我有左侧的聊天列表和右侧给定聊天的消息。Windows UWP - 如何以编程方式在ContentTemplate中滚动ListView

我希望MessageList在出现或获取其数据更新时滚动到底部。我怎样才能做到这一点?

我的代码是基于关闭微软的主/详细信息视图示例: https://github.com/Microsoft/Windows-universal-samples/blob/master/Samples/XamlMasterDetail/cs/MasterDetailPage.xaml

的XAML页面:

<Page 
x:Class="MyApp.Pages.ChatsPage" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="using:MyApp.Pages" 
xmlns:data="using:MyApp.Model.Profile" 
xmlns:vm="using:MyApp.ViewModel" 
xmlns:util="using:MyApp.Util" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d"> 

<Page.Transitions> 
    <TransitionCollection> 
     <NavigationThemeTransition /> 
    </TransitionCollection> 
</Page.Transitions> 


<Page.Resources> 

    <util:BoolToVisibilityConverter x:Key="BoolToVisConverter" /> 

    <!--CollectionViewSource x:Name="Chats" 
     Source="{x:Bind ViewModel}"/> 
    <CollectionViewSource x:Name="Chat" 
     Source="{Binding ChatViewModel, Source={StaticResource Chats}}"/> 
    <CollectionViewSource x:Name="Messages" 
     Source="{Binding MessageViewModel, Source={StaticResource Chat}}"/--> 

    <DataTemplate x:Key="MasterListViewItemTemplate" > 
     <Grid Margin="0,11,0,13" BorderBrush="Gray" BorderThickness="2"> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
       <RowDefinition Height="Auto" /> 
      </Grid.RowDefinitions> 

      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*" /> 
       <ColumnDefinition Width="Auto" /> 
      </Grid.ColumnDefinitions> 

      <TextBlock Text="{Binding ChatName}" Style="{ThemeResource ChatListTitleStyle}" /> 

      <TextBlock 
       Text="{Binding LastMessage}" 
       Grid.Row="1" 
       MaxLines="1" 
       Style="{ThemeResource ChatListTextStyle}" /> 
      <TextBlock 
       Text="{Binding LastSender}" 
       Grid.Column="1" 
       Margin="12,1,0,0" 
       Style="{ThemeResource ChatListLastSenderStyle}" /> 
     </Grid> 
    </DataTemplate> 

    <DataTemplate x:Key="DetailContentTemplate"> 

     <ListView x:Name="MessageList" ItemsSource="{Binding Messages}" ScrollViewer.VerticalScrollMode="Auto"> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <StackPanel BorderBrush="Black" BorderThickness="1" Padding="1"> 
         <TextBlock Text="{Binding Message}" Style="{StaticResource NewsfeedTextStyle}"/> 
         <Image Visibility="{Binding Path=IsPhoto, Converter={StaticResource BoolToVisConverter} }" Source="{Binding Photo}" /> 
         <Image Visibility="{Binding Path=IsReaction, Converter={StaticResource BoolToVisConverter} }" Width="200" Height="200" Source="{Binding Reaction}" /> 
         <StackPanel Orientation="Horizontal"> 
          <TextBlock Text="{Binding Sender}" Style="{StaticResource NewsfeedTimestampStyle}" Margin="1"/> 
          <TextBlock Text="{Binding SentTime}" Style="{StaticResource NewsfeedTimestampStyle}" Margin="1"/> 
         </StackPanel> 
        </StackPanel> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
     </ListView> 
    </DataTemplate> 

</Page.Resources> 

<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded"> 
    <VisualStateManager.VisualStateGroups> 
     <VisualStateGroup x:Name="AdaptiveStates" CurrentStateChanged="AdaptiveStates_CurrentStateChanged"> 
      <VisualState x:Name="DefaultState"> 
       <VisualState.StateTriggers> 
        <AdaptiveTrigger MinWindowWidth="720" /> 
       </VisualState.StateTriggers> 
      </VisualState> 

      <VisualState x:Name="NarrowState"> 
       <VisualState.StateTriggers> 
        <AdaptiveTrigger MinWindowWidth="0" /> 
       </VisualState.StateTriggers> 

       <VisualState.Setters> 
        <Setter Target="MasterColumn.Width" Value="*" /> 
        <Setter Target="DetailColumn.Width" Value="0" /> 
        <Setter Target="MasterListView.SelectionMode" Value="None" /> 
       </VisualState.Setters> 
      </VisualState> 
     </VisualStateGroup> 
    </VisualStateManager.VisualStateGroups> 

    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto" /> 
     <RowDefinition Height="*" /> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition x:Name="MasterColumn" Width="320" /> 
     <ColumnDefinition x:Name="DetailColumn" Width="*" /> 
    </Grid.ColumnDefinitions> 

    <TextBlock 
     Text="Chats" 
     Margin="12,8,8,8" 
     Style="{ThemeResource TitleTextBlockStyle}" /> 

    <ListView 
     x:Name="MasterListView" 
     Grid.Row="1" 
     ItemContainerTransitions="{x:Null}" 
     ItemTemplate="{StaticResource MasterListViewItemTemplate}" 
     Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
     IsItemClickEnabled="True" 
     ItemClick="MasterListView_ItemClick"> 
     <ListView.ItemContainerStyle> 
      <Style TargetType="ListViewItem"> 
       <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
      </Style> 
     </ListView.ItemContainerStyle> 
    </ListView> 

    <ContentPresenter 
     x:Name="DetailContentPresenter" 
     Grid.Column="1" 
     Grid.RowSpan="2" 
     BorderThickness="1,0,0,0" 
     Padding="24,0" 
     BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}" 
     Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"    
     ContentTemplate="{StaticResource DetailContentTemplate}"> 
     <ContentPresenter.ContentTransitions> 
      <!-- Empty by default. See MasterListView_ItemClick --> 
      <TransitionCollection /> 
     </ContentPresenter.ContentTransitions> 
    </ContentPresenter> 
</Grid> 

+2

[ListViewBase.MakeVisible](https://msdn.microsoft.com/en-us/library/windows/apps/hh702523.aspx)。 – IInspectable

+0

本例中没有MessageListView请更具体。你的意思是MessageList? –

+0

是的。 MessageList就是我的意思。我会编辑我的问题。 –

回答

1

我认为这是关键的一点,你的ListView是内ContentTemplateContentPresenter

通常我们可以使用ListViewBase.ScrollIntoView(Object) method方法滚动ListView到具体项目,但是当ListViewDataTemplate里面,它是未曝光。下面是一个方法,我们可以用VisualTreeHelper得到这个ListView

public static T FindChildOfType<T>(DependencyObject root) where T : class 
{ 
    var queue = new Queue<DependencyObject>(); 
    queue.Enqueue(root); 
    while (queue.Count > 0) 
    { 
     DependencyObject current = queue.Dequeue(); 
     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++) 
     { 
      var child = VisualTreeHelper.GetChild(current, i); 
      var typedChild = child as T; 
      if (typedChild != null) 
      { 
       return typedChild; 
      } 
      queue.Enqueue(child); 
     } 
    } 
    return null; 
} 

我的例子是这样的:

<Grid.ColumnDefinitions> 
    <ColumnDefinition x:Name="MasterColumn" Width="320" /> 
    <ColumnDefinition x:Name="DetailColumn" Width="*" /> 
</Grid.ColumnDefinitions> 

<ListView x:Name="MasterListView" Grid.Column="0" ItemsSource="{x:Bind ChatList}" SelectionChanged="MasterListView_SelectionChanged"> 
    <ListView.ItemTemplate> 
     <DataTemplate x:DataType="local:ChatEntity"> 
      <TextBlock Text="{x:Bind Member}" /> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 

<ContentPresenter x:Name="DetailContentPresenter" Grid.Column="1" 
        Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"> 
    <ContentPresenter.ContentTemplate> 
     <DataTemplate x:DataType="local:ChatEntity"> 
      <Grid> 
       <Grid.Resources> 
        <DataTemplate x:Key="FromMessageDataTemplate"> 
         <StackPanel Orientation="Horizontal" FlowDirection="LeftToRight"> 
          <TextBlock Text="{Binding Member}" Width="30" Foreground="Blue" FontWeight="Bold" /> 
          <TextBlock Text=":" Width="10" Foreground="Blue" FontWeight="Bold" /> 
          <TextBlock Text="{Binding Content}" Foreground="Red" /> 
         </StackPanel> 
        </DataTemplate> 
        <DataTemplate x:Key="ToMessageDataTemplate"> 
         <StackPanel Orientation="Horizontal" FlowDirection="RightToLeft"> 
          <TextBlock Text="Me" Width="30" HorizontalAlignment="Right" Foreground="Blue" FontWeight="Bold" /> 
          <TextBlock Text=":" Width="10" HorizontalAlignment="Right" Foreground="Blue" FontWeight="Bold" /> 
          <TextBlock Text="{Binding Content}" HorizontalAlignment="Right" Foreground="Green" /> 
         </StackPanel> 
        </DataTemplate> 
        <local:ChatDataTemplateSelector x:Key="ChatDataTemplateSelector" 
           MessageFromTemplate="{StaticResource FromMessageDataTemplate}" 
           MessageToTemplate="{StaticResource ToMessageDataTemplate}" /> 
       </Grid.Resources> 
       <ListView ItemsSource="{x:Bind MessageList}" ItemTemplateSelector="{StaticResource ChatDataTemplateSelector}"> 
        <ListView.ItemContainerStyle> 
         <Style TargetType="ListViewItem"> 
          <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
         </Style> 
        </ListView.ItemContainerStyle> 
       </ListView> 
      </Grid> 
     </DataTemplate> 
    </ContentPresenter.ContentTemplate> 
</ContentPresenter> 

ChatEntity类和MessageEntity类是这样的:

public class ChatEntity 
{ 
    public string Member { get; set; } 
    public ObservableCollection<MessageEntity> MessageList { get; set; } 
} 

public class MessageEntity 
{ 
    public enum MsgType 
    { 
     From, 
     To 
    } 

    public string Member { get; set; } 
    public string Content { get; set; } 
    public MsgType MessageType { get; set; } 
} 

和我的ChatDataTemplateSelector是这样的:

public class ChatDataTemplateSelector : DataTemplateSelector 
{ 
    public DataTemplate MessageFromTemplate { get; set; } 
    public DataTemplate MessageToTemplate { get; set; } 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) 
    { 
     MessageEntity msg = item as MessageEntity; 
     if (msg != null) 
     { 
      if (msg.MessageType == MessageEntity.MsgType.From) 
       return MessageFromTemplate; 
      else 
       return MessageToTemplate; 
     } 
     return null; 
    } 
} 

首先,我在左ListView加载ChatList,并在SelectionChanged事件左ListView的,我装了MessageList,这将确保MessageList进行更新。这就像每次选择左边的ListView中的项目时手动刷新ObservableCollectionMessageList)右边的ListView。但是,您也可以在其他时间将数据添加到MessageList,并在有新消息时向其添加数据。 ObservableCollection可以自动刷新。这里是我的代码:

private ObservableCollection<MessageEntity> messageList; 
private ObservableCollection<ChatEntity> ChatList; 

public MainPage() 
{ 
    this.InitializeComponent(); 
    messageList = new ObservableCollection<MessageEntity>(); 
    ChatList = new ObservableCollection<ChatEntity>(); 
} 

protected override void OnNavigatedTo(NavigationEventArgs e) 
{ 
    ChatList.Add(new ChatEntity { Member = "Tom", MessageList = messageList }); 
    ChatList.Add(new ChatEntity { Member = "Peter" }); 
    ChatList.Add(new ChatEntity { Member = "Clark" }); 
} 

private void MasterListView_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    messageList.Clear(); 
    for (int i = 0; i < 100; i++) 
    { 
     if (i % 2 == 0) 
      messageList.Add(new MessageEntity { Member = "Tom", Content = "Hello!", MessageType = MessageEntity.MsgType.From }); 
     else 
      messageList.Add(new MessageEntity { Content = "World!", MessageType = MessageEntity.MsgType.To }); 
    } 
    var listView = FindChildOfType<ListView>(DetailContentPresenter); 
    listView.ScrollIntoView(messageList.Last()); 
} 

我的样品中的数据都是假的。该示例看起来有点复杂,但实际上很简单,只需使用VisualTreeHelper即可找到ListView并使用其方法滚动到最后一项。

+0

谢谢你提供非常详细和有用的答案。 –

相关问题