BringIntoView on a ListBoxItem with an attached pr

2019-08-30 01:11发布

I'm trying to sync scrolling of my ListBox using BringIntoView on an attached property.

My attempt is not working. When I run my code the selected items are synced across the list boxes but if I select an item that is not displayed on the other ListBox it does not automatically scroll.

I have a simple ViewModel with IsShown and ItemText properties.

    <Window.Resources>
    <Style TargetType="ListBoxItem">
        <Setter Property="IsSelected" Value="{Binding IsShown, Mode=TwoWay}"/>
        <Setter Property="local:CustomProperties.BringIntoView" Value="{Binding IsShown}"/>
    </Style>
    <DataTemplate x:Key="DataTemplate1">
        <TextBlock Name="_txt" Text="{Binding ItemText}"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="255*"/>
        <ColumnDefinition Width="262*"/>
    </Grid.ColumnDefinitions>
    <ListBox ItemsSource="{Binding ItemList}" ItemTemplate="{StaticResource DataTemplate1}" />  
    <ListBox Grid.Column="1" ItemsSource="{Binding ItemList}" ItemTemplate="{StaticResource DataTemplate1}" >
    </ListBox>                
</Grid>

Here is my dependency property

public static class CustomProperties
{
    public static readonly DependencyProperty BringIntoViewProperty =
      DependencyProperty.RegisterAttached( "BringIntoView",
      typeof( bool ),
      typeof( CustomProperties ),
      new PropertyMetadata( OnBringIntoViewChanged ) );

    public static void SetBringIntoView ( DependencyObject o, bool value )
    {
        o.SetValue( BringIntoViewProperty, value );
    }

    public static bool GetBringIntoView ( DependencyObject o )
    {

        return ( bool )o.GetValue( BringIntoViewProperty );
    }

    private static void OnBringIntoViewChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        if ( ( bool )e.NewValue )
        {
           if ( d is FrameworkElement )
                ( ( FrameworkElement )d ).BringIntoView();
        }
    }
}

标签: wpf mvvm
1条回答
你好瞎i
2楼-- · 2019-08-30 01:37

How about a different approach?

You want the selected item to always be brought into view. If you use a ListCollectionView in your ViewModel, you can change selection by calling MoveCurrentTo() and other functions. Your ListBox would bind to this ListCollectionView, and by default will sync the selected item using the IsSynchronizedWithCurrentItem property.

In this way, your ViewModel is keeping track of the current item and your ListBox is binding to it.

Now, if you want the behavior of your ListBox to include scrolling to keep the selected item in view, add the following code to your view code-behind:

private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    listBox.ScrollIntoView(listBox.SelectedItem);
}

Your ViewModel probably doesn't need to know this detail. Auto-scrolling is just part of your view's behavior.

It's probably wrong to assume everything needs to be in your ViewModel. For example, we don't care about caret positions in text boxes, for example. I think this qualifies as something view-specific.

查看更多
登录 后发表回答