A rectangle drawn with DrawingContext.DrawRectangle blocks mouse - c#

The WPF program below puts up a window which looks like this:
Mouse-movement outside the black square causes the window title to be updated with the mouse's position. The updating stops when the mouse enters the square.
I'd like for MouseMove to continue to trigger even when the mouse is over the square. Is there a way to do this?
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Wpf_Particle_Demo
{
class DrawingVisualElement : FrameworkElement
{
public DrawingVisual visual;
public DrawingVisualElement() { visual = new DrawingVisual(); }
protected override int VisualChildrenCount { get { return 1; } }
protected override Visual GetVisualChild(int index) { return visual; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var canvas = new Canvas();
Content = canvas;
var element = new DrawingVisualElement();
canvas.Children.Add(element);
CompositionTarget.Rendering += (s, e) =>
{
using (var dc = element.visual.RenderOpen())
dc.DrawRectangle(Brushes.Black, null, new Rect(0, 0, 50, 50));
};
MouseMove += (s, e) => Title = e.GetPosition(canvas).ToString();
}
}
}

By far the simplest way is to use the "tunneling" event on the window, PreviewMouseDown. It is delivered to the window first and works its way up the hierarchy. So it doesn't matter at all which other elements you have in the window. In code:
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
this.PreviewMouseMove += new MouseEventHandler(Window1_PreviewMouseMove);
}
void Window1_PreviewMouseMove(object sender, MouseEventArgs e) {
this.Title = e.GetPosition(this).ToString();
}
}

You will need to Capture the mouse this will allow your Canvas to continue to respond to the MouseMove Event, Try something like this it will update your coordinates as long as the Mouse is Pressed
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var canvas = new Canvas();
Content = canvas;
var element = new DrawingVisualElement();
canvas.Children.Add(element);
CompositionTarget.Rendering += (s, e) =>
{
using (var dc = element.visual.RenderOpen())
dc.DrawRectangle(Brushes.Black, null, new Rect(0, 0, 50, 50));
};
Mouse.Capture(canvas);
MouseDown += (s, e) => Mouse.Capture((UIElement)s);
MouseMove += (s, e) => Title = e.GetPosition(canvas).ToString();
MouseUp += (s, e) => Mouse.Capture(null);
}
Second Method
public MainWindow()
{
InitializeComponent();
var canvas = new Canvas();
Content = canvas;
DrawingVisualElement element = new DrawingVisualElement();
Grid myElement = new Grid();
canvas.Children.Add(myElement);
CompositionTarget.Rendering += (s, e) =>
{
using (var dc = element.visual.RenderOpen())
{
dc.DrawRectangle(Brushes.Black, null, new Rect(100, 0, 50, 50));
}
DrawingImage myImage = new DrawingImage(element.visual.Drawing);
myElement.Height = myImage.Height;
myElement.Width = myImage.Width;
myElement.Background = new ImageBrush(myImage);
};
MouseMove += (s, e) => Title = e.GetPosition(canvas).ToString();
}
Using a Hook be sure to put a using System.Windows.Interop;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var canvas = new Canvas();
Content = canvas;
var element = new DrawingVisualElement();
canvas.Children.Add(element);
CompositionTarget.Rendering += (s, e) =>
{
using (var dc = element.visual.RenderOpen())
{
dc.DrawRectangle(Brushes.Black, null, new Rect(0, 0, 50, 50));
}
};
this.SourceInitialized += new EventHandler(OnSourceInitialized);
}
void OnSourceInitialized(object sender, EventArgs e)
{
HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
source.AddHook(new HwndSourceHook(HandleMessages));
}
IntPtr HandleMessages(IntPtr hwnd, int msg,IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x200)
Title = Mouse.GetPosition(this).ToString(); // because I did not want to split the lParam into High/Low values for Position information
return IntPtr.Zero;
}
}

The answer is so much easier. All you need is to set element.IsHitTestVisible=false;

Related

Xamarin.Forms Java.Lang.NullPointerException when ContentPage has SizeChanged event listener

I'm getting the following error message when I try to overwrite a Xamarin.Forms ContentPage.Content.
I just get the message if the new ContentPage has a SizeChanged event listener.
Java.Lang.NullPointerException
Message=Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference
Code of the ContentPage I want to load
public class ShowSizePage : ContentPage
{
readonly Label label;
public ShowSizePage()
{
label = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Text = "initialized",
};
SizeChanged += OnPageSizeChanged;
Content = label;
}
void OnPageSizeChanged (object sender, EventArgs args)
{
label.Text = String.Format("{0} \u00D7 {1}", Width, Height);
}
}
Code from the caller of above ContentPage
// Part of an other ContentPage
void OnShowSizeClicked (object sender, EventArgs args)
{
ContentPage c = new ShowSizePage();
Content = c.Content; // Exception is caused here
}
I don't get the Exception if I call ShowSizePage directly from my MainPage.
What do I have to change to avoid the Exception?
Edit minimal reproducible example code:
using System;
using Xamarin.Forms;
namespace NP_Exception
{
public partial class App : Application
{
public App()
{
InitializeComponent();
// Change this to false to see that ShowSizePage works properly if not loaded through MenuPage
bool showMenu = true;
if (showMenu)
MainPage = new MenuPage(); // Throws exception
else
MainPage = new ShowSizePage(); // Works
}
}
public class MenuPage : ContentPage
{
readonly StackLayout menu = new StackLayout();
readonly ScrollView menuView = new ScrollView();
public MenuPage ()
{
Button showSizeButton = new Button { Text = "Show Size" };
showSizeButton.Clicked += OnShowSizeClicked;
menu.Children.Add(showSizeButton);
Button showLabelButton = new Button { Text = "Show Label" };
showLabelButton.Clicked += OnShowLabelClicked;
menu.Children.Add(showLabelButton);
menuView.Content = menu;
Content = menuView;
}
void OnShowSizeClicked(object sender, EventArgs args)
{
ContentPage c = new ShowSizePage();
Content = c.Content; // Exception is caused here
}
void OnShowLabelClicked(object sender, EventArgs args)
{
ContentPage c = new ShowLabelPage();
Content = c.Content;
}
protected override bool OnBackButtonPressed()
{
Content = menuView;
return true;
}
}
public class ShowSizePage : ContentPage
{
readonly Label label;
public ShowSizePage()
{
label = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Text = "initialized",
};
SizeChanged += OnPageSizeChanged;
Content = label;
}
void OnPageSizeChanged(object sender, EventArgs args)
{
label.Text = String.Format("{0} \u00D7 {1}", Width, Height);
}
}
public class ShowLabelPage : ContentPage {
public ShowLabelPage ()
{
Content = new Label { Text = "Hello World! Press hardware back button for menu." };
}
}
}
Update:
The exception occurs on Xamarin.Forms v4.4.0.991265 but seems to be fixed in v4.6.0.726
I found a solution to get rid of the Exception while getting the desired result.
I had to wrap the Page Content into a ContentView and bind the SizeChanged event to the ContentView
Working Code Sample:
public class ShowSizePage : ContentPage
{
readonly Label label;
public ShowSizePage()
{
label = new Label
{
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Text = "initialized",
};
// Wrap Content in ContentView to avoid Exception
ContentView view = new ContentView { Content = label };
// Bind SizeChanged event to view
view.SizeChanged += OnPageSizeChanged;
// Set View as Content
Content = view;
}
void OnPageSizeChanged(object sender, EventArgs args)
{
// Use Width & Height of the sender object
View view = (View)sender;
label.Text = String.Format("{0} \u00D7 {1}", view.Width, view.Height);
}
}
This issue has been fixed in the latest version of Xamrin.forms v4.6.0.726. Anyone who meet this issue with earlier version can update your Xamarin.Forms to solve it.

How to animate a custom shape object in c# WPF

I am relatively new to c# and WPF, maybe that’s why I cannot find the (probably very obvious) answer to my problem. I have been trying and googling but with no success.
I have a custom shape class that returns 3 RectangleGeometries in a GeometryGroup. The 3 corresponding rectangles can be displayed in a Canvas in MainWindow as expected. I would now like to animate each of the rectangles individually, say drop the first one to the bottom of the canvas, rotate the second one and animate the width of the third one.
My own research says the key are Dependency Properties. So I registered them but I couldn’t get them to do any changes on the rectangles.
Preferably, I would do all this in code behind. Only the Canvas
has been added in XAML. Can it be done? Here is some code to work with.
Thank you in advance
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Test1
{
public partial class MainWindow : Window
{
CustomShape customShape = new CustomShape();
public MainWindow()
{
InitializeComponent();
customShape.Fill = Brushes.Blue;
cnvMain.Children.Add(customShape);
}
}
class CustomShape : Shape
{
private Rect rect1, rect2, rect3;
private RectangleGeometry rg1, rg2, rg3;
private GeometryGroup allRectangleGeometries = new GeometryGroup();
//Constructor
public CustomShape()
{
makeCustomShape();
}
private void makeCustomShape()
{
rect1 = new Rect(50, 20, 100, 50);
rg1 = new RectangleGeometry(rect1);
allRectangleGeometries.Children.Add(rg1);
rect2 = new Rect(200, 20, 60, 20);
rg2 = new RectangleGeometry(rect2);
allRectangleGeometries.Children.Add(rg2);
rect3 = new Rect(300, 20, 200, 80);
rg3 = new RectangleGeometry(rect3);
allRectangleGeometries.Children.Add(rg3);
}
protected override Geometry DefiningGeometry
{
get
{
return allRectangleGeometries;
}
}
}
}
Looks like I found an answer myself.
I implemented 3 Dependency Properties and a Callback method that is executed every time a property changes.
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Test1
{
public partial class MainWindow : Window
{
CustomShape customShape = new CustomShape();
public MainWindow()
{
InitializeComponent();
customShape.Fill = Brushes.Blue;
cnvMain.Children.Add(customShape);
}
private void ButtonAnimate_Clicked(object sender, RoutedEventArgs e)
{
DoubleAnimation rec1Animation = new DoubleAnimation(500, TimeSpan.FromSeconds(1));
customShape.BeginAnimation(CustomShape.Rec1YProperty, rec1Animation);
DoubleAnimation rec2Animation = new DoubleAnimation(360, TimeSpan.FromSeconds(1));
customShape.BeginAnimation(CustomShape.Rec2RotateProperty, rec2Animation);
DoubleAnimation rec3Animation = new DoubleAnimation(400, TimeSpan.FromSeconds(1));
customShape.BeginAnimation(CustomShape.Rec3WidthProperty, rec3Animation);
}
}
class CustomShape : Shape
{
private Rect rect1, rect2, rect3;
private RectangleGeometry rg1, rg2, rg3;
private GeometryGroup allRectangleGeometries = new GeometryGroup();
public double Rec1Y
{
get { return (double)GetValue(Rec1YProperty); }
set { SetValue(Rec1YProperty, value); }
}
public static readonly DependencyProperty Rec1YProperty =
DependencyProperty.Register("Rec1Y", typeof(double), typeof(CustomShape), new PropertyMetadata(20d, new PropertyChangedCallback(OnAnyPropertyChanged)));
public double Rec2Rotate
{
get { return (double)GetValue(Rec2RotateProperty); }
set { SetValue(Rec2RotateProperty, value); }
}
public static readonly DependencyProperty Rec2RotateProperty =
DependencyProperty.Register("Rec2Rotate", typeof(double), typeof(CustomShape), new PropertyMetadata(0d, new PropertyChangedCallback(OnAnyPropertyChanged)));
public double Rec3Width
{
get { return (double)GetValue(Rec3WidthProperty); }
set { SetValue(Rec3WidthProperty, value); }
}
public static readonly DependencyProperty Rec3WidthProperty =
DependencyProperty.Register("Rec3Width", typeof(double), typeof(CustomShape), new PropertyMetadata(200d, new PropertyChangedCallback(OnAnyPropertyChanged)));
//Constructor
public CustomShape()
{
makeCustomShape();
}
private void makeCustomShape()
{
rect1 = new Rect(50, Rec1Y, 100, 50);
rg1 = new RectangleGeometry(rect1);
allRectangleGeometries.Children.Add(rg1);
rect2 = new Rect(200, 20, 60, 20);
rg2 = new RectangleGeometry(rect2);
rg2.Transform = new RotateTransform(Rec2Rotate, 230, 30);
allRectangleGeometries.Children.Add(rg2);
rect3 = new Rect(300, 20, Rec3Width, 80);
rg3 = new RectangleGeometry(rect3);
allRectangleGeometries.Children.Add(rg3);
}
private static void OnAnyPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
CustomShape customShape = source as CustomShape;
customShape.allRectangleGeometries.Children.Clear();
customShape.makeCustomShape();
}
protected override Geometry DefiningGeometry
{
get
{
return allRectangleGeometries;
}
}
}
}

How to update plot using Oxyplot in Windows Form

I am working on a project where I read some serial data from a board, and try to show it on a graph plot.
So far I have managed to implement the Oxyplot inside my application.
But I am confused how to update the plot on each new data coming from the serial port?
Here is my code in a simplified version
using OxyPlot;
namespace Motor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ComPort.DataReceived += new
System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
plot1.Model = GridLinesHorizontal();
}
public static PlotModel GridLinesHorizontal()
{
var plotModel = new PlotModel();
plotModel.Title = "Horizontal";
var linearAxis1 = new LinearAxis();
linearAxis1.MajorGridlineStyle = LineStyle.Solid;
linearAxis1.MinorGridlineStyle = LineStyle.Dot;
linearAxis1.Maximum = 5;
linearAxis1.Minimum = -5;
plotModel.Axes.Add(linearAxis1);
return plotModel;
}
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
InputData = ComPort.ReadLine();
if (InputData != String.Empty)
{
this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
}
}
private void SetText(string text)
{
dVal = double.Parse(text, CultureInfo.InvariantCulture); // convert to double
///// HERE I WANT TO UPDATE THE PLOT with dval
}
}
}
Not sure if that's the best way of doing it but something like this should work:
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
namespace Motor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ComPort.DataReceived += new
System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
plot1.Model = GridLinesHorizontal();
//create new LineSeries and add it to the PlotView
Line1 = new LineSeries
{
Title = "Test Series",
Color = OxyColors.Red,
TextColor = OxyColors.Red,
BrokenLineColor = OxyColors.Red
};
plot1.Model.Series.Add(Line1);
}
LineSeries Line1; // declare Line1 as global
public static PlotModel GridLinesHorizontal()
{
var plotModel = new PlotModel();
plotModel.Title = "Horizontal";
var linearAxis1 = new LinearAxis();
linearAxis1.MajorGridlineStyle = LineStyle.Solid;
linearAxis1.MinorGridlineStyle = LineStyle.Dot;
linearAxis1.Maximum = 5;
linearAxis1.Minimum = -5;
plotModel.Axes.Add(linearAxis1);
return plotModel;
}
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
InputData = ComPort.ReadLine();
if (InputData != String.Empty)
{
this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
}
}
int plotIndex = 0;
private void SetText(string text)
{
dVal = double.Parse(text, CultureInfo.InvariantCulture); // convert to double
///// plotIndex is the x value of the new point, not sure if OxyPlot offers an auto increment option
Line1.Points.Add(new DataPoint(plotIndex, dVal));
plotIndex++;
plot1.Invalidate();
}
}
}

Conflict in using SizeToContent=widthandHeight and WindowStartupLocation in WPF

We're stuck on an issue about the appropriate use of SizeToContent=WidthandHeight and WindowStartupLocation=CenterScreen in WPF. After resizing, our window has strange black border and it is not at the center.
The code does not work until MaxWith and MaxHeight is given.
To fix this, I used the RestoreBounds instead:
var previosWidth = this.RestoreBounds.Width;
var previosHeight = this.RestoreBounds.Height;
We have solved it with this class. You should use it instead of common Window.
public class CustomizableChromeWindow : Window, INotifyPropertyChanged
{
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
OnPropertyChanged("CaptionButtonMargin");
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
HandleSizeToContent();
}
private void HandleSizeToContent()
{
if (this.SizeToContent == SizeToContent.Manual)
return;
var previosTopXPosition = this.Left;
var previosTopYPosition = this.Top;
var previosWidth = this.MaxWidth;
var previosHeight = this.MaxHeight;
var previousWindowStartupLocation = this.WindowStartupLocation;
var previousSizeToContent = SizeToContent;
SizeToContent = SizeToContent.Manual;
Dispatcher.BeginInvoke(
DispatcherPriority.Loaded,
(Action)(() =>
{
this.SizeToContent = previousSizeToContent;
this.WindowStartupLocation = WindowStartupLocation.Manual;
this.Left = previosTopXPosition + (previosWidth - this.ActualWidth)/2;
this.Top = previosTopYPosition + (previosHeight - this.ActualHeight) / 2;
this.WindowStartupLocation = previousWindowStartupLocation;
}));
}
public Thickness CaptionButtonMargin
{
get
{
return new Thickness(0, 0, 0, 0);
}
}
#region INotifyPropertyChanged
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}

Gtk Expander in a scrolled window: The width of the scrolledwindow doesn't update

When using a Gtk ScrolledWindow with the horizontal Policy Never, I expect the scrolledwindow to adapt, if the horizontal size request of the child changes.
But that's not the case, for example when using an Expander: When I expand the expander, and the child is wider that the scrolled window, the scrolled window doesn't adapt it's size.
Minimum Example:
using System;
using Gtk;
namespace ExpanderIssue
{
class MainClass
{
static bool useExpander = true;
private static Widget createScrolledWindow(Widget child)
{
ScrolledWindow scrolledWindow = new ScrolledWindow();
scrolledWindow.SetPolicy(PolicyType.Never, PolicyType.Automatic);
scrolledWindow.AddWithViewport(child);
return scrolledWindow;
}
private static Widget createSingleExpaner(int index)
{
Button button = new Button(String.Format("ExampleButton {0}", index));
button.WidthRequest = 800;
if(useExpander) {
Expander expander = new Expander(String.Format("Expander {0}", index));
expander.Add(button);
return expander;
} else {
return button;
}
}
public static void Main(string[] args)
{
Application.Init();
createMainWindow();
Application.Run();
}
private static Window createMainWindow()
{
VBox vbox = new VBox();
vbox.Spacing = 4;
for(int i=0; i<32; ++i)
vbox.PackStart(createSingleExpaner(i), false, false, 0);
Window mainWindow = new Window("Expander Width Request issue");
mainWindow.SetDefaultSize(240, 160);
mainWindow.Add(createScrolledWindow(vbox));
mainWindow.ShowAll();
mainWindow.Destroyed += (sender, e) => Application.Quit();
return mainWindow;
}
}
}
Try to create the viewport for your scrollbar manually and adjust the Width Request from viewport when the SizeRequested Event from child is sent.
For your minimal example it should look like this:
using System;
using Gtk;
namespace ExpanderIssue
{
class MainClass
{
static bool useExpander = true;
private static Widget createScrolledWindow(Widget child)
{
ScrolledWindow scrolledWindow = new ScrolledWindow();
scrolledWindow.SetPolicy(PolicyType.Never, PolicyType.Automatic);
scrolledWindow.Add(doWorkaround(child));
return scrolledWindow;
}
private static Viewport doWorkaround(Widget child)
{
Viewport viewport = new Viewport();
viewport.Add(child);
child.SizeRequested += (o, args) =>
{
viewport.WidthRequest = viewport.Child.Requisition.Width;
};
return viewport;
}
private static Widget createSingleExpaner(int index)
{
Button button = new Button(String.Format("ExampleButton {0}", index));
button.WidthRequest = 800;
if(useExpander) {
Expander expander = new Expander(String.Format("Expander {0}", index));
expander.Add(button);
return expander;
} else {
return button;
}
}
public static void Main(string[] args)
{
Application.Init();
createMainWindow();
Application.Run();
}
private static Window createMainWindow()
{
VBox vbox = new VBox();
vbox.Spacing = 4;
for(int i=0; i<32; ++i)
vbox.PackStart(createSingleExpaner(i), false, false, 0);
Window mainWindow = new Window("Expander Width Request issue");
mainWindow.SetDefaultSize(240, 160);
mainWindow.Add(createScrolledWindow(vbox));
mainWindow.ShowAll();
mainWindow.Destroyed += (sender, e) => Application.Quit();
return mainWindow;
}
}
}

Categories