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

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

新規登録して質問してみよう
ただいま回答率
85.30%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

788閲覧

ListBoxのタッチスクロールについて

Mashimasa

総合スコア51

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2024/05/17 05:20

実現したいこと

ListBoxでタッチ(ドラッグ)スクロールができるUserControlを作成したい。

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

タッチ(ドラッグ)したままListBoxの範囲外にでるとスクロールが反転してしまう。

該当のソースコード

SliderControl.vb

VB.net

1Imports System.ComponentModel 2 3Public Class SliderControl : Implements INotifyPropertyChanged 4 5 6 Public Shared ReadOnly TargetListBoxProperty As DependencyProperty = DependencyProperty.Register("TargetListBox", GetType(ListBox), GetType(SliderControl), New PropertyMetadata(Nothing)) 7 Private WithEvents _ListBox As ListBox 8 Private _Draging As Boolean 9 Private _DragStart As Double 10 Private _StartScrollY As Double 11 Private WithEvents _ScrollViewer As ScrollViewer 12 Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged 13 14 15 Public Property TargetListBox As ListBox 16 Get 17 Return CType(GetValue(TargetListBoxProperty), ListBox) 18 End Get 19 20 Set(value As ListBox) 21 SetValue(TargetListBoxProperty, value) 22 RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("TargetListBox")) 23 End Set 24 End Property 25 26 Private Sub SliderControl_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded 27 _ListBox = TargetListBox 28 _ScrollViewer = GetDescendantsFromVisual(Of ScrollViewer)(_ListBox)(0) 29 End Sub 30 31 32 Private Sub ListBox_PreviewMouseDown(sender As Object, e As MouseButtonEventArgs) Handles _ListBox.PreviewMouseDown 33 _Draging = True 34 SetMousePosition(e.GetPosition(Me)) 35 End Sub 36 37 Private Sub SetMousePosition(p As Point) 38 _DragStart = p.Y 39 _StartScrollY = _ScrollViewer.VerticalOffset 40 End Sub 41 42 Private Sub ListBox_PreviewMouseUp(sender As Object, e As MouseButtonEventArgs) Handles _ListBox.PreviewMouseUp 43 _Draging = False 44 End Sub 45 46 Private Sub OrderListView_PreviewMouseMove(sender As Object, e As MouseEventArgs) Handles _ListBox.PreviewMouseMove 47 If _Draging Then 48 Dim point = e.GetPosition(Me) 49 Dim offset = _DragStart - point.Y 50 Dim current = _StartScrollY + offset 51 Console.WriteLine(current) 52 53 _ScrollViewer.ScrollToVerticalOffset(current) 54 End If 55 End Sub 56 57 Public Shared Function GetDescendantsFromVisual(Of T As {DependencyObject})(obj As DependencyObject) As IEnumerable(Of T) 58 Return Descendants(obj).OfType(Of T) 59 End Function 60 Private Shared Iterator Function Descendants(obj As DependencyObject) As IEnumerable(Of DependencyObject) 61 If obj Is Nothing Then 62 Return 63 End If 64 65 For Each child In Children(obj) 66 Yield child 67 For Each gc In Descendants(child) 68 Yield gc 69 Next 70 Next 71 End Function 72 Private Shared Iterator Function Children(obj As DependencyObject) As IEnumerable(Of DependencyObject) 73 If obj Is Nothing Then 74 Return 75 End If 76 77 Dim count = VisualTreeHelper.GetChildrenCount(obj) 78 If count = 0 Then 79 Return 80 End If 81 82 For i As Integer = 0 To count - 1 83 Dim child = VisualTreeHelper.GetChild(obj, i) 84 If child IsNot Nothing Then 85 Yield child 86 End If 87 Next 88 End Function 89End Class

SliderControl.xaml

WPF

1<UserControl x:Class="SliderControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 mc:Ignorable="d" > 7 8</UserControl>

MainWindow.vb

VB.net

1Class MainWindow 2 Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded 3 Dim l As New List(Of String) 4 For i = 0 To 300 5 l.Add(CStr(i)) 6 Next 7 Listbox.ItemsSource = l 8 9 End Sub 10End Class

MainWindow.xaml

VB.net

1<Window x:Class="MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp39" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <ListBox Name="Listbox" Width="150" Height="300" HorizontalAlignment="Left"></ListBox> 11 <local:SliderControl TargetListBox="{Binding ElementName=Listbox}"/> 12 </Grid> 13</Window>

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

.net4.8 Windows11

TargetListBoxにBindingすると対象のListBoxがタッチスクロールできるというものです。

以下の手順で症状の確認ができます。
・マウスホイールで画面の中央あたりまでスクロールします。
・ListBoxの画面上部をドラッグします。
・そのまま上方向へListBox範囲外になるまでドラッグします。
・ListBoxの範囲外になるとスクロールが反転します。

また上記操作で、ListBox上部の境界線からぎりぎりはみ出たところでドラッグしていると
マウスカーソルを動かしていないのにも関わらず、0.5秒間隔くらいで1行くらい反転してスクロールがされる謎の挙動も発生します。

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

TN8001👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ListBoxでタッチ(ドラッグ)スクロールができるUserControlを作成したい。

マウスドラッグでスマホのような?スクロールがしたいという意味ですよね?
実際のタッチ操作の話ではないんですよね?

タッチ(ドラッグ)したままListBoxの範囲外にでるとスクロールが反転してしまう。
マウスカーソルを動かしていないのにも関わらず、0.5秒間隔くらいで1行くらい反転してスクロールがされる謎の挙動も発生します。

ListBoxは標準で範囲外にドラッグすると、(動かさなくても)自動でスクロールする動作になっています。

↓は素の(何もしていない)ListBoxです。
自動スクロール

この動作と干渉して何かおかしなことになっていると思います。
WPF 横方向にしたListBoxでアイテムを上下にドラッグすると数秒間操作不能になる - スタック・オーバーフロー

ListBox.cs

回避法ですがひとつはScrollViewerのイベントやCaptureMouseをするような方法(単純にやると選択できなくなります)
c# - in WPF. How to scroll Objects in ScrollViewer by mouse-dragging, like as iPhone? - Stack Overflow

あるいはListBoxを継承して、余計な動作を潰す方法でしょうか。

C#タグもあるのでC#です。

xml

1<Window 2 x:Class="Qsfo4uasocdzdqy.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Qsfo4uasocdzdqy" 6 Width="800" 7 Height="450"> 8 <UniformGrid Rows="1"> 9 <local:MyListBox x:Name="listbox1" Margin="50" /> 10 <local:MyListBox x:Name="listbox2" Margin="50" /> 11 <local:MyListBox x:Name="listbox3" Margin="50" /> 12 </UniformGrid> 13</Window>

cs

1using System.Windows; 2using System.Windows.Controls; 3using System.Windows.Controls.Primitives; 4using System.Windows.Input; 5using System.Windows.Media; 6 7namespace Qsfo4uasocdzdqy; 8 9 10public partial class MainWindow : Window 11{ 12 public MainWindow() 13 { 14 InitializeComponent(); 15 16 listbox1.ItemsSource = Enumerable.Range(0, 300); 17 listbox1.SelectedIndex = 150; 18 listbox1.ScrollIntoView(listbox1.SelectedItem); 19 20 listbox2.ItemsSource = Enumerable.Range(0, 30); 21 listbox2.SelectedIndex = 15; 22 listbox2.ScrollIntoView(listbox2.SelectedItem); 23 24 listbox3.ItemsSource = Enumerable.Range(0, 3); 25 } 26} 27 28public class MyListBox : ListBox 29{ 30 private ScrollViewer _scrollViewer; 31 private ScrollBar _scrollBar; 32 private bool _draging; 33 private double _dragStart; 34 private double _startScrollY; 35 36 public override void OnApplyTemplate() 37 { 38 _scrollViewer = GetDescendantsFromVisual<ScrollViewer>(this).FirstOrDefault(); 39 _scrollViewer.ApplyTemplate(); 40 _scrollBar = _scrollViewer.Template.FindName("PART_VerticalScrollBar", _scrollViewer) as ScrollBar; 41 } 42 43 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) 44 { 45 base.OnPreviewMouseLeftButtonDown(e); 46 47 var point = e.GetPosition(this); 48 49 // スクロールバー上はなにもしない 50 if (ActualWidth - _scrollBar.ActualWidth <= point.X) return; 51 52 // スクロールできない(全部見えている)なら何もしない 53 if (_scrollViewer.ComputedVerticalScrollBarVisibility != Visibility.Visible) return; 54 55 _draging = true; 56 _dragStart = point.Y; 57 _startScrollY = _scrollViewer.VerticalOffset; 58 Cursor = Cursors.ScrollNS; 59 } 60 61 protected override void OnMouseMove(MouseEventArgs e) 62 { 63 // 範囲外へのドラッグでスクロールする動作を抑制 64 //base.OnMouseMove(e); 65 66 if (_draging) 67 { 68 var point = e.GetPosition(this); 69 var offset = _dragStart - point.Y; 70 var current = _startScrollY + offset; 71 72 _scrollViewer.ScrollToVerticalOffset(current); 73 } 74 } 75 76 protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) 77 { 78 base.OnPreviewMouseLeftButtonUp(e); 79 80 _draging = false; 81 Cursor = null; 82 } 83 84 protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e) 85 { 86 // 範囲外へのドラッグでスクロールする動作を抑制(タイマーを呼ばれないように) 87 //base.OnIsMouseCapturedChanged(e); 88 } 89 90 91 private static IEnumerable<T> GetDescendantsFromVisual<T>(DependencyObject obj) where T : DependencyObject => Descendants(obj).OfType<T>(); 92 private static IEnumerable<DependencyObject> Descendants(DependencyObject obj) 93 { 94 if (obj == null) yield break; 95 foreach (var child in Children(obj)) 96 { 97 yield return child; 98 foreach (var gc in Descendants(child)) yield return gc; 99 } 100 } 101 private static IEnumerable<DependencyObject> Children(DependencyObject obj) 102 { 103 if (obj == null) yield break; 104 var count = VisualTreeHelper.GetChildrenCount(obj); 105 for (var i = 0; i < count; i++) 106 { 107 var child = VisualTreeHelper.GetChild(obj, i); 108 if (child != null) yield return child; 109 } 110 } 111}

アプリ動画
アイテム数が少ないとちょっと操作が繊細になりますね(お好みに調整してください)


本題とは関係ないが気になった点

依存関係プロパティのCLRプロパティラッパーに、GetValueSetValue以外の処理を書かないでください。
ラッパーはユーザー(このコードを利用する開発者)の利便性のために用意するもので、バインド時には通りません(なくてもバインドできます)

vb

1Public Property TargetListBox As ListBox 2 Get 3 Return CType(GetValue(TargetListBoxProperty), ListBox) 4 End Get 5 6 Set(value As ListBox) 7 SetValue(TargetListBoxProperty, value) 8 'RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("TargetListBox")) 9 End Set 10End Property

wpf - Why are .NET property wrappers bypassed at runtime when setting dependency properties in XAML? - Stack Overflow

カスタム依存関係プロパティ - WPF .NET | Microsoft Learn

INotifyPropertyChangedを実装する意図も図りかねます。
依存関係プロパティの変更を感知したい場合は、「プロパティ変更コールバック」を使用してください。
依存関係プロパティのコールバックと検証 - WPF .NET | Microsoft Learn
PropertyChangedCallback Delegate (System.Windows) | Microsoft Learn

投稿2024/05/17 11:57

TN8001

総合スコア10093

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2024/05/17 11:58

C#が全然わからないということであればコメントしてください。 がんばってVBにします^^;
Mashimasa

2024/05/19 23:12 編集

ListBoxのIsMouseCapturedChangedイベントでReleaseMouseCaptureすることにより解決しました。 今回はListBoxの中身を選択する必要はありませんでした。 ありがとうございます。 サンプルコードも無駄がなく参考になります...。 いつもありがとうございます。 ・依存関係プロパティのCLRプロパティラッパーに、GetValue・SetValue以外の処理を書かないでください。  →承知しました。 ・INotifyPropertyChangedを実装する意図も図りかねます。  →こちらはコードの消し忘れでした。   実際にはほかにもいろいろ機能があり、そこから余計なものを削除したつもりでした。
TN8001

2024/05/20 10:01

> ListBoxのIsMouseCapturedChangedイベントでReleaseMouseCaptureすることにより解決しました。 範囲外に行ったときはスクロール止まっていいんですね^^; その場合範囲外でドラッグをやめた時ステートが食い違う(MouseUpがこないので_dragging = trueのままになる)ので、 private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Released) { _dragging = false; return; } のような処理を入れておいたほうがいいでしょう。 > 今回はListBoxの中身を選択する必要はありませんでした。 説明不足でしたが選択できなくなるのは、ドラッグ開始(PreviewMouseDown)時にScrollViewerでCaptureMouseする実装(結果的にListBoxはReleaseMouseCaptureされる)を試した場合の話です(全部_scrollViewer.PreviewMouseDown等に代わるイメージ) --- (もう結構作りこんでおられるようなのでいいっちゃいいんですが)バインドするためだけに実体のないUserControlを作るのはかなり違和感があります。 既存のコントロールの動作をカスタムする場合は、継承コントロール(WPFでこういう言い方あまりしないけど回答のようなスタイルを上書きしないカスタムコントロールのこと)か、ビヘイビアが一般的かと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問