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

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

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

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

WPF

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

Q&A

解決済

5回答

5636閲覧

WPFのCanvasでマウスイベントが取得できない時がある。

Jellyfish0511

総合スコア16

C#

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

WPF

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

1グッド

0クリップ

投稿2020/06/13 18:25

C# + WPFのプログラムを勉強して半年程の初心者です。

WPFのCanvas上で図形描画をC#から定期的に更新する処理を作っています。(パラパラ漫画的なものです)
処理は単純でタイマー(Timerクラス)のCallBack関数から一回画面をClear()して図形を描画するという動作になっています。
見た目は特に問題なく動ているのですが、マウスでのヒットテストの処理も入ってるのですが、時々イベントハンドラに処理が飛んでこない事があります。
タイマのタイムアウト時間を早くすると顕著になります。
上記で説明したような処理に問題があるのか、ご教授頂けると幸いです。

該当のソースコード

XAML

<Canvas x:Name="canvas" MouseLeftButtonDown="canvas_MouseLeftDown"> … </Canvas>

C#

void TimerCallBack(object state) { this.Dispatcher.BeginInvoke(new Action(() => { { canvas.Children.Clear(); 図形描画の処理     } })); } private void canvas_MouseLeftDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { ヒットテスト処理 }

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

Visual Studio 2019
.framework 4.8

TN8001👍を押しています

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

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

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

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

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

dodox86

2020/06/14 00:28

タイマのコールバック内の図形描画処理はUIスレッドで行われているはずなので、そこで時間を占有すると同じUIスレッドでのマウスイベントのハンドルがスキップ、あるいは遅延してしまうのだと思います。(あくまで推測なのでコメントのみ、ですが)
Jellyfish0511

2020/06/14 15:51

早々のコメントありがとうございます。 描画するオブジェクト数をかなり減らしてみたのですが、結果は変わりませんでした。 また、何も描画されていない個所をクリックする分には、正常にハンドラに飛ぶので良くわからないですね。
guest

回答5

0

ベストアンサー

100本の棒が回転しています(これ自体に意味はない。動いてるのがわかりやすい例ってだけ)
棒の中央あたりをクリック連打してください。
中央あたりは常に反応するはずですが、たまに抜けるのが確認できると思います(抜けない場合はiterationを増やしてください。カクカクになるくらいでちょうどいいです)
タイマーの種類はあまり関係ありません。
canvas.Children.Clear()している以上、反応するかはタイミング次第ということになります。

xml

1<Window 2 x:Class="Questions269801.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="800" 6 Height="450"> 7 <Canvas x:Name="canvas" MouseLeftButtonDown="canvas_MouseLeftDown" /> 8</Window>

cs

1using System; 2using System.Diagnostics; 3using System.Linq; 4using System.Windows; 5using System.Windows.Input; 6using System.Windows.Media; 7using System.Windows.Shapes; 8using System.Windows.Threading; 9 10namespace Questions269801 11{ 12 public partial class MainWindow : Window 13 { 14 System.Threading.Timer threadingTimer; 15 DispatcherTimer dispatcherTimer; 16 17 const int iteration = 100; 18 int angle; 19 public MainWindow() 20 { 21 InitializeComponent(); 22 23 //threadingTimer = new System.Threading.Timer(new System.Threading.TimerCallback(TimerCallBack)); 24 //threadingTimer.Change(0, 1); 25 26 dispatcherTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1), }; 27 dispatcherTimer.Tick += Timer_Tick; 28 dispatcherTimer.Start(); 29 } 30 31 private void Timer_Tick(object sender, EventArgs e) 32 { 33 angle++; 34 var sin = Math.Sin(angle * (Math.PI / 180)); 35 var cos = Math.Cos(angle * (Math.PI / 180)); 36 canvas.Children.Clear(); 37 38 foreach(var i in Enumerable.Range(0, iteration)) 39 { 40 var line = new Polyline { Stroke = Brushes.Black, StrokeThickness = 50, }; 41 line.Points.Add(new Point(cos * 100 + 200 + i * 100, sin * 100 + 200)); 42 line.Points.Add(new Point(-cos * 100 + 200 + i * 100, -sin * 100 + 200)); 43 canvas.Children.Add(line); 44 } 45 } 46 47 void TimerCallBack(object state) 48 { 49 angle++; 50 var sin = Math.Sin(angle * (Math.PI / 180)); 51 var cos = Math.Cos(angle * (Math.PI / 180)); 52 Dispatcher.BeginInvoke(new Action(() => 53 { 54 canvas.Children.Clear(); 55 56 foreach(var i in Enumerable.Range(0, iteration)) 57 { 58 var line = new Polyline { Stroke = Brushes.Black, StrokeThickness = 50, }; 59 line.Points.Add(new Point(cos * 100 + 200 + i * 100, sin * 100 + 200)); 60 line.Points.Add(new Point(-cos * 100 + 200 + i * 100, -sin * 100 + 200)); 61 canvas.Children.Add(line); 62 } 63 })); 64 } 65 66 private void canvas_MouseLeftDown(object sender, MouseButtonEventArgs e) 67 { 68 Debug.WriteLine("canvas_MouseLeftDown"); 69 } 70 } 71}

hihijijiさんが言っているように、クリアしない手法を考える必要があります。
どうするかは描画内容次第ですが、今回はRotateTransformを使ってみました。
クリック連打時の違いを確認してください。

xamlは同一

cs

1using System; 2using System.Diagnostics; 3using System.Linq; 4using System.Windows; 5using System.Windows.Input; 6using System.Windows.Media; 7using System.Windows.Shapes; 8using System.Windows.Threading; 9 10namespace Questions269801 11{ 12 public partial class MainWindow : Window 13 { 14 DispatcherTimer dispatcherTimer; 15 16 const int iteration = 100; 17 int angle; 18 public MainWindow() 19 { 20 InitializeComponent(); 21 22 dispatcherTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1), }; 23 dispatcherTimer.Tick += Timer_Tick; 24 dispatcherTimer.Start(); 25 26 angle++; 27 var sin = Math.Sin(angle * (Math.PI / 180)); 28 var cos = Math.Cos(angle * (Math.PI / 180)); 29 canvas.Children.Clear(); 30 31 foreach(var i in Enumerable.Range(0, iteration)) 32 { 33 var line = new Polyline { Stroke = Brushes.Black, StrokeThickness = 50, }; 34 line.Points.Add(new Point(cos * 100 + 200 + i * 100, sin * 100 + 200)); 35 line.Points.Add(new Point(-cos * 100 + 200 + i * 100, -sin * 100 + 200)); 36 line.RenderTransform = new RotateTransform 37 { 38 CenterX = 200 + i * 100, 39 CenterY = 200, 40 }; 41 canvas.Children.Add(line); 42 } 43 } 44 45 private void Timer_Tick(object sender, EventArgs e) 46 { 47 angle++; 48 foreach(Polyline line in canvas.Children) 49 { 50 var t = line.RenderTransform as RotateTransform; 51 t.Angle = angle; 52 } 53 } 54 55 private void canvas_MouseLeftDown(object sender, MouseButtonEventArgs e) 56 { 57 Debug.WriteLine("canvas_MouseLeftDown"); 58 } 59 } 60}

投稿2020/07/16 14:52

編集2023/07/22 09:21
TN8001

総合スコア9862

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

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

Jellyfish0511

2020/07/20 06:55

大変丁寧なご回答ありがとうございました。 クリアしない手法のサンプルをこちらで動かした所、正常に動いていることを確認できました。 上記サンプルを例にこちらのプログラムを修正したいと思います。 ご対応ありがとうございました。
guest

0

<Canvas x:Name="canvas" Background="Black"/> <Canvas x:Name="mouseCanvas" Background="Transparent" MouseLeftButtonDown="canvas_MouseLeftDown"/>

マウスクリック用の透明なキャンバスを上から被せれば、描画用のキャンバスをクリアしてもイベントがクリアされないので、問題無く動作しています。

投稿2022/02/16 16:13

Jellyfish0511

総合スコア16

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

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

TN8001

2022/02/17 22:03

それで済む話だったのですか? なら要素のIsHitTestVisibleをfalseにすれば、常にCanvasに届くんじゃないですかね?(詳細な確認はしていません) falseでもVisualTreeHelper.HitTestは反応します(確認済み) もちろんHitTest中にビジュアル ツリーを変更するすると、同じような問題は出ます。 まあ1枚余計にかぶせても大差ないでしょうが^^;
Jellyfish0511

2022/02/25 05:45 編集

ご連絡ありがとうございます。 教えて頂いた方法でもマウスイベントがCanvasに届くようになり、VisualTreeHelper.HitTestも動いています。 何度もご回答を頂き誠にありがとうございました。 教えて頂いた方法やCanvasを被せた時でもマウスイベントは取れるようにはなりますが、結局はClear()はしているのでタイミングによってはHitTestが失敗すると思っていたのですが、どういう訳か殆ど失敗せず(稀に失敗しているのかもしれないのですが)許容できる範囲です。 素人的な発想ですがマウスイベントで座標を取っておき、Clear()する直前、又は直後でHitTestをすれば上記失敗も回避できるのではと思っていますが駄目ですかね。
TN8001

2022/03/01 11:29

通知が来なくて気が付くのが遅れました。すいません。 > 素人的な発想ですがマウスイベントで座標を取っておき、Clear()する直前、又は直後でHitTestをすれば上記失敗も回避できるのではと思っていますが駄目ですかね。 コールバック(HitTestResultCallback)を使うと処理が前後しそうな気がするんですが、詳細は追っていません^^; まあ動いているならそれが正義という気はします^^;
Jellyfish0511

2022/03/01 16:03

ご連絡ありがとうございました。 >まあ動いているならそれが正義という気はします^^; カップインまではしていませんが、動いているから良しとしたいと思います。 まさか2年近く経過して本件がクローズするとは夢にも思いませんでした。 色々とアドバイスを頂きにありがとうございました。
guest

0

Canvasの子要素をクリックしてCanvasがイベントを受け取れるのは、WPFのルーティングイベント-バブルイベントの仕組みのおかげです。
内部では、子要素で処理しなかったイベントを親要素に投げているのです。
Canvasをクリアしたタイミングによっては、イベントを投げる前に子要素との親子関係が切れたり子要素自体が消されたりは十分考えられます。

詳しくは「WPF ルーティングイベント」でググってくだされたし。

投稿2020/06/15 10:08

hihijiji

総合スコア4152

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

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

Jellyfish0511

2020/06/16 14:56

ご回答ありがとうございます。 この対策で良いのか分からないのですが、 試しに子要素(図形)のマウスボタン押下のイベントハンドラも登録してみました。 例) var line = new PolyLine(); line .Points.Add(new Point(0.0, 0.0)); line .Points.Add(new Point(10.0, 10.0)); … line.MouseLeftButtonDown += line_MouseLeftButtonDown; …(座標変換も実施) add.canvas.Children(line); 失敗する頻度は下がったような気がするのですが、 canvas, 図形、両方のイベントハンドラが呼ばれない時があります。
hihijiji

2020/06/16 22:08

いやいや canvas.Children.Clear(); をやめましょう。
Jellyfish0511

2020/06/17 16:02

Remove, RemoveAt, RemoveRange等を使って、削除しながら描画するという事でしょうか。
hihijiji

2020/06/18 01:19

おそらく部分的な変更で対処するのは困難だと思います。 WPFの特性を理解した上で根本的に実装を見直すことをお勧めします。
guest

0

できるだけ単純化したアプリで同様の状況が発生するか確認してみてください。
以下のコードでは問題は発生しないので、UIの問題ではなくタイマー処理の問題かもしれません。

XAML

1<Window 2 x:Class="WPF_Canvas.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="300" Width="300" 9 > 10 <DockPanel> 11 <Button DockPanel.Dock="Bottom" 12 Width="100" Margin="0, 10" 13 Content="Clear Canvas" Click="Button_Click" 14 /> 15 <Canvas x:Name="xCanvas" 16 Background="Transparent" 17 MouseLeftButtonDown="Canvas_MouseLeftButtonDown" 18 > 19 <Rectangle Canvas.Top="70" Canvas.Left="100" 20 Width="100" Height="100" 21 Fill="Aquamarine"         22 MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" 23 /> 24 </Canvas> 25 </DockPanel> 26</Window>

C#

1using System.Diagnostics; 2using System.Windows; 3using System.Windows.Input; 4 5namespace WPF_Canvas 6{ 7 public partial class MainWindow : Window 8 { 9 public MainWindow() 10 { 11 InitializeComponent(); 12 } 13 14 private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 15 { 16 Debug.WriteLine("Canvas_MouseLeftButtonDown!"); 17 e.Handled = true; 18 } 19 20 private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 21 { 22 Debug.WriteLine("Rectangle_MouseLeftButtonDown!"); 23 e.Handled = true; 24 } 25 26 private void Button_Click(object sender, RoutedEventArgs e) 27 { 28 xCanvas.Children.Clear(); 29 e.Handled = true; 30 } 31 } 32}

投稿2020/07/12 01:34

mabmab

総合スコア20

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

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

Jellyfish0511

2020/07/16 13:20 編集

-----------------XAML--------------- <Window x:Class="AppTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AppTest" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Canvas x:Name="canvas" Background="Black" MouseLeftButtonDown="Canvas_MouseLeftButtonDown"> </Canvas> </Window> ------------------------------------- -----------------C#--------------- using System; using System.Diagnostics; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; namespace AppTest { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { Timer drawTimer; public MainWindow() { InitializeComponent(); drawTimer = new Timer(TimeCallBackDraw, null, Timeout.Infinite, Timeout.Infinite); drawTimer.Change(0, 10); } void TimeCallBackDraw(object state) { this.Dispatcher.BeginInvoke(new Action(() => { canvas.Children.Clear(); for (int i = 0; i < 10; i++) { Ellipse ellipse = new Ellipse(); ellipse.Width = 30; ellipse.Height = 30; ellipse.Fill = Brushes.White; ellipse.Stroke = Brushes.White; Canvas.SetTop(ellipse, 10 + i * 10); Canvas.SetLeft(ellipse, 10 + i * 10); canvas.Children.Add(ellipse); } })); } int count = 0; private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine($"MouseLeftDown : {count++}"); } } } ------------------------------------- 上記のような単純なソフトで確認してみたのですが、円の上でクリックすると時々イベントが飛んでこない時があります。何も描画されていない所では、問題ないです。
guest

0

結論から言えば、Canvasに何も書かれていない場所はmouseイベントが素通りしているだけです。
新規に作成した以下のアプリにCanvasとmouseイベントハンドラーを追加しただけではCanvasでmouseイベントは発生しません。

XAML

1<Window x:Class="WpfApp1.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 mc:Ignorable="d" 7 Title="MainWindow" Height="300" Width="300"> 8 <Grid> 9 <Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown"/> 10 </Grid> 11</Window>

C#

1using System.Diagnostics; 2using System.Windows; 3using System.Windows.Input; 4 5namespace WpfApp1 6{ 7 public partial class MainWindow : Window 8 { 9 public MainWindow() 10 { 11 InitializeComponent(); 12 } 13 14 private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 15 { 16 Debug.WriteLine("Canvas_MouseLeftButtonDown!"); 17 } 18 } 19}

 
でも、こうすれば見た目は同じでもmouseイベントが発生するはずです。

XAML

1 <Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown" Background="Transparent">

投稿2020/06/30 08:26

mabmab

総合スコア20

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

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

Jellyfish0511

2020/07/06 16:14

ご回答ありがとうございます。 こちらのサンプルソースに誤りがあり、XAMLにはBackgroundで色を付けております。 全くcanvasのマウスイベントが入らなくなるのではなく、 canvas.Children.Clear(); を実行すると、時々マウスイベントが入らない時があるようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問