The default winforms Button control only draws itself in a "clicked state" when the user left clicks the button. I need the Button control to draw itself in the clicked state regardless of it was left clicked or right clicked. How would I accomplish this?
More specifically, I know I will need to derive a control from Button which extends the functionality, but I have no experience in extending functionality of winforms controls or drawing in GDI+. So I'm a little dumbfounded on what exactly I'll need to do once in there.
Thanks for the help.
Standard button control sets the button in down and pressed mode using a private SetFlag method. You can do it yourself too. I did it in following code:
using System.Windows.Forms;
public class MyButton : Button
{
protected override void OnMouseDown(MouseEventArgs e)
{
SetPushed(true);
base.OnMouseDown(e);
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
SetPushed(false);
base.OnMouseUp(e);
Invalidate();
}
private void SetPushed(bool value)
{
var setFlag = typeof(ButtonBase).GetMethod("SetFlag",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
if(setFlag != null)
{
setFlag.Invoke(this, new object[] { 2, value });
setFlag.Invoke(this, new object[] { 4, value });
}
}
}
Related
So I have built an application in C# using Winforms and my application uses a few different buttons. I'd like to have a highlight on the button that has been clicked to show what 'tab' you're in.
I've tried doing the following;
// BUTTONS //
private void dashboard_btn_Click(object sender, EventArgs e)
{
// Load Form
OpenChildForm(new FormDashboard());
dashboard_btn.FlatAppearance.BorderColor = Color.Red;
dashboard_btn.FlatAppearance.BorderSize = 1;
}
However, this of course doesn't work nicely since it adds a border around the button but when I click another button the border also stays around the previous button.
How would you implement a feature to add a border around the button that get's clicked but have the border disappear after you click another button?
Thank you for any feedback!
EDIT:
I've implemented Jimi's advice and used the Leave event to change the border around the button back to 0. However I'm not sure how to implement this in a global way so all my buttons are subscribed to this event.
My code now looks like this;
// BUTTONS //
private void dashboard_btn_Click(object sender, EventArgs e)
{
// Load Form
OpenChildForm(new FormDashboard());
// Button Highlight
dashboard_btn.FlatAppearance.BorderColor = Color.Red;
dashboard_btn.FlatAppearance.BorderSize = 1;
}
// BUTTON REMOVE HIGHLIGHT //
private void dashboard_btn_Leave(object sender, EventArgs e)
{
dashboard_btn.FlatAppearance.BorderSize = 0;
}
EDIT 2:
I ended up using Jimi's example and this worked for me :)
This might lend itself to a RadioButton style functionality because clicking a different radio button in the same container will uncheck the others. So, to implement the "generalized approach" that you mention in your comment, you could make a simple custom RadioButtonEx class where the Appearance property is set to Button then change your border style when the Checked property changes. In this example, the Click event has been changed to static so that clicking on any button directs the event to the common onAnyClick method.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
RadioButtonEx.Click += onAnyClick;
}
private void onAnyClick(object sender, EventArgs e)
{
label1.Text = ((RadioButtonEx)sender).Text;
}
}
public class RadioButtonEx : RadioButton
{
public static new event EventHandler Click;
public RadioButtonEx()
{
FlatAppearance.BorderColor = Color.Red;
FlatAppearance.BorderSize = 1;
Appearance = Appearance.Button;
}
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
if(Checked)
{
FlatStyle = FlatStyle.Flat;
Click?.Invoke(this, EventArgs.Empty);
}
else
{
FlatStyle = FlatStyle.Standard;
}
}
}
I've been trying to intercept the user going back from a page in my Xamarin.Forms UWP app, in order to either block it or present them with an "Are you sure?" dialog.
I've been able to remove the navigation bar back button using this in the constructor of the ContentPage:
NavigationPage.SetHasBackButton(this, false);
However, the back button on the mouse (XButton1) still causes the page to back.
I tried disabling it using this on the page:
protected override bool OnBackButtonPressed()
{
return true;
}
This would disable the hardware back button on something like Android, but it is not called at all when hitting the mouse back button.
I've also tried playing with the PointerPressed event on the UWP MainPage:
public MainPage()
{
this.InitializeComponent();
LoadApplication(new MyApp.App());
this.PointerPressed += MainPage_PointerPressed;
}
private void MainPage_PointerPressed(object sender, PointerRoutedEventArgs e)
{
PointerPoint currentPoint = e.GetCurrentPoint(this);
if (currentPoint.PointerDevice.PointerDeviceType == PointerDeviceType.Mouse)
{
PointerPointProperties pointerProperties = currentPoint.Properties;
if (pointerProperties.IsXButton1Pressed)
{
// back button pressed
}
}
}
This method is called correctly for all mouse inputs except for the XButton1 mouse back button if the app's current page is currently in a NavigationPage - almost like Xamarin.Forms is intercepting it somewhere along the way. Outside of a navigation page it picks up the XButton1 fine, and it always picks up every other input (including XButton2).
Is there a way to intercept or disable the XButton1 back function for a Xamarin.Forms UWP app?
Found a renderer workaround that allows you to handle the back button:
using Xamarin.Forms.Platform.UWP;
using Windows.UI.Xaml.Input;
using Windows.Devices.Input;
[assembly: ExportRenderer(typeof(Xamarin.Forms.Page), typeof(MyApp.UWP.Renderers.PageCustomRenderer))]
namespace MyApp.UWP.Renderers
{
public class PageCustomRenderer : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);
this.PointerPressed += PageCustomRenderer_PointerPressed;
}
private void PageCustomRenderer_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Handled) return;
var point = e.GetCurrentPoint(Control);
if (point == null || point.PointerDevice.PointerDeviceType != PointerDeviceType.Mouse) return;
if (point.Properties.IsXButton1Pressed)
{
e.Handled = true;
if (Element != null)
{
Element.SendBackButtonPressed();
}
}
}
}
}
You can then override OnBackButtonPressed on the page as in the OP to stop it (or remove the Element.SendBackButtonPressed() from the renderer above to disable it entirely).
I'm trying to make a launcher for a game that fixes some of its bugs.
Right now I'm just working on the interface and I want to make custom buttons, not just those generic squares, but I can't figure out how.
Here's some example images.
I just threw those buttons together quickly, but that's what I want.
I want the button to highlight when I mouse over it, without it being inside of the default square buttons.
This can be done with a custom drawn button. This demo from MSDN shows you how to override OnPaint and swap the bitmaps by responding to OnMouseDown and OnMouseUp. To get the image to change on hover instead, just swap the bitmaps during OnEnter and OnLeave.
Here's a cut-down example from the linked page:
public class PictureButton : Control
{
Image staticImage, hoverImage;
bool pressed = false;
// staticImage is the primary default button image
public Image staticImage
{
get {
return this.staticImage;
}
set {
this.staticImage = value;
}
}
// hoverImage is what appears when the mouse enters
public Image hoverImage
{
get {
return this.hoverImage;
}
set {
this.hoverImage = value;
}
}
protected override void OnEnter(EventArgs e)
{
this.pressed = true;
this.Invalidate();
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
this.pressed = false;
this.Invalidate();
base.OnLeave(e);
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.pressed && this.hoverImage != null)
e.Graphics.DrawImage(this.hoverImage, 0, 0);
else
e.Graphics.DrawImage(this.staticImage, 0, 0);
base.OnPaint(e);
}
}
I used a picture box, and then added in my button picture with a transparent background. Then added a click event, mouse enter, and mouse leave event.
I have a list view of items and I'd like it so that when the user right-clicks on one of the items it would bring up a contextmenustrip with a few options or tasks the user could perform, BUT I would like it to only bring the contextmenustrip when an item is r-clicked, as opposed to white space. Is there a setting for this?
You have to do this yourself. It's a bit of a pain. Basic flow is this...
Create a global boolean called contextMenuAllowed.
Subscribe to MouseDown event for the ListView. Do a HitTest on the ListView using the coordinates of the mouse (e.X and e.Y). If they hit on an item AND it was a right click, set contextMenuAllowed to true.
Subscribe to MouseUp event for the ListView. If it is the right mouse button, set contextMenuAllowed to false.
Subscribe to Opening event for the ContextMenu/ContextMenuStrip. If contextMenuAllowed is false, set e.Cancel to true and return. This is stop the context menu from actually opening.
It's a bit of a pain but easily done and the user will never know the difference.
Here is an example where I just made a new custom control that will do exactly what you are looking for:
using System;
using System.Windows.Forms;
public class CustomListView : ListView
{
private bool contextMenuAllowed = false;
public override ContextMenuStrip ContextMenuStrip
{
get
{
return base.ContextMenuStrip;
}
set
{
base.ContextMenuStrip = value;
base.ContextMenuStrip.Opening += ContextMenuStrip_Opening;
}
}
public CustomListView()
{
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ListViewHitTestInfo lvhti = HitTest(e.X, e.Y);
if (lvhti.Item != null)
{
contextMenuAllowed = true;
}
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
contextMenuAllowed = false;
}
base.OnMouseUp(e);
}
private void ContextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!contextMenuAllowed)
e.Cancel = true;
}
}
There is a very simple way to do this:
Assign the context menu to the ContextMenuStrip property of the ListView object (this can be done in the GUI settings)
Handle the Opening event of the context menu object and do a check inside the event handler, whether any item of the ListView object is selected. Cancel the event if this is not the case:
If myListView.SelectedItems.Count = 0 Then e.Cancel = True
I need to reinvent/recreate the Label Control from scratch to add my own mojoness to it. Yes, I know what you're thinking (and if you're not thinking that, shouldn't you be?).
Can somebody point me in the right direction?
Thank you.
The whole purpose for recreating the label is that I want full control over how it is drawn onto screen, and so that I can have KeyDown Event Handlers for it, too. For example, the user can edit the contents of a label the same way they would edit the contents of a TextBox control.
Also, I cannot just simply use a TextBox control, as it would require almost, if not more work to get my desired result.
Why not just extend the current one?
class MyMojoLabel : Label // Kind of thing
A good starting point would maybe to understand how Microsoft implemented the label.
To get a better in-depth look you should take a look with Reflector into the class or debugging the source code of the Label control.
public class MyLabel : System.Windows.Forms.Label
{
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
// or leave base out
// you would determine these values by the length of the text
e.Graphics.DrawEllipse(new System.Drawing.Pen(System.Drawing.Color.Red),
0, 0, 50, 12);
}
protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
{
base.OnKeyDown(e);
// although a label has a KeyDown event I don't know how it would
// receive focus, maybe you should create a text box that looks
// like a label
}
}
How about this?
Create a class that inherits from Control. Use the SetStyle() call to enable user painting and double-buffering, and override the OnPaint(), and any other methods that you need.
class MyLabel : System.Windows.Forms.Control
{
public MyLabel()
{
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder3D( e.Graphics, this.ClientRectangle, Border3DStyle.Etched, Border3DSide.All);
e.Graphics.DrawString(this.Text, this.Font, Brushes.Red, 0, 0);
}
}
Creating your own label control is simple enough, you just need to start with Control and override OnPaint(). What is going to byte is to turn it into a control that also has focusing behavior. And allows the user to edit the text. By the time you're done, you'll have re-invented the TextBox control. Which is a lot harder than it looks.
Focus on the focusing first, that's the trickiest problem. It isn't very likely that the user will want to focus the control frequently. Maybe some kind of secret handshake, like a double-click. When you detect one, you could create a TextBox control and put it in front of the label. And dispose it when it loses focus, updating the label's Text property. Or a simple context menu that displays a small editing dialog.
An example that uses the double-click to edit approach:
using System;
using System.Windows.Forms;
class MyLabel : Label {
private TextBox mEditor;
protected override void OnDoubleClick(EventArgs e) {
if (mEditor == null) {
mEditor = new TextBox();
mEditor.Location = this.Location;
mEditor.Width = this.Width;
mEditor.Font = this.Font;
mEditor.Text = this.Text;
mEditor.SelectionLength = this.Text.Length;
mEditor.Leave += new EventHandler(mEditor_Leave);
this.Parent.Controls.Add(mEditor);
this.Parent.Controls.SetChildIndex(mEditor, 0);
mEditor.Focus();
}
base.OnDoubleClick(e);
}
void mEditor_Leave(object sender, EventArgs e) {
this.Text = mEditor.Text;
mEditor.Dispose();
mEditor = null;
}
protected override void Dispose(bool disposing) {
if (disposing && mEditor != null) mEditor.Dispose();
base.Dispose(disposing);
}
}