質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.19%

[WPF] 複数のListViewのスクロールバーを同期させたい。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 4,334

paul

score 21

前提・実現したいこと

WPFアプリケーションを作成しています。
二つのListViewを持つアプリケーションですが、二つのListViewのスクロールバーの挙動を同期させたいと考えています。
※片方がスクロールしたらもう片方も同時にスクロールするイメージです。

WPFのScrollViewerやScrollBarのスクロール位置を同期させる 
上記の記事を参考に実装を進めています。

しかし、XAMLの知識が浅いためなのか、うまく同期させられず困っております。

ソース

※Behaviorは、とりあえず上記の記事をそのまま使わせていただいております。

1.下記ソースではスクロール同期はされない。

            <Grid Grid.Column="0">
                <ListView Margin="10,41,5,30" ItemsSource="{Binding ViewA}">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="{x:Type ListViewItem}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDiff}" Value="true">
                                    <Setter Property="Background" Value="#ffc8c8" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.View>
                        <GridView>
                            <GridViewColumn DisplayMemberBinding="{Binding A}" Header="A" />
                            <GridViewColumn DisplayMemberBinding="{Binding B}" Header="B" />
                            <GridViewColumn DisplayMemberBinding="{Binding C}" Header="C" />
                            <GridViewColumn DisplayMemberBinding="{Binding D}" Header="D" />
                            <GridViewColumn DisplayMemberBinding="{Binding E}" Header="E" />
                            <GridViewColumn DisplayMemberBinding="{Binding F}" Header="F" />
                        </GridView>
                    </ListView.View>
                    <i:Interaction.Behaviors>
                        <b:ScrollSyncronizingBehavior ScrollGroup="Group1" Orientation="Vertical" />
                    </i:Interaction.Behaviors>
                </ListView>
            </Grid>

            <Grid Grid.Column="1">
                <ListView Margin="5,41,10,30" ItemsSource="{Binding ViewB}">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="{x:Type ListViewItem}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDiff}" Value="true">
                                    <Setter Property="Background" Value="#ffc8c8" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding IsNone}" Value="true">
                                    <Setter Property="Background" Value="#606060" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.View>
                        <GridView>
                            <GridViewColumn DisplayMemberBinding="{Binding A}" Header="A" />
                            <GridViewColumn DisplayMemberBinding="{Binding B}" Header="B" />
                            <GridViewColumn DisplayMemberBinding="{Binding C}" Header="C" />
                            <GridViewColumn DisplayMemberBinding="{Binding D}" Header="D" />
                            <GridViewColumn DisplayMemberBinding="{Binding E}" Header="E" />
                            <GridViewColumn DisplayMemberBinding="{Binding F}" Header="F" />
                        </GridView>
                    </ListView.View>
                    <i:Interaction.Behaviors>
                        <b:ScrollSyncronizingBehavior ScrollGroup="Group1" Orientation="Vertical" />
                    </i:Interaction.Behaviors>
                </ListView>
            </Grid>

2.ScrollViewerを中で定義してそこにビヘイビアを仕掛けてみるも、XamlParseExceptionが発生

            <Grid Grid.Column="0">
                <ListView Margin="10,41,5,30" ItemsSource="{Binding ViewA}">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="{x:Type ListViewItem}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDiff}" Value="true">
                                    <Setter Property="Background" Value="#ffc8c8" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.View>
                        <GridView>
                            <GridViewColumn DisplayMemberBinding="{Binding A}" Header="A" />
                            <GridViewColumn DisplayMemberBinding="{Binding B}" Header="B" />
                            <GridViewColumn DisplayMemberBinding="{Binding C}" Header="C" />
                            <GridViewColumn DisplayMemberBinding="{Binding D}" Header="D" />
                            <GridViewColumn DisplayMemberBinding="{Binding E}" Header="E" />
                            <GridViewColumn DisplayMemberBinding="{Binding F}" Header="F" />
                        </GridView>
                    </ListView.View>
                <ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
                    <i:Interaction.Behaviors>
                        <b:ScrollSyncronizingBehavior ScrollGroup="Group1" Orientation="Vertical" />
                    </i:Interaction.Behaviors>
                </ScrollViewer>
                </ListView>
            </Grid>

            <Grid Grid.Column="1">
                <ListView Margin="5,41,10,30" ItemsSource="{Binding ViewB}">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="{x:Type ListViewItem}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDiff}" Value="true">
                                    <Setter Property="Background" Value="#ffc8c8" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding IsNone}" Value="true">
                                    <Setter Property="Background" Value="#606060" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.View>
                        <GridView>
                            <GridViewColumn DisplayMemberBinding="{Binding A}" Header="A" />
                            <GridViewColumn DisplayMemberBinding="{Binding B}" Header="B" />
                            <GridViewColumn DisplayMemberBinding="{Binding C}" Header="C" />
                            <GridViewColumn DisplayMemberBinding="{Binding D}" Header="D" />
                            <GridViewColumn DisplayMemberBinding="{Binding E}" Header="E" />
                            <GridViewColumn DisplayMemberBinding="{Binding F}" Header="F" />
                        </GridView>
                    </ListView.View>
                <ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
                    <i:Interaction.Behaviors>
                        <b:ScrollSyncronizingBehavior ScrollGroup="Group1" Orientation="Vertical" />
                    </i:Interaction.Behaviors>
                </ScrollViewer>
                </ListView>
            </Grid>

発生している問題・エラーメッセージ

上記2のエラー

System.Windows.Markup.XamlParseException が発生しました
HResult=0x80131501
Message='型 'System.Windows.Controls.ItemCollection' のコレクションに値を追加しているときに例外がスローされました。' 行番号 '68'、行位置 '7'。
Source=PresentationFramework
スタック トレース:
at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
内部例外 1:
InvalidOperationException: ItemsSource が使用中の場合、操作は無効です。代わりに ItemsControl.ItemsSource を使用して要素にアクセスし、変更してください。

試したこと

テンプレート?を使ってScrollViewerもしくはScrollBarを自分で定義して、その中にBehaviorを仕掛けるみたいなことを考えましたが、実行時に、ItemXamlParseExceptionが発生してしまいます。
ScrollViewerを定義したことで、GridViewColumnの定義自体がなくなってしまっている??のでしょうか。

補足情報(言語/FW/ツール等のバージョンなど)

それぞれのListViewにはGridViewColumnを定義しています。
ViewModelに定義してあるDataViewをItemsSourceにバインドしています。
基本的にMVVMに準拠しつつ作成を進めています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

単純に、「ListViewに中身のItemsSourceがあるのに、ScrollViewerを入れるな」というエラーですね。

ListViewは、ScrollViewerを中に持っているので、

[WPF]ScrollViewerが入れ子のときマウススクロールが効かない問題への対処法

の感じで、ListViewのScrollViewerを無くして、ListViewをScrollViewerを囲むとうまくいくのかな?(やっていない)

もしくは、ビヘイビアの方を、
Accessing the ScrollViewer of a ListBox from C#
これに対応させる形で改造する。

ビヘイビアは何回も使うなら便利だけど、一回しか使わないようなものだと、
大して便利ではないという印象なので、わたしなら、コードビハインドでやってしまうかな。
ビヘイビアは、結局コードビハインドでやっていることをやるので。(結構複雑な形で)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/30 11:57 編集

    回答ありがとうございます。

    おっしゃる通り、確かに「ListViewのScrollViewerを無くして、ListViewをScrollViewerを囲む」と、二つのListViewが同期して動くことを確認しました。
    ありがとうございます。


    理解力不足で申し訳ありませんが、XAMLの理解を深めたいため、質問させてください。
    ・ListViewにScrollViewerを新たに含めようとするとエラーが起こることがわかりましたが、「ListViewに中身のItemsSourceがあるのに、ScrollViewerを入れるな」の理由が未だわかっておりません。
    ・ListView自体が持っているScrollViewerにビヘイビアを効かせることはできますでしょうか。

    また、以下より相談です。
    ListViewがデータの大きさによって現在の表示領域外まで広がるようになりました。
    (ScrollViewerは、中身のコンテンツ(今回でいうとListViewにバインドしているデータ)が外側の枠組みで表示しきれなかった場合、スクロール領域を用意し表示可能にするものですので当たり前といえば当たり前なのですが)ListViewの大きさはデータの行数によって動的に変化し、現在の表示領域以上の大きさになる、その大きいListViewコンテンツ全体をスクロールしてあげる状態になった状態といえます。しかしその状態でスクロールするとカラムヘッダーが隠れてしまいます。
    また、バインドしたデータのカラムが大きかった場合でも、横幅は自動的に広がらないため、今回用意したScrollViewerの水平方向のスクロールバーが現れません。(また、これはビヘイビアの問題な気がしますが、ListView自体のスクロールバーを動かそうと思っても正しいスクロールバーの動作をしません。)

    また、バインドしている最中、非常に動作が重くなります。ListViewのUIのHeightがバインド中常に増加して再描画が発生しているからなのかとにらんでいます。Heightを固定幅にしたら重い動作が改善されました。

    これを以前のListViewの中に存在するScrollViewerのようなものに近づけたく思っております。

    よろしくお願いいたします。

    キャンセル

check解決した方法

0

自己解決しましたので報告いたします。

ListView自体が持っているScrollViewerにビヘイビアを効かせることができました。

        <Style x:Key="ListViewScrollSyncStyle" TargetType="{x:Type ListView}">
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
            <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
            <Setter Property="ScrollViewer.PanningMode" Value="Both" />
            <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListView}">
                        <Themes:ListBoxChrome
                            x:Name="Bd"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            RenderFocused="{TemplateBinding IsKeyboardFocusWithin}"
                            RenderMouseOver="{TemplateBinding IsMouseOver}"
                            SnapsToDevicePixels="true">
                            <ScrollViewer Padding="{TemplateBinding Padding}" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                                <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                <!-- ビヘイビア -->
                                <i:Interaction.Behaviors>
                                    <b:ScrollSyncronizingBehavior Orientation="Vertical" ScrollGroup="Group_V" />
                                    <b:ScrollSyncronizingBehavior Orientation="Horizontal" ScrollGroup="Group_H" />
                                </i:Interaction.Behaviors>
                                <!-- ビヘイビア -->
                            </ScrollViewer>
                        </Themes:ListBoxChrome>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsGrouping" Value="true" />
                                    <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
                                </MultiTrigger.Conditions>
                                <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Styleを定義して、そのStyle内でControlTemplateを定義し、ListViewのスクロールバーにビヘイビアを効かせることができました。

また、水平方向スクロールバーが正しく動作しなかったのは、XAML側(使用する側)のビヘイビアの記述の仕方で水平方向の行が不足していたことが原因でした。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.19%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる