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

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

新規登録して質問してみよう
ただいま回答率
85.31%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

WPF

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

Q&A

解決済

3回答

939閲覧

WPF MainWindowとSubWindowで別のViewModel?を使いたい

nodoita

総合スコア7

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

WPF

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

0グッド

0クリップ

投稿2023/07/04 02:35

編集2023/07/06 05:31

お世話になります。
調べたのですが、分からないため教えてください。

実現したいこと

以前質問させていただいた2つを組み合わせたものにしようとしています。

Rectangleの質問【1】のデータを主軸とし、
画像選択サブウィンドウのデータ【2】を追加しようとしています。

組み合わせても問題なく動作するようにしたいです。

【1】
WPF 動的生成したRectangleがドラッグで移動できない、Bindingが難しい…
https://teratail.com/questions/j700zzpncph60k
→ソースが変わる前のものを利用しています。

【2】
WPF C# ImageSourceを利用しているからか、画像がロックされてしまう
https://teratail.com/questions/t6uox4f1iujybm

試したこと

ViewModelを1つにまとめました。
Itemsという同じ名称のものは、「RectItems」「PicItems」としました。

MainWindow.xaml

1 <Grid> 2 <Grid.RowDefinitions> 3 <RowDefinition Height="30" /> 4 <RowDefinition Height="600"/> 5 <RowDefinition Height="*"/> 6 </Grid.RowDefinitions> 7 <ToolBar Grid.Row="0" VerticalAlignment="Top" Height="30"> 8 <Button Content="画像選択" Width="128" Height="32" Click="Button_Click"/> 9 <RadioButton x:Name="DefaultButton" Width="32" Height="32" 10 Content="?" IsChecked="True" /> 11 <RadioButton x:Name="RectButton" Width="32" Height="32"> 12 <Rectangle Width="24" Height="16" Stroke="Black"/> 13 </RadioButton> 14 </ToolBar> 15 16 <!-- <Image Grid.Row="1" MinWidth="600" MinHeight="600" Height="600" MaxWidth="800" MaxHeight="600" Width="800" Source="{Binding SelectedPic.FilePath, Converter={StaticResource ImageConverter}}" StretchDirection="DownOnly" HorizontalAlignment="Left" VerticalAlignment="Top" />--> 17 <local:MyCanvas MinHeight="600" MaxHeight="600" Height="600" MinWidth="800" Width="800" MaxWidth="800" Grid.Row="1" RectItems="{Binding RectInfoCollection}" SelectRect="{Binding SelectRect, Mode=TwoWay}" > 18 </local:MyCanvas> 19 <local:MyDataGrid Grid.Row="2" DataContext="{Binding }"/> 20 21 </Grid>

MainWindow.cs

1 public partial class MainWindow : Window 2 { 3 public ViewModel vm ; 4 public MainWindow() 5 { 6 InitializeComponent(); 7 var imageFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "image"); 8 vm = new ViewModel(imageFolder); 9 this.DataContext = vm; 10 } 11 private void Button_Click(object sender, RoutedEventArgs e) 12 => new SubWindow((ViewModel)DataContext).ShowDialog(); 13 14 private void Window_Loaded(object sender, RoutedEventArgs e) 15 { 16 vm.RectInfoCollection.Add(new RectInfo("1", 10, 10, 100, 100, "Red", "")); 17 vm.RectInfoCollection.Add(new RectInfo("2", 100, 100, 200, 200, "Blue", "")); 18 19 } 20} 21

ViewModel.cs

1public partial class ViewModel : BindableBase 2{ 3 private RectInfo? _SelectedRect; 4 public RectInfo? SelectedRect { get => _SelectedRect; set => SetProperty(ref _SelectedRect, value); } 5 6 public ObservableCollection<Item> PicItems { get; } = new(); 7 8 private Item? _SelectPic; 9 public Item? SelectPic { get => _SelectPic; set => SetProperty(ref _SelectPic, value); } 10 11 private readonly string folder; 12 public ViewModel(string folder) 13 { 14 this.folder = folder; 15 Directory.CreateDirectory(folder); 16 ImportFiles(Directory.EnumerateFiles(folder, "*.jpg", SearchOption.TopDirectoryOnly)); 17 } 18 19 [RelayCommand] 20 private void Apply(Item? item) => SelectPic = item; 21 22 public void ImportFiles(IEnumerable<string> paths) 23 { 24 foreach (var path in paths) PicItems.Add(new(path)); 25 } 26 27 public void AddItem(string path, Func<bool> isOverwrite) 28 { 29 var fileName = System.IO.Path.GetFileNameWithoutExtension(path); 30 var outPath = System.IO.Path.Combine(folder, $"{fileName}.jpg"); 31 32 if (File.Exists(outPath)) 33 { 34 if (!isOverwrite()) return; 35 PicItems.Remove(PicItems.FirstOrDefault(x => x.FilePath == outPath)!); 36 } 37 38 using (var fsin = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) 39 using (var fsout = new FileStream(outPath, FileMode.Create, FileAccess.Write)) 40 { 41 var f = BitmapFrame.Create(fsin, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnDemand); 42 var meta = f.Metadata.Clone() as BitmapMetadata ?? new BitmapMetadata("jpg"); 43 var enc = new JpegBitmapEncoder(); 44 enc.Frames.Add(BitmapFrame.Create(f, f.Thumbnail, meta, f.ColorContexts)); 45 enc.Save(fsout); 46 } 47 48 PicItems.Add(new(outPath)); 49 } 50}

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

  • Rectangleが表示されない(SelectRectもRe
  • Canvasの裏に画像を表示させようとImageを追加したが、それも表示されない(SelectPicのデータはあり)

MyCanvas.cs

1 public MyCanvas() 2 { 3 InitializeComponent(); 4 } 5 6 private System.Windows.Point _dragOffset; 7 private System.Windows.Shapes.Rectangle? _dragRectangle; 8 public RectInfo SelectRect 9 { 10 get { return (RectInfo)GetValue(SelectRectProperty); } 11 set { SetValue(SelectRectProperty, value); } 12 } 13 14 public static readonly DependencyProperty SelectRectProperty = 15 DependencyProperty.Register("SelectRect", typeof(RectInfo), typeof(MyCanvas), new PropertyMetadata(null, SelectRect_Changed)); 16 17 private static void SelectRect_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) 18 { 19 (d as MyCanvas)?.SelectRectChanged(e.OldValue, e.NewValue); 20 } 21 22 private void SelectRectChanged(object oldValue, object newValue) 23 { 24 if (oldValue is RectInfo o) 25 { 26 var target = FindRectangle(o); 27 if (target != null) 28 { 29 target.StrokeDashArray = null; 30 } 31 } 32 if (newValue is RectInfo n) 33 { 34 var target = FindRectangle(n); 35 if (target != null) 36 { 37 target.StrokeDashArray = new DoubleCollection() { 2 }; 38 } 39 } 40 } 41 42 public ObservableCollection<RectInfo> RectItems 43 { 44 get { return (ObservableCollection<RectInfo>)GetValue(RectItemsProperty); } 45 set { SetValue(RectItemsProperty, value); } 46 } 47 48 public static readonly DependencyProperty RectItemsProperty = 49 DependencyProperty.Register("RectItems", typeof(ObservableCollection<RectInfo>), typeof(MyCanvas), new PropertyMetadata(null, RectItemsChangedCallback)); 50 51 private static void RectItemsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 52 { 53 (d as MyCanvas)?.RegisterCollectionChanged(); 54 } 55 56 private void RegisterCollectionChanged() 57 { 58 RectItems.CollectionChanged += RectItems_CollectionChanged; 59 } 60 61 private void RectItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) 62 { 63 switch (e.Action) 64 { 65 case NotifyCollectionChangedAction.Add: 66 if (e.NewItems != null) 67 { 68 foreach (RectInfo mp in e.NewItems) 69 { 70 AddRectangle(mp); 71 mp.PropertyChanged += RectInfo_PropertyChanged; 72 } 73 } 74 break; 75 76 case NotifyCollectionChangedAction.Remove: 77 if (e.OldItems != null) 78 { 79 foreach (RectInfo info in e.OldItems) 80 { 81 var target = FindRectangle(info); 82 if (target != null) 83 { 84 canvas2.Children.Remove(target); 85 info.PropertyChanged -= RectInfo_PropertyChanged; 86 } 87 } 88 } 89 break; 90 91 default: 92 break; 93 } 94 } 95 96 private System.Windows.Shapes.Rectangle? FindRectangle(RectInfo info) 97 { 98 if (info == null) return null; 99 return canvas2.Children.OfType<System.Windows.Shapes.Rectangle>().FirstOrDefault(i => info == (RectInfo)i.Tag); 100 } 101 102 internal void AddRectangle(RectInfo info) 103 { 104 var r = new System.Windows.Shapes.Rectangle 105 { 106 Width = 20, 107 Height = 20, 108 Stroke=Brushes.Black, 109 StrokeThickness=5, 110 ToolTip = info.Name, 111 Tag = info 112 }; 113 114 r.Width = info.Width; 115 r.Height = info.Height; 116 117 try 118 { 119 var brush = (SolidColorBrush)new BrushConverter().ConvertFromString(info.BorderColor); 120 r.Stroke = brush; 121 } 122 catch 123 { 124 r.Stroke = Brushes.Transparent; 125 } 126 127 try { 128 var brush = (SolidColorBrush)new BrushConverter().ConvertFromString(info.FillColor); 129 r.Fill = brush; 130 } 131 catch 132 { 133 r.Fill = Brushes.Transparent; 134 } 135 136 //イベント追加 137 r.MouseDown += DragStart; 138 r.MouseUp += (o, e) => { _dragRectangle = null; r.ReleaseMouseCapture(); }; 139 r.MouseMove += MoveRectangle; 140 141 //パネルに追加 142 canvas2.Children.Add(r); 143 Canvas.SetLeft(r, info.X); 144 Canvas.SetTop(r, info.Y); 145 }

###分かったこと
画面上にボタンを置き
「vm.RectInfoCollection.Add(new RectInfo("3", 10, 10, 100, 100, "Red", ""));」としてみたところ、四角形が描画されました。
起動時に作成したデータでは四角形が出来ず、DataGridのみに表示されますが、ボタンで実行すると四角形描画とDataGridへの追加もどちらもできるようです。

画面上に置いたボタンで追加できるのであれば、
Binding自体は問題ない気がします。

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

Visualstudio2022 V17.6.3
.NET 6.0 C#

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

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

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

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

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

hqf00342

2023/07/06 05:15

SubWindow.xaml.csの1行目PicModel変数はViewModelクラスで合ってますか? PicModelというクラスも作られているようなので入り混じっているように見えます。 基本はTN8001さんの回答の通りと思います。 私のサンプルを利用しているようなので補足すると、 元サンプルはDataBindingとUIドラッグのサンプルなので、簡便にするためViewModel内で直接データ追加しています。(これが良くなかったか・・・) 本来はVMでデータ管理するのではなく、データ管理するモデルクラスを作り、VMはそれを参照しましょう。 2つのViewが同じデータを参照したいのならばそれぞれのViewModelからそのモデルを参照します。 サンプルも更新しました。 ただ、最初はMVVMにこだわりすぎず、やりたいことを実現したほうが良いと思います。
nodoita

2023/07/06 05:28

hqf00342様 回答ありがとうございます。PicModelは削除しました! 全てをViewModelに統合しました。 サンプル更新ありがとうございます。 改変を重ねていて今の途中段階をどう移植したら良いのか結構大変そうです。 やり方が悪かったのだとは思いますが、1つだけ分かりました。
guest

回答3

0

ベストアンサー

質問内容を変えないほうが良いです。
最初の質問に対してはTN8001さんの回答が適切です。

Rectangleが表示されない(SelectRectもRe

データの初期化タイミングmyCanvas::RectItemsChangedCallback()での初期化が足りていないことによると思われます。このメソッドはRectItemsが最初に代入されたときに呼ばれる依存関係プロパティの機能で、その際にデータが入っていたら、それを反映させるべきです。
(最初のサンプルはここに来るときデータが空である事が自明であったため省略してました。)
以下を参照してください。

cs

1 private static void ItemsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 2 { 3 if (d is MyCanvas mycanvas) 4 { 5 // Itemsプロパティに変更通知を登録 6 mycanvas.Items.CollectionChanged += mycanvas.Items_CollectionChanged; 7 8 //ItemsのデータをCanvasに初期登録する 9 if (e.NewValue is IEnumerable<RectInfo> infos) 10 { 11 foreach (var info in infos) 12 { 13 mycanvas.AddRectangle(info); 14 info.PropertyChanged += mycanvas.RectInfo_PropertyChanged; 15 } 16 } 17 } 18 }

この部分はListBoxやDataGridのItemsSource機能をCanvasを使って再発明しているような状態です。
どちらが簡単かはよく考えて使ってみてください。

Canvasの裏に画像を表示させようとImageを追加したが、それも表示されない(SelectPicのデータはあり)

「裏」がよくわかってないですがMyCanvas 内のCanvasコントロールの上にImage コントロールを貼る、もしくはCanvasのBackgroundを画像から生成したブラシで塗れば実現できると思います。
文字通り裏なのであればCanvasが非透明だと見えないだけのようにも思えます。

投稿2023/07/06 11:33

編集2023/07/11 04:07
hqf00342

総合スコア394

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

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

nodoita

2023/07/07 04:28

回答ありがとうございます! 新しいソースを利用し1から作成し直したらMyDataあたりがとても分かりやすくなってました! 自分で作り変えていた部分を確認しつつ修正していこうと思います。 ListBoxの動作(選択したら薄く枠が色づく等)が私の想定と違ったため Canvasの方を利用させて頂いてます! MyCanvasを透過モードに設定し、後ろ?にImageをとりあえずあるもので貼り付けてみました。無事画像は表示されたのですが、Rectが後ろに隠れてしまうようになりました。 以前のソースの時は、 Panel.ZIndex="255"とRectのアイテムが最前面に来るようにしていたのですが、この場合どのようにしたら良いのでしょうか? <local:MyCanvas Items="{Binding RectInfoCollection}" SelectedItem="{Binding ElementName=datagrid,Path=SelectedItem, Mode=TwoWay}" > <Image Source="img\test.jpg" StretchDirection="DownOnly" HorizontalAlignment="Left" VerticalAlignment="Top"></Image> </local:MyCanvas>
hqf00342

2023/07/07 06:18 編集

そのXAMLだと<MyCanvas>の上に<Image>がある状態です。重なり方はおよそ以下のイメージです(デバッグ中にVisualTreeで確認できます)。 ↑上 <Image> <MyCanvas内Rectangle>×n個 <MyCanvas内Canvas> <MyCanvas内UserControl> <MyCanvas> <Grid?> <Window> ↓下 透明化ができているなら<Image>を<MyCanvas>の前に書くのが早いはずです。 他にもMyCanvas.xaml内の<Canvas>内に画像を配置しRectangleの真下にする、や Canvasの背景色として画像を指定(以下)等、いろいろ方法はあるので試してください <Canvas x:Name="canvas2"> <Canvas.Background> <ImageBrush ImageSource="....jpg"/> </Canvas.Background> </Canvas>
nodoita

2023/07/07 07:16

Canvasの前に持ってきたら問題なく表示できるようになりました!ありがとうございます!
guest

0

上記のようにしたら参照できるかと思ったらNGでした。

これ自体の表記はあっています。

"PicModel" は名前空間 "clr-namespace:wpfRectangleBindingTest.ViewModels" に存在しません。とエラーになります。

PicModel追加後にビルドをしていないため、デザイナが認識できていません(SubWindow.xaml.csにエラーがあるため、現状ビルドも通らないはずです)

そもそもですがd:DataContextはデザイナ上でインテリセンスを利かすためのもので、実行時には何の効果もありません。
デザイン サーフェイス上のサンプル データとプロトタイプを作るためのサンプル データ - UWP applications | Microsoft Learn


このPicModelは、MainWindowでもSubWindowでも利用します。
元々はItemsだったのですが、Rectangleのものと被るために
新しくPicModelとして作成して参照させようとしてます。

ゴールがさっぱり見えません。
RectInfoCollectionPicItemsは同じものを指しているんですか?(画像はすべて対応する四角がある)
だったらRectInfoItemをマージして、ひとつのコレクションにしてください。

RectInfoCollectionPicItemsは違うものを指しているんですか?(画像は選択肢であって四角と1対1ではない)
だったらViewModelPicModelをマージして、ひとつのViewModelにしてください。

投稿2023/07/04 10:10

TN8001

総合スコア10022

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

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

nodoita

2023/07/06 02:22

RectInfoCollectionは、Rectの情報のみが集まったものです。 PicItemsは、画像の情報データが集まったものです。 public partial class ViewModel : BindableBase { /// <summary> /// Rectangleの位置情報コレクション /// </summary> public ObservableCollection<RectInfo> RectInfoCollection { get; set; } = new(); /// <summary> /// 選択中のアイテム /// Mainwindow.xamlでMyCanvasにBindingされている /// </summary> private RectInfo? _SelectedRect; public RectInfo? SelectedRect { get => _SelectedRect; set => SetProperty(ref _SelectedRect, value); } public ObservableCollection<Item> PicItems { get; } = new(); private Item? _SelectPic; public Item? SelectPic { get => _SelectPic; set => SetProperty(ref _SelectPic, value); } private readonly string folder; public ViewModel(string folder) { this.folder = folder; Directory.CreateDirectory(folder); ImportFiles(Directory.EnumerateFiles(folder, "*.jpg", SearchOption.TopDirectoryOnly)); } [RelayCommand] private void Delete(Item item) { if (MessageBox.Show(item.FileTitle + "を削除しても良いですか?", "ファイル削除", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.No) { return; } PicItems.Remove(item); File.Delete(item.FilePath); } [RelayCommand] private void Apply(Item? item) => SelectPic = item; public void ImportFiles(IEnumerable<string> paths) { foreach (var path in paths) PicItems.Add(new(path)); } public void AddItem(string path, Func<bool> isOverwrite) { var fileName = System.IO.Path.GetFileNameWithoutExtension(path); var outPath = System.IO.Path.Combine(folder, $"{fileName}.jpg"); if (File.Exists(outPath)) { if (!isOverwrite()) return; PicItems.Remove(PicItems.FirstOrDefault(x => x.FilePath == outPath)!); } using (var fsin = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) using (var fsout = new FileStream(outPath, FileMode.Create, FileAccess.Write)) { var f = BitmapFrame.Create(fsin, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnDemand); var meta = f.Metadata.Clone() as BitmapMetadata ?? new BitmapMetadata("jpg"); var enc = new JpegBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(f, f.Thumbnail, meta, f.ColorContexts)); enc.Save(fsout); } PicItems.Add(new(outPath)); } } ViewModelを統合して利用することにしました! Itemsは同じ名称だと困るので、RectのものはRectItems、PicのものはPicItemsとしています。
TN8001

2023/07/06 11:59

> RectInfoCollectionは、Rectの情報のみが集まったものです。 > PicItemsは、画像の情報データが集まったものです。 「画像は選択肢であって四角と1対1ではない」ということですね。 > Canvasの裏に画像を表示させようとImageを追加したが、それも表示されない 四角に画像を入れるのではなく、背景に画像を出すってことですか? 四角に入れるものだと思ってました^^; だったら【1】と【2】ほとんど関係ないような... > 以前質問させていただいた2つを組み合わせたものにしようとしています。 【1】のBA回答は移動のみですがいいのですか? 次の質問が「四角をリサイズするには」になったりしませんか? わたしの2つ目の回答は移動・リサイズできています。 まああまりいい出来ではなかったので、よりシンプルなものに書き直して手元では大体できてはいます。 しかし機能が盛沢山すぎて1万字に収まる気がしません... あぁ「SubWindowはあっちの回答そのままです」って手で省略できるか??でもあっちは中途半端な状態なんだよなぁ... SubWindowは「モードレス・[適用]ボタンを押したとき」でいいんでしょうか? そうならあっちを編集します。
nodoita

2023/07/07 00:16

>「画像は選択肢であって四角と1対1ではない」ということですね。 >四角に画像を入れるのではなく、背景に画像を出すってことですか? そうです!言葉足らずで申し訳ありません。 【1】と【2】のデータというか、そういうのは特に関係ないです。 SubWindowが出ているときはMainWindowの操作予定はないので モーダル?で問題ないです!
TN8001

2023/07/07 08:54

> SubWindowが出ているときはMainWindowの操作予定はないので > モーダル?で問題ないです! モーダル:ほげダイアログ モードレス:ほげウィンドウ と用語を使い分けていただくと、ほかの人(特にPC歴長い方)に通じやすいです^^ モーダルなのかぁ(また予想が外れたw まあ週末のネタができたので全然いいです^^; 部分部分に分けて過去回答書き替えていくかなぁ? 次の質問(リサイズが来るんですよね?)に間に合うかはわかりませんが^^;
nodoita

2023/07/10 04:52

色々とお世話になり、またご迷惑をおかけし申し訳ありません。 サイズ変更はとりあえず置いておいて、機能を優先したいと思ったのですが、動作が上手くいかないのでまた質問させていただきます。 毎回ありがとうございます!
guest

0

【1】
WPF 動的生成したRectangleがドラッグで移動できない、Bindingが難しい…
https://teratail.com/questions/j700zzpncph60k

上記が新しくなってました。
新しくしてどうか確認します。

投稿2023/07/04 06:51

nodoita

総合スコア7

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

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

TN8001

2023/07/04 10:09

ここは回答欄です。回答欄に回答でないものを書き込まないでください。 このような内容は質問のコメント欄か、質問を編集して追記してください。 この回答は削除リクエスト(右下のゴミ箱アイコン)してください。
nodoita

2023/07/06 01:26

ありがとうございます、削除リクエストしました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問