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

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

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

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

Q&A

1回答

3182閲覧

[WPF] StackPanelで並んでいるThumbをドラッグで幅を変更したい。

Y...M

総合スコア18

C#

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

0グッド

0クリップ

投稿2020/07/08 07:10

編集2020/07/08 14:14

前提・実現したいこと

StackPanelで並んでいるどのItemをドラッグしてもすべてのItemの幅を変更したい。

以下のXamlのようにItemsControlでStackPanelとThumbを使用しています。
ThumbのドラッグイベントでList内のItemの幅を変更しています。

用途・シナリオとしては、画像を並べて表示し、画像間のマージンをD&Dで変更するようなケースを想定しています。

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

e.HorizontalChangeの移動量が大きく、正しくサイズ変更ができません。
ドラッグに追従して幅を変更する方法はありませんか?

該当のソースコード

Xaml

1<Window x:Class="BlankCoreApp2.Views.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:prism="http://prismlibrary.com/" 5 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 6 prism:ViewModelLocator.AutoWireViewModel="True" 7 Title="{Binding Title}" Height="350" Width="525" > 8 <Grid> 9 <ItemsControl ItemsSource="{Binding List}" Margin="50"> 10 <ItemsControl.ItemsPanel> 11 <ItemsPanelTemplate> 12 <StackPanel Orientation="Horizontal" Background="Green"/> 13 </ItemsPanelTemplate> 14 </ItemsControl.ItemsPanel> 15 <ItemsControl.ItemTemplate> 16 <DataTemplate> 17 <Grid> 18 <Rectangle x:Name="rectangle" 19 Width="{Binding Width}" 20 Height="{Binding Height}" 21 Stroke="Red" 22 StrokeThickness="1" 23 Fill="Yellow"/> 24 <Thumb x:Name="rectangleThumb" 25 DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" 26 Background="Transparent" 27 Width="{Binding Width, ElementName=rectangle}" 28 Height="{Binding Height, ElementName=rectangle}" 29 Opacity="0"> 30 <i:Interaction.Triggers> 31 <i:EventTrigger EventName="DragDelta"> 32 <prism:InvokeCommandAction Command="{Binding DragDeltaCommand}"/> 33 </i:EventTrigger> 34 </i:Interaction.Triggers> 35 </Thumb> 36 </Grid> 37 </DataTemplate> 38 </ItemsControl.ItemTemplate> 39 </ItemsControl> 40 </Grid> 41</Window>

C#

1 public class MainWindowViewModel : BindableBase 2 { 3 private string _title = "Prism Application"; 4 public string Title 5 { 6 get { return _title; } 7 set { SetProperty(ref _title, value); } 8 } 9 public enum Direction 10 { 11 Horizontal, 12 Vertical 13 } 14 ObservableCollection<Item> _list = new ObservableCollection<Item>(); 15 public ObservableCollection<Item> List 16 { 17 get { return _list; } 18 set { SetProperty(ref _list, value); } 19 } 20 public MainWindowViewModel() 21 { 22 for (int i = 0; i < 10; i++) 23 { 24 List.Add(new Item() { Width = 50, Height = 50 }); 25 } 26 } 27 28 private DelegateCommand<DragDeltaEventArgs> _dragDeltaCommand; 29 public DelegateCommand<DragDeltaEventArgs> DragDeltaCommand 30 { 31 get 32 { 33 if (_dragDeltaCommand == null) 34 _dragDeltaCommand = new DelegateCommand<DragDeltaEventArgs>(e => MarginDragDelta(e, Direction.Horizontal)); 35 return _dragDeltaCommand; 36 } 37 } 38 public void MarginDragDelta(DragDeltaEventArgs e, Direction direction) 39 { 40 List.Select(x => x.Width += (int)e.HorizontalChange).ToList(); 41 } 42 } 43 44 public class Item : BindableBase 45 { 46 private int _width = 0; 47 private int _height = 0; 48 49 50 public int Width 51 { 52 get { return _width; } 53 set { SetProperty(ref _width, value); } 54 } 55 56 public int Height 57 { 58 get { return _height; } 59 set { SetProperty(ref _height, value); } 60 } 61 }

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

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

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

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

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

guest

回答1

0

DragDeltaはクリックした位置からの差分なので、毎回足していくとどんどん伸びることになります。
前回のHorizontalChangeを引くか、クリックした時のWidthを覚えておくかになると思います。
そうなるとDragStartedDragCompletedの監視が必要になりそうです。

そもそもとして左端のThumbはいいですが、右のほうのThumbはサイズが変わったことで移動してしまいDragDeltaが大きく暴れてしまいます。
DragDeltaが使えないとなると、Mouse.GetPosition(Application.Current.MainWindow).Xとかになるでしょうが「どうなの?」って気はします。

もうすこし用途というかシナリオが分かればいい方法もありそうな気がしますが、かなり凝ったことをされているようなので説明しづらいでしょうね^^;


検証中はMVVMにこだわらずコードビハインドに書いていいと思います。
希望の動作になったら移せばいいので。


追記
一応それっぽくはなったのですが、非常に泥臭くなってしまいました^^;
これはビヘイビアにするのが正攻法でしょうね(ViewModelでやる処理じゃないですね)

注意

  • Prism.Coreだけ入れました
  • 構造は全部決め打ちです
  • 画像のサイズが同じという前提です(違うならサイズでなくてマージンで調整か?)
  • 縦線や半透明ピンクは確認用なので深い意味はないです
  • Rectangleなのも特に意味はないです
  • 画像も確認用なので特に意味はないです

WidthHeightのバインディングが必要ないなら、ItemsControl自体のドラッグで全てのContentPresenterをリサイズするほうが簡単そうな気もします(位置補正まですると大差ないかもしれない)

xml

1<Window 2 x:Class="Questions276037.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="525" 6 Height="350"> 7 <Window.Resources> 8 <!-- 個々のアイテム --> 9 <DataTemplate x:Key="itemTemplate"> 10 <Grid> 11 <Image 12 x:Name="image" 13 HorizontalAlignment="Center" 14 VerticalAlignment="Center" 15 Source="{Binding Source}" /> 16 <Rectangle 17 Width="{Binding Width}" 18 Height="{Binding Height}" 19 MinWidth="{Binding ActualWidth, ElementName=image}" 20 MinHeight="{Binding ActualHeight, ElementName=image}" 21 Fill="#4CFF0000" 22 MouseDown="Rectangle_MouseDown" 23 MouseMove="Rectangle_MouseMove" 24 MouseUp="Rectangle_MouseUp" /> 25 </Grid> 26 </DataTemplate> 27 28 <!-- 目印用グリッド線 --> 29 <VisualBrush 30 x:Key="lineBrush" 31 TileMode="Tile" 32 Viewbox="0,0,25,25" 33 ViewboxUnits="Absolute" 34 Viewport="0,0,25,25" 35 ViewportUnits="Absolute"> 36 <VisualBrush.Visual> 37 <Path Data="M0,0L0,25" Stroke="Gray" /> 38 </VisualBrush.Visual> 39 </VisualBrush> 40 </Window.Resources> 41 42 <ItemsControl ItemTemplate="{StaticResource itemTemplate}" ItemsSource="{Binding List}"> 43 <ItemsControl.Template> 44 <ControlTemplate> 45 <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 46 <ItemsPresenter /> 47 </ScrollViewer> 48 </ControlTemplate> 49 </ItemsControl.Template> 50 <ItemsControl.ItemsPanel> 51 <ItemsPanelTemplate> 52 <StackPanel Background="{StaticResource lineBrush}" Orientation="Horizontal" /> 53 </ItemsPanelTemplate> 54 </ItemsControl.ItemsPanel> 55 </ItemsControl> 56</Window>

cs

1using System; 2using System.Collections.ObjectModel; 3using System.Windows; 4using System.Windows.Controls; 5using System.Windows.Input; 6using System.Windows.Media; 7using System.Windows.Media.Imaging; 8using System.Windows.Shapes; 9using Prism.Mvvm; 10 11namespace Questions276037 12{ 13 public partial class MainWindow : Window 14 { 15 private readonly BitmapImage i1 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/KnkDDC5A_thumbnail.jpg")); 16 private readonly BitmapImage i2 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u10/105091/d8tKNb7D_thumbnail.jpg")); 17 18 public ObservableCollection<Item> List { get; } = new ObservableCollection<Item>(); 19 20 public MainWindow() 21 { 22 InitializeComponent(); 23 24 for(var i = 0; i < 5; i++) 25 { 26 List.Add(new Item() { Width = 95, Height = 95, Source = i1, }); 27 List.Add(new Item() { Width = 95, Height = 95, Source = i2, }); 28 } 29 DataContext = this; 30 } 31 32 private Point oldPos; 33 private bool dragging; 34 35 private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e) 36 { 37 dragging = true; 38 var rectangle = sender as Rectangle; 39 rectangle.CaptureMouse(); 40 oldPos = Mouse.GetPosition(null); 41 } 42 private void Rectangle_MouseMove(object sender, MouseEventArgs e) 43 { 44 if(!dragging) return; 45 46 var rectangle = sender as Rectangle; 47 var newPos = Mouse.GetPosition(null); 48 var deltaX = newPos.X - oldPos.X; 49 oldPos = newPos; 50 51 if(rectangle.Width + deltaX < rectangle.MinWidth) return; // 画像より小さくしない 52 53 var contentPresenter = rectangle.FindAncestor<ContentPresenter>(); 54 var stackPanel = rectangle.FindAncestor<StackPanel>(); 55 var scrollViewer = rectangle.FindAncestor<ScrollViewer>(); 56 var itemsControl = rectangle.FindAncestor<ItemsControl>(); 57 58 foreach(Item item in itemsControl.Items) 59 item.Width += deltaX; 60 61 var offset = scrollViewer.TranslatePoint(new Point(), stackPanel); // StackPanelがScrollViewerからはみ出た長さ 62 var index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter); 63 var total = (index + 1 - 0.5) * deltaX; // カーソル左側の伸びた分合計 64 65 scrollViewer.ScrollToHorizontalOffset(offset.X + total); // スクロールバー調整 66 } 67 68 private void Rectangle_MouseUp(object sender, MouseButtonEventArgs e) 69 { 70 dragging = false; 71 var rectangle = sender as Rectangle; 72 rectangle.ReleaseMouseCapture(); 73 oldPos = new Point(); 74 } 75 } 76 77 public class Item : BindableBase 78 { 79 private double _width = 0; 80 private double _height = 0; 81 public double Width { get => _width; set => SetProperty(ref _width, value); } 82 public double Height { get => _height; set => SetProperty(ref _height, value); } 83 public BitmapImage Source { get; set; } 84 } 85 86 internal static class DependencyObjectExtensions 87 { 88 public static T FindAncestor<T>(this DependencyObject obj) where T : DependencyObject 89 { 90 do 91 { 92 obj = VisualTreeHelper.GetParent(obj); 93 if(obj == null) return null; 94 } while(!(obj is T)); 95 96 return obj as T; 97 } 98 } 99}

アプリ動画

投稿2020/07/08 11:48

編集2023/07/22 08:48
TN8001

総合スコア9862

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

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

Y...M

2020/07/08 14:12

おっしゃるとおり、右側の問題が一番困っています。 用途・シナリオとしては、画像を並べて表示し、画像間のマージンをD&Dで変更するようなケースです。 もし、良い案があればご教示お願いします。 特にThumbにこだわっている訳ではありません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問