トップ回答者
Silverlightで複数DataGridによるスクロールの同期

質問
-
こんにちは
Silverlightで、複数DataGridを横に並べて配置した場合のスクロールの同期方法がわからず悩んでいます。
(FlexGridでは実現可能な方法があるのは確認しましたが、Silverlightでの実現方法が見つかりませんでした)
http://www.fxug.net/modules/xhnewbb/viewtopic.php?topic_id=3643実現方法がわかる方がいらっしゃいましたら、ご教授をお願いいたします。
ちなみに、環境は、
VisualStudio2010+Silverlight4です。2011年10月26日 9:09
回答
-
以下が参考になるのではないかと思います。
Synchronous scrolling DataGrid in Silverlight
http://reynaerta.wordpress.com/2010/12/29/synchronous-scrolling-datagrid-in-silverlight/
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/2011年10月27日 5:27モデレータ
すべての返信
-
以下が参考になるのではないかと思います。
Synchronous scrolling DataGrid in Silverlight
http://reynaerta.wordpress.com/2010/12/29/synchronous-scrolling-datagrid-in-silverlight/
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/2011年10月27日 5:27モデレータ -
解決済みだと思い、気が付くのが遅くなりました。すみません。
ソースを見てみました。基本的には自分(SynchronousScrollDataGrid)がスクロールした際に、自分(SynchronousScrollDataGrid)のSynchronousScrollingDataGridプロパティに設定したSynchronousScrollDataGridをスクロールさせることによって同期する動作になっています。
であれば、SynchronousScrollingDataGridプロパティを同期させたいSynchronousScrollDataGridの数だけ用意し、それらを全て動かすようにすれば実現できるはずです。
これが汎用的でない場合、同期させたい複数のSynchronousScrollingDataGridをコレクションで渡すことを考えます。XAMLだとちょっと私は思い付かないので、複数のSynchronousScrollingDataGridを含むGridを渡し、コードでその中から複数のSynchronousScrollingDataGridを取り出す方法を思い付きます。また、発想を変えて、イベントで複数のSynchronousScrollingDataGridにスクロールするように通知することも考えられます。この場合、メインのSynchronousScrollingDataGridを一つ決めて、そこに例えばScrollPositionChangedのようなイベントを作成し、各SynchronousScrollingDataGridがそれをリッスンするようにすれば良いでしょう。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/2011年11月9日 4:50モデレータ -
trapemiyaさん
ご回答ありがとうございます。
コレクションで渡す方法とイベントでの通知取得は、やり方がすぐに思いつかなかったので、同期させるDataGrid数分SyncronousScrollingDataGridプロパティを作成し、スクロール動作時(UpdateScrollPositionメソッド)に同期させる他の2つのDataGridのスクロールをさせることで何とかできました。
ただ、この方法だと、DataGridをマウスホイールで移動とDataGridのスクロールバーを直接上下に移動する場合には同期されるのですが、DataGrid上にカーソルがあり、上下キーでレコードを移動した場合のScrollBarの変動が同期できませんでした。
このDataGrid上での上下キーによる移動で発生するScrollbarの変動でも同期をとるにはどうしたらよいのでしょうか?
以下は追加したプロパティと変更したコントロールメソッドおよびXAML変更部分です。
■追加プロパティ(SynchronousScrollDataGrid.cs)■public SynchronousScrollDataGrid SynchronousScrollingDataGrid2 { get { return (SynchronousScrollDataGrid)GetValue(SynchronousScrollingDataGrid2Property); } set { SetValue(SynchronousScrollingDataGrid2Property, value); } } public static readonly DependencyProperty SynchronousScrollingDataGrid2Property = DependencyProperty.Register("SynchronousScrollingDataGrid2", typeof(SynchronousScrollDataGrid), typeof(SynchronousScrollDataGrid), null);
■コントロールメソッド(SynchronousScrollDataGrid.cs)■
private void UpdateScrollPosition(double newValue) { SynchronousScrollDataGrid otherDg = GetValue(SynchronousScrollingDataGridProperty) as SynchronousScrollDataGrid; if (otherDg != null) otherDg.Scroll(ScrollMode.Vertical, newValue); //追加した部分↓ SynchronousScrollDataGrid otherDg2 = GetValue(SynchronousScrollingDataGrid2Property) as SynchronousScrollDataGrid; if (otherDg2 != null) otherDg2.Scroll(ScrollMode.Vertical, newValue); //追加した部分↑ }
■Xaml(MainPage.xaml)■<myControls:SynchronousScrollDataGrid x:Name="firstDataGrid" SynchronousScrollingDataGrid="{Binding ElementName=secondDataGrid}" SynchronousScrollingDataGrid2="{Binding ElementName=thirdDataGrid}" ItemsSource="{Binding ItemsList1}" CanUserSortColumns="False" RowHeight="20" AutoGenerateColumns="False"> : : </myControls:SynchronousScrollDataGrid> <myControls:SynchronousScrollDataGrid x:Name="secondDataGrid" Grid.Column="1" RowHeight="20" CanUserSortColumns="False" SynchronousScrollingDataGrid="{Binding ElementName=firstDataGrid}" SynchronousScrollingDataGrid2="{Binding ElementName=thirdDataGrid}" ItemsSource="{Binding ItemsList2}" AutoGenerateColumns="False"> : : </myControls:SynchronousScrollDataGrid> <myControls:SynchronousScrollDataGrid x:Name="thirdDataGrid" Grid.Column="1" RowHeight="20" CanUserSortColumns="False" SynchronousScrollingDataGrid="{Binding ElementName=firstDataGrid}" SynchronousScrollingDataGrid2="{Binding ElementName=secondDataGrid}" ItemsSource="{Binding ItemsList3}" AutoGenerateColumns="False"> : : </myControls:SynchronousScrollDataGrid>
- 編集済み sugarwing 2011年11月14日 1:23
2011年11月14日 1:20 -
VerticalScrollBarのValueChangedイベントで同期させるぐらいでしょうか。上記で紹介したページのオリジナルのソースを以下のように修正します。いずれもSL_Synchronous_DataGrid_Sampleクラスの変更になります。
//コンストラクタの追加 public SynchronousScrollDataGrid() { forceScroll = false; } /// <summary> /// Get both scrollbars. /// </summary> private void LoadScrollBars() { verticalScrollBar = this.GetTemplateChild("VerticalScrollbar") as ScrollBar; if (verticalScrollBar != null) verticalScrollBar.Scroll += new ScrollEventHandler(OnVerticalScroll); verticalScrollBar = this.GetScrollbar(ScrollMode.Vertical); verticalScrollBar.ValueChanged += new RoutedPropertyChangedEventHandler<double>(verticalScrollBar_ValueChanged); // <---追加 } //verticalScrollBar_ValueChangedを追加 void verticalScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (forceScroll) { forceScroll = false; return; } //UpdateScrollPosition(e.NewValue); if (e.NewValue > e.OldValue) OnVerticalScroll(this, new ScrollEventArgs(ScrollEventType.SmallIncrement, this.GetScrollPosition(ScrollMode.Vertical) + this.RowHeight)); else if (e.NewValue < e.OldValue) OnVerticalScroll(this, new ScrollEventArgs(ScrollEventType.SmallDecrement, this.GetScrollPosition(ScrollMode.Vertical) - this.RowHeight)); } public bool forceScroll { get; set; } // <---追加。UpdateScrollPosition実行後にもValueChangedイベントが発生するのでそれを無視するための制御 /// <summary> /// Updates the scoll position on the other datagrid. /// </summary> /// <param name="newValue"></param> private void UpdateScrollPosition(double newValue) { SynchronousScrollDataGrid otherDg = GetValue(SynchronousScrollingDataGridProperty) as SynchronousScrollDataGrid; if (otherDg != null) { otherDg.forceScroll = true; // <---追加 otherDg.Scroll(ScrollMode.Vertical, newValue); } }
でも、微妙にずれますね。OnMouseWheelのoverrideを真似して書いたんですが、元々マウスホイールだとずれるんですね・・・
もうちょっと精査する必要があると思いますが、とりあえず方針としてはこんな感じかと。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/2011年11月15日 1:55モデレータ -
trapemiyaさん
ご返信および詳細なソースサンプルまで提示していただきありがとうございます。
確かに2行分ずれて連動してしまいますね。
解決方法としてOnSelectedChangedイベントで強制的にforceScrollをFalseにすることでずれることなく連動できました。
ただ、下キーを押しっぱなしの状態でレコードを移動すると連動先のDataGridのスクロールが2倍位のスクロール量で動き、その際に連動元のスクロールバーの最大値が多くなる方向に変化してしまって、連動元と連動先のスクロールバーの最大値が違くなるという現象が発生しました。(スクロールバーの長さが違ってしまう)
(※1レコードずつ下キーを押下していく分には問題ありません)
今回の加工内容としては、verticalScrollBar_ValueChangedを変更して、OnVerticalScroll2というメソッドを用意しました。
また、OnSelectedChangedイベントをOverrideして上下キーで動作させたときにforceScrollを強制的にFalseにしています。//OnSelectedChangedイベントでフラグ強制解除 protected override void OnSelectionChanged(System.Windows.Controls.SelectionChangedEventArgs e) { forceScroll = false; base.OnSelectionChanged(e); } //verticalScrollBar_ValueChangedを追加 void verticalScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (forceScroll) { forceScroll = false; return; } OnVerticalScroll2(this, new ScrollEventArgs(ScrollEventType.SmallIncrement, this.GetScrollPosition(ScrollMode.Vertical))); } //擬似イベントなのでハンドルなし private void OnVerticalScroll2(object sender, ScrollEventArgs e) { if (e.ScrollEventType != ScrollEventType.EndScroll) { UpdateScrollPosition(e.NewValue); } }
2011年11月15日 6:48