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
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/14 15:51
回答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総合スコア9862
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/07/20 06:55
0
<Canvas x:Name="canvas" Background="Black"/> <Canvas x:Name="mouseCanvas" Background="Transparent" MouseLeftButtonDown="canvas_MouseLeftDown"/>
マウスクリック用の透明なキャンバスを上から被せれば、描画用のキャンバスをクリアしてもイベントがクリアされないので、問題無く動作しています。
投稿2022/02/16 16:13
総合スコア16
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/02/17 22:03
2022/02/25 05:45 編集
2022/03/01 11:29
2022/03/01 16:03
0
Canvasの子要素をクリックしてCanvasがイベントを受け取れるのは、WPFのルーティングイベント-バブルイベントの仕組みのおかげです。
内部では、子要素で処理しなかったイベントを親要素に投げているのです。
Canvasをクリアしたタイミングによっては、イベントを投げる前に子要素との親子関係が切れたり子要素自体が消されたりは十分考えられます。
詳しくは「WPF ルーティングイベント」でググってくだされたし。
投稿2020/06/15 10:08
総合スコア4152
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/16 14:56
2020/06/16 22:08
2020/06/17 16:02
2020/06/18 01:19
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
総合スコア20
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/07/16 13:20 編集
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
総合スコア20
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/07/06 16:14
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。