powered by simpleCommunicator - 2.0.35     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Не всегда срабатывает MouseDown
16 сообщений из 16, страница 1 из 1
Не всегда срабатывает MouseDown
    #39986760
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здравствуйте!

Есть код. Это маленькая программа рисовалка.
XAML
Код: html
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
<Window x:Class="WpfApp_test20_canvas2.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:WpfApp_test20_canvas2"
        mc:Ignorable="d"
        Title="Рисовалка" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="340"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Canvas Name="MyCanvas" Background="White" Grid.Row="0" MouseMove="myCanvas_MouseMove" MouseLeave="myCanvas_MouseLeave"
                MouseDown="myCanvas_MouseDown" MouseUp="myCanvas_MouseUp"/>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <GroupBox Grid.Column="0" Header="Текущие координаты" Margin="5">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>

                    <Label Content="X:"  HorizontalAlignment="Right" VerticalAlignment="Center" Margin ="0" Grid.Row="3" Grid.Column="0"></Label>
                    <Label Name="label_CurrentPoint_x" VerticalAlignment="Center" Margin ="0,0,0,0" Grid.Row="3" Grid.Column="1"></Label>
                    <Label Content="Y:"  HorizontalAlignment="Right" VerticalAlignment="Center" Margin ="5,0,0,0" Grid.Row="2" Grid.Column="2"></Label>
                    <Label Name="label_CurrentPoint_y" VerticalAlignment="Center" Margin ="0,0,0,0" Grid.Row="3" Grid.Column="3"></Label>
                </Grid>
            </GroupBox>
            <GroupBox Grid.Column="1" Header="Рисование" Margin="5">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                    </Grid.ColumnDefinitions>

                    <Button Name ="Button_StartStop" Grid.Column="0" Margin="5" Content="Начать рисовать" Click="Button_StartStop_Click"/>
                    <Button Name ="Button_Reset" Grid.Column="1" Margin="5" Content="Сбросить" Click="Button_Reset_Click"/>
                </Grid>
            </GroupBox>
        </Grid>
    </Grid>    
</Window>

C#
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp_test20_canvas2
{
    //Класс, в котором хранятся коррдинаты одной точки
    public class MyTable
    {
        public MyTable(double x, double y)
        {
            X = x;
            Y = y;
        }

        public double X { get; set; }
        public double Y { get; set; }
    }

    public partial class MainWindow : Window
    {
        ObservableCollection<MyTable> PointList; //Список для сохранения коррдинат точек
        private bool DrawingMode = false; //Режим рисования?
        private bool FirstPoint = false; //Это первая точка?        

        //Предпоследняя точка
        private int X0 = 0;
        private int Y0 = 0;

        //Последняя, текущая точка
        private int X = 0;
        private int Y = 0;

        public MainWindow()
        {
            InitializeComponent();

            PointList = new ObservableCollection<MyTable>();
        }

        //Событие перемещения мыши. Для отображения текущей координаты на Panel1
        private void myCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            //Для отображения текущей точки на форме
            label_CurrentPoint_x.Content = Convert.ToString(e.GetPosition(null).X);
            label_CurrentPoint_y.Content = Convert.ToString(e.GetPosition(null).Y);

            //Если режим рисования и это не первая точка
            if (DrawingMode && FirstPoint == false)
            {   
                //Получаем текущие координаты
                X = (int)e.GetPosition(null).X;
                Y = (int)e.GetPosition(null).Y;
                //Рисуем линии. С последней линией
                DrawLines(true);                
            }
        }

        //Событие, если мышь находится вне Panel1 и обнуляет кооррдинаты
        private void myCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            //Для отображения текущей точки на форме. В данном случае пусто
            label_CurrentPoint_x.Content = "";
            label_CurrentPoint_y.Content = "";

            if (DrawingMode)
            {
                //Рисуем линии. Без последней линии
                DrawLines();                          
            }
        }

        //Событие. Если нажата клавиша мыши
        private void myCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            //Если режим рисования и первая точка
            if (DrawingMode && FirstPoint)
            {
                //Получаем текущие координаты
                X0 = (int)e.GetPosition(null).X;
                Y0 = (int)e.GetPosition(null).Y;

                X = X0;
                Y = Y0;

                FirstPoint = false;
            }

            if (DrawingMode && FirstPoint == false)
            {
                PointList.Add(new MyTable(Convert.ToDouble(X), Convert.ToDouble(Y)));
            }
        }

        private void myCanvas_MouseUp(object sender, MouseEventArgs e)
        {
           
        }        

        private void DrawLines(Boolean LastContourPoint = false)
        {
            MyCanvas.Children.Clear();

            int len = PointList.Count;            

            if (len > 0)
            {               
                if (len == 1 && LastContourPoint)
                {
                    DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));
                }
                else
                {
                    for (int i = 1; i < len; i++)
                    {
                        DrawOneLine(PointList[i-1].X, PointList[i-1].Y, PointList.X, PointList[i].Y);                        
                    }

                    if (LastContourPoint)
                    {
                        DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));                        
                    }
                }
            }
        }

        //Кнопка стереть
        private void Button_Reset_Click(object sender, RoutedEventArgs e)
        {
            PointList.Clear();
            MyCanvas.Children.Clear();
            Button_StartStop.Content = "Начать рисовать";
            Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(221, 221, 221));
            DrawingMode = false;
        }

        //Кнопка Начать рисовать/Стоп
        private void Button_StartStop_Click(object sender, RoutedEventArgs e)
        {
            if (DrawingMode == false)
            {
                if (PointList.Count == 0) FirstPoint = true;
                DrawingMode = true;
                Button_StartStop.Content = "Стоп";
                Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
            }
            else
            {
                DrawingMode = false;                
                Button_StartStop.Content = "Продолжить рисовать";
                Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(221, 221, 221));
            }
        }

        private void DrawOneLine(double x1, double y1, double x2, double y2)
        {
            Line MyLine = new Line();
            MyLine.X1 = (float)x1; //BeginPoint.X;
            MyLine.Y1 = (float)y1; //BeginPoint.Y;
            MyLine.X2 = (float)x2; //EndPoint.X;
            MyLine.Y2 = (float)y2; //EndPoint.Y;
            MyLine.Stroke = System.Windows.Media.Brushes.Blue; //Задаем цвет линии
            MyLine.StrokeThickness = 2; //Ширина линий
            MyCanvas.Children.Add(MyLine); //Добавляем в canvas
        }
    }
}

Алгоритм пока не совсем оптимальный, так как пока экспериментирую. Код работает, но не всегда срабатывает [i]MouseDown . Почему так?
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39986836
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
для рисование геометрических примитивов, таких как линии, кривые и т.д. в WPF используют геометрию PathGeometry и другие виды геометрий (к примеру, StreamGeometry и т.д.).
А для отображения геометрии на экране используют элементу управления Shape, это может быть как собственный Shape с рассчитываемой геометрией внутри, либо объект Path, который отображают любую геометрию, которую ему предоставят.
То есть, изучите следующие темы:
1) Синтаксис разметки геометрии https://docs.microsoft.com/ru-ru/dotnet/framework/wpf/graphics-multimedia/path-markup-syntax, синтаксис 1 в 1 как в SVG
2) Разобраться в таких объектах как Freezable , Geometry , PathGeometry
3) Разобраться в таких элементах управления как Shape , Path

как пример, можно глянуть вот это: https://github.com/meytes/LSystemShape
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39986953
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtes,

ОК, спасибо за ссылки! Прочитаю эти материалы.


Roman Mejtes
для рисование геометрических примитивов, таких как линии, кривые и т.д. в WPF используют геометрию PathGeometry и другие виды геометрий (к примеру, StreamGeometry и т.д.).
А для отображения геометрии на экране используют элементу управления Shape, это может быть как собственный Shape с рассчитываемой геометрией внутри, либо объект Path, который отображают любую геометрию, которую ему предоставят.
Но все таки хотелось бы понять почему в данной ситуации не всегда срабатывает событие на нажатие кнопки мыши. Ведь в панеле инструментов присутствует объект Canvas и он же как то должен корректно работать. Или используется в только в других случаях.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39986963
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ferzmikk
Но все таки хотелось бы понять почему в данной ситуации не всегда срабатывает событие на нажатие кнопки мыши. Ведь в панеле инструментов присутствует объект Canvas и он же как то должен корректно работать. Или используется в только в других случаях.

А есть уверенность, что не срабатывает именно обработчик события? Внутри него есть условные ветвления, может, в зависимости от неких условий не срабатывают они?
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39986985
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сон Веры Павловны
А есть уверенность, что не срабатывает именно обработчик события?
Ставлю точку останова - не всегда останавливается, когда нажимаю на кнопку мыши. Внутри него есть условные ветвления, может, в зависимости от неких условий не срабатывают они?Что за условные ветвеления?
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39987002
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
событие MouseDown может не сработать по нескольким причинам:
А) событие уже было обработано другим элементом управления, который находится внутри вашего Canvas или находится поверх него.
в WPF есть 2 типа таких событий:
обычный событий и Preview события (для примера PreviewMouseDown и MouseDown), оба этих события маршрутизируемые.
PreviewMouseDown - это туннелированное событие (tunnel), оно поднимается от корневого элемента управления (окна) к вершинам. Элементы управления принимая это событие проверяют (выполняют HitTest) на них или нет было осуществлено это нажатие и возбуждают ответное пузырьковое (всплывающее) событие, например, MouseDown.
MouseDown - пузырьковое событие (bubble), оно опускается от вершины на которое вы нажали к корню.
Почитайте об этом подробнее https://metanit.com/sharp/wpf/6.php.
Б) Элемент управления на который вы нажимаете не имеет заднего фона. У элементов управления есть свойство Background, которое определяет цвет заливки элемента управления, тип свойства Brush, если это свойство задано как {x:Null} (значение по умолчанию), то такой элемент управления не имеет фона, то есть он образно говоря с дыркой, следовательно он не пройдет HitTest, то есть событие MouseDown не будет вызвано, элемент будет прозрачным для взаимодействия. Если задать цвет фона Transparent, элемент управления хоть и останется прозрачным (визуально), но начнет реагировать на событий
В) у элемента управления задано свойство HitTestVisibility = false, такой элемент не будет событий ввода.

Если короче, то можно либо:
А) использовать Preview события, этот метод плохой, так как нарушает всю концепцию
Б) задать фон элементу управления на котором осуществляется ввод с мышки
В) обрабатывать события не напрямую с элемента управления, а в элементе управления предке или корневом элементе управления.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989141
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если упростить пример кода, чтобы акцентироваться на проблему, то получится так:
XAML
Код: html
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<Window x:Class="WpfApp_test20_canvas4.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:WpfApp_test20_canvas4"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Canvas Name="MyCanvas" Background="White"  MouseMove="myCanvas_MouseMove" MouseLeave="myCanvas_MouseLeave"
                MouseDown="myCanvas_MouseDown"/>
    </Grid>
</Window>

C#
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;

namespace WpfApp_test20_canvas4
{
    //Класс, в котором хранятся коррдинаты одной точки
    public class MyTable
    {
        public MyTable(double x, double y)
        {
            X = x;
            Y = y;
        }

        public double X { get; set; }
        public double Y { get; set; }
    }

    public partial class MainWindow : Window
    {
        ObservableCollection<MyTable> PointList; //Список для сохранения коррдинат точек        
        private bool FirstPoint = false; //Это первая точка?        

        //Предпоследняя точка
        private int X0 = 0;
        private int Y0 = 0;

        //Последняя, текущая точка
        private int X = 0;
        private int Y = 0;

        public MainWindow()
        {
            InitializeComponent();
            PointList = new ObservableCollection<MyTable>();
        }

        //Событие перемещения мыши.
        private void myCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            //Если это не первая точка
            if (FirstPoint == false)
            {
                //Получаем текущие координаты
                X = (int)e.GetPosition(null).X;
                Y = (int)e.GetPosition(null).Y;
                //Рисуем линии. С последней линией
                DrawLines(true);
            }
        }

        //Событие, если мышь находится вне Canvas
        private void myCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            //Рисуем линии. Без последней линии
            DrawLines();
        }

        
        private void myCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            //Если режим рисования и первая точка
            if (FirstPoint)
            {
                //Получаем текущие координаты
                X0 = (int)e.GetPosition(null).X;
                Y0 = (int)e.GetPosition(null).Y;

                X = X0;
                Y = Y0;

                FirstPoint = false;
            }

            if (FirstPoint == false)
            {
                PointList.Add(new MyTable(Convert.ToDouble(X), Convert.ToDouble(Y)));
            }
        }

        //Рисуем линии
        private void DrawLines(Boolean LastContourPoint = false)
        {
            MyCanvas.Children.Clear();

            int len = PointList.Count;

            if (len > 0)
            {
                if (len == 1 && LastContourPoint)
                {
                    DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));
                }
                else
                {
                    for (int i = 1; i < len; i++)
                    {
                        DrawOneLine(PointList[i - 1].X, PointList[i - 1].Y, PointList.X, PointList[i].Y);
                    }

                    if (LastContourPoint)
                    {
                        DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));
                    }
                }
            }
        }

        //Рисуем линию
        private void DrawOneLine(double x1, double y1, double x2, double y2)
        {
            Line MyLine = new Line();
            MyLine.X1 = (float)x1; //BeginPoint.X;
            MyLine.Y1 = (float)y1; //BeginPoint.Y;
            MyLine.X2 = (float)x2; //EndPoint.X;
            MyLine.Y2 = (float)y2; //EndPoint.Y;
            MyLine.Stroke = System.Windows.Media.Brushes.Blue; //Задаем цвет линии
            MyLine.StrokeThickness = 2; //Ширина линий
            MyCanvas.Children.Add(MyLine); //Добавляем в canvas
        }
    }
}


Roman Mejtes
событие MouseDown может не сработать по нескольким причинам:
Спасибо за описание, разбираю.

Подскажите, как в Visual Studio отслеживать какие события запускаются/улавливаются?

Roman Mejtes
Если короче, то можно либо:
А) использовать Preview события, этот метод плохой, так как нарушает всю концепцию
если так написать
Код: html
1.
2.
<Canvas Name="MyCanvas" Background="White"  PreviewMouseMove="myCanvas_MouseMove" MouseLeave="myCanvas_MouseLeave"
                PreviewMouseDown="myCanvas_MouseDown"/>

то также

Б) задать фон элементу управления на котором осуществляется ввод с мышки [i]Background
указан.

В) обрабатывать события не напрямую с элемента управления, а в элементе управления предке или корневом элементе управления.Если так написать
Код: html
1.
2.
3.
<Grid Background="White" MouseMove="myCanvas_MouseMove" MouseLeave="myCanvas_MouseLeave" MouseDown="myCanvas_MouseDown">
        <Canvas Name="MyCanvas"/>
</Grid>

то опять также
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989223
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ferzmikk
Подскажите, как в Visual Studio отслеживать какие события запускаются/улавливаются?

Наверное, только проверить экспериментально.
Это очень зависит от контейнеров компоновки.

Вы знакомы с концепций событий в WPF?

Все события возникают на уровне ОС.
Допустим, юзер нажал кнопку.
Драйвер клавиатуры определил это и послал сообщение OC.
ОС определяет, что сейчас активно окно такого-то приложения.
Какой именно элемент в окне сейчас вводи текст - ОС не знает.
Она просто передаёт Окну, что нажата кнопка.
Окно тоже имеет в себе множество UI элементов.
Какой элемент требует ввода она не знает.
Она просто вызывает вложенный элемент и передаёт ему событие.
Тот элемент передаёт своему дочернему.
И так событие спускается вниз пока не наткнётся на элемент который ожидает это событие.

Такие спускающиеся события называются туннельными. И обычно имеют префикс Preview.
Если элемент сам обрабатывает событие, то он может отменить его дальнейшее погружение.
И его дочерние элементы это событие уже не получат.

Обработав туннельное событие UI элемент создаёт событие, уведомляющее об этом.
Это событие начинает наоборот движение снизу-вверх, для того чтобы вышестоящие контейнеры могли обработать изменение дочерних элементов.
То есть в элемент пришло сверху событие, допустим, PreviewMouseDown и если он его обработал, то он посылает вверх событие MouseDouwn.
Такие события называются пузырьковыми (буквальный перевод) или всплывающими (более распространено в русс. среде).
Выше стояший контейнер может обработать такое событие и прекратить его всплытие.
Допустим, таким образом поступают кнопки (ButtonBase) они обрабатывают событие MouseLeftButtonDown и дальше из них оно не всплывает.

Поэтому, во многих случаях, чтобы контейнер знал о событии, надо обрабатывать туннельные события (Preview...), так как всплывающие события из вложенных элементов могут просто не дойти, не всплыть до контейнера.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989422
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld Hasp
Вы знакомы с концепций событий в WPF?
Пытаюсь пока разобраться.

Поэтому, во многих случаях, чтобы контейнер знал о событии, надо обрабатывать туннельные события (Preview...),Скажите, а смысл использования туннельных событий, если MouseDown привязан к одному элементу - Canvas .
Код: html
1.
2.
3.
<Grid>
        <Canvas PreviewMouseDown="myCanvas_MouseDown"/>
</Grid>


Туннельные события используются в основном, когда так пишут.
Код: html
1.
2.
3.
<Grid PreviewMouseDown="myCanvas_MouseDown">
        <Canvas PreviewMouseDown="myCanvas_MouseDown"/>
</Grid>


так как всплывающие события из вложенных элементов могут просто не дойти, не всплыть до контейнера.Все таки хотелось как нибудь отследить, что доходит и что не доходит до контейнера, и почему.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989514
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ferzmikk
Скажите, а смысл использования туннельных событий, если MouseDown привязан к одному элементу - Canvas .

Один из дочерних элементов Canvas может не дать всплывать MouseDown и до Canvas это событие не дойдёт.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989519
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ferzmikk, кстати почему у вас Canvas без размеров?
Или я чего-то не вижу?
По умолчанию у Canvas размер, вроде, ноль.
Попробуйте явно задать привязку размеров к размерам внешнего контейнера.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989644
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld Hasp,

размер элемента управления определяет не он сам, а его контейнер, элемент управления только "сообщает" контейнеру желаемый размер.
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<Window x:Class="WpfApp10.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:WpfApp10"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Canvas Background="Transparent" MouseDown="Canvas_MouseDown"/>
    </Grid>
</Window>


Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private const double EllipseWidth = 10.0;
    private const double EllipseHeight = 10.0;

    private Point? _lastPosition;

    private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        Canvas canvas = (Canvas)sender;
        Point position = e.GetPosition(canvas);

        if (_lastPosition.HasValue)
        {
            Line line = new Line()
            {
                X1 = _lastPosition.Value.X,
                Y1 = _lastPosition.Value.Y,
                X2 = position.X,
                Y2 = position.Y,
                Stroke = Brushes.Black
            };
            canvas.Children.Add(line);
        }

        Ellipse ellipse = new Ellipse()
        { 
            Width = EllipseWidth, 
            Height= EllipseHeight,
            Fill = Brushes.Black
        };
        ellipse.SetValue(Canvas.LeftProperty, position.X - EllipseWidth / 2.0);
        ellipse.SetValue(Canvas.TopProperty, position.Y - EllipseHeight / 2.0);
        canvas.Children.Add(ellipse);

        _lastPosition = position;

    }
}
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989658
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в общем не поленился, глянул код автора, говнокод редкостный, просто адище, сильно его вычищать не стал, не моё это дело,
но вот рабочее решение:

Замечания по коду:
  • не используйте тип float, используйте родной тип double, у вас в коде постоянное приведение из int в float, из float в double, где то явное, где то неявное, это не хорошо
  • постоянно очищаться и добавлять в Canvas.Children элементы это плохая идея. Тем более вы используйте коллекцию ObsevableCollection, гораздо лучше будет, если ваш элемент управления будет отслеживать изменения в коллекцию и добавлять\удалять элементы из Canvas (гуглите в интернетах шаблон поведения Observer (наблюдатель))
  • когда вы в методе размещаете линию (последнюю), которая бежит за мышкой, то она перекрывает собой элемент управления Canvas, вы размещаете линию прямо под мышкой, следовательно MouseDown происходит на самой линии, а не на Canvas'е, собственно в этом и была ваша ошибка, я подсветил её в вашем коде.
  • когда работаете с координатами точки, используйте соответствующие типы объектов Point, Rect и т.д. Нет никакого смысла хранить эти точки в полях X и Y, имеет смысл хранить это 1 поле с типом Point. Уходите от функционального программирования и стремитесь к объектно ориентированному, ведь вы работайте с объектом точка, а не её координатами.
  • старайтесь писать более лакончиный код, с минимальным количеством ветвления (условных переходов) там, где они нафиг не нужны. В методе DrawLines у вас куча каких то условий, хоть всё, что нужно, нарисовать существующие линии и затем последнюю, достаточно 1 условия, а не кучи вложенных в друг друга. то же самое касается MouseDown. в таком мелком коде это не критично, но когда программа большая, с таким кодом понять, что она делает будет крайне не просто.
  • не используйте лишние состояния, о том, что это первая точка можно понять не используя поле FirstPoint, а последнюю точку, лучше не извлекать из массива, а хранить в отдельном поле, так вы легкой поймете, что это первая точка (если последняя точка не определена) и будете иметь указатель на последнюю точку, тогда её не надо будет извлекать из массива. Ничего страшного в дублировании нет.
  • Вот этот ужас:
Код: c#
1.
2.
label_CurrentPoint_x.Content = Convert.ToString(e.GetPosition(null).X);
label_CurrentPoint_y.Content = Convert.ToString(e.GetPosition(null).Y);


во первых, операция не "атомарна" (на уровне выполнения кода, а не процессора), то есть вы получаете позицию 2 раза, между 1 и 2 разом может произойти всё, что угодно
во вторых, в методе e.GetPosition(null) нужно указывать тот объект, относительно которого вы хотите получить координаты, следовательно в аргументах должна быть ссылка на объект Canvas, а не на Null; правильнее будет e.GetPosition((IInputElement)sender) или e.GetPosition((Canvas)sender). Даже затрудняюсь сказать, что будет если передать Null, скорее всего будет использован или текущий элемент или окно. Но это не очевидно и неправильно.

я бы мог еще долго тут разносить этот код, но время дорого стоит.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp10
{
    public class MyTable
    {
        public MyTable(double x, double y)
        {
            X = x;
            Y = y;
        }

        public double X { get; set; }
        public double Y { get; set; }
    }

    public partial class MainWindow : Window
    {
        ObservableCollection<MyTable> PointList; //Список для сохранения коррдинат точек
        private bool DrawingMode = false; //Режим рисования?
        private bool FirstPoint = false; //Это первая точка?        

        //Предпоследняя точка
        private int X0 = 0;
        private int Y0 = 0;

        //Последняя, текущая точка
        private int X = 0;
        private int Y = 0;

        public MainWindow()
        {
            InitializeComponent();

            PointList = new ObservableCollection<MyTable>();
        }

        //Событие перемещения мыши. Для отображения текущей координаты на Panel1
        private void myCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            //Для отображения текущей точки на форме
            label_CurrentPoint_x.Content = Convert.ToString(e.GetPosition(null).X);
            label_CurrentPoint_y.Content = Convert.ToString(e.GetPosition(null).Y);

            //Если режим рисования и это не первая точка
            if (DrawingMode && FirstPoint == false)
            {
                //Получаем текущие координаты
                X = (int)e.GetPosition(null).X;
                Y = (int)e.GetPosition(null).Y;
                //Рисуем линии. С последней линией
                DrawLines(true);
            }
        }

        //Событие, если мышь находится вне Panel1 и обнуляет кооррдинаты
        private void myCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            //Для отображения текущей точки на форме. В данном случае пусто
            label_CurrentPoint_x.Content = "";
            label_CurrentPoint_y.Content = "";

            if (DrawingMode)
            {
                //Рисуем линии. Без последней линии
                DrawLines();
            }
        }

        //Событие. Если нажата клавиша мыши
        private void myCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            //Если режим рисования и первая точка
            if (DrawingMode && FirstPoint)
            {
                //Получаем текущие координаты
                X0 = (int)e.GetPosition(null).X;
                Y0 = (int)e.GetPosition(null).Y;

                X = X0;
                Y = Y0;

                FirstPoint = false;
            }

            if (DrawingMode && FirstPoint == false)
            {
                PointList.Add(new MyTable(Convert.ToDouble(X), Convert.ToDouble(Y)));
            }
        }

        private void myCanvas_MouseUp(object sender, MouseEventArgs e)
        {

        }

        private void DrawLines(Boolean LastContourPoint = false)
        {
            MyCanvas.Children.Clear();

            int len = PointList.Count;

            if (len > 0)
            {
                if (len == 1 && LastContourPoint)
                {
                    DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));
                }
                else
                {
                    for (int i = 1; i < len; i++)
                    {
                        DrawOneLine(PointList[i - 1].X, PointList[i - 1].Y, PointList[i].X, PointList[i].Y);
                    }

                    if (LastContourPoint)
                    {
                        DrawOneLine(PointList[len - 1].X, PointList[len - 1].Y, Convert.ToDouble(X), Convert.ToDouble(Y));
                    }
                }
            }
        }

        //Кнопка стереть
        private void Button_Reset_Click(object sender, RoutedEventArgs e)
        {
            PointList.Clear();
            MyCanvas.Children.Clear();
            Button_StartStop.Content = "Начать рисовать";
            Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(221, 221, 221));
            DrawingMode = false;
        }

        //Кнопка Начать рисовать/Стоп
        private void Button_StartStop_Click(object sender, RoutedEventArgs e)
        {
            if (DrawingMode == false)
            {
                if (PointList.Count == 0) FirstPoint = true;
                DrawingMode = true;
                Button_StartStop.Content = "Стоп";
                Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
            }
            else
            {
                DrawingMode = false;
                Button_StartStop.Content = "Продолжить рисовать";
                Button_StartStop.Background = new SolidColorBrush(Color.FromRgb(221, 221, 221));
            }
        }

        private void DrawOneLine(double x1, double y1, double x2, double y2)
        {
            Line MyLine = new Line();
            MyLine.X1 = (float)x1; //BeginPoint.X;
            MyLine.Y1 = (float)y1; //BeginPoint.Y;
            MyLine.X2 = (float)x2; //EndPoint.X;
            MyLine.Y2 = (float)y2; //EndPoint.Y;
            MyLine.IsHitTestVisible = false;
            MyLine.Stroke = System.Windows.Media.Brushes.Blue; //Задаем цвет линии
            MyLine.StrokeThickness = 2; //Ширина линий
            MyCanvas.Children.Add(MyLine); //Добавляем в canvas
        }
    }
}
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989664
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman Mejtes, вы правы.
По какой-то причине (уже не могу вспомнить), я думал, что у Canvas иное поведение.
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989667
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld Hasp,

Canvas действительно сообщает контейнеру, что его желаемый размер является (0.0, 0.0), по этому, если вы размещаете Canvas в таком элементе управления, у которого ширина или высота бесконечны (StackPanel, ScrollViewer (с прокруткой) и т.д. То Canvas будет иметь 0 размер по соответствующим осям
...
Рейтинг: 0 / 0
Не всегда срабатывает MouseDown
    #39989809
ferzmikk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtes,

Спасибо за замечания!
...
Рейтинг: 0 / 0
16 сообщений из 16, страница 1 из 1
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Не всегда срабатывает MouseDown
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]