Overridden c# TabControl OnClick is not called - c#

I'm using Microsoft Visual Studio 2013 and writing in c#. I have written a descendant of the TabControl class and overridden the OnClick method, then changed my existing TabControl element to use the new class. Everything compiles and runs, my breakpoint in the constructor is reached, but it's not using the OnClick override! Here's the TabControl descendant code, thanks in advance for any help!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LumaSense.Imaging.Calibration.UI
{
public partial class TabControlModified : TabControl
{
public TabControlModified()
{
InitializeComponent();
}
private bool superuser;
public int lastTabSelectedIndex = 0;
public bool Superuser
{
get { return superuser; }
set { superuser = value; }
}
public int LastTabSelectedIndex
{
get { return lastTabSelectedIndex; }
set
{ this.lastTabSelectedIndex = value; }
}
protected override void OnClick(EventArgs e)
{
// SelectedIndex and tab have already changed before we get here
if (this.superuser == false)
{
if (this.SelectedIndex <= this.lastTabSelectedIndex)
{
this.LastTabSelectedIndex = this.SelectedIndex;
base.OnClick(e);
}
else
{
base.OnClick(e);
this.SelectedIndex = lastTabSelectedIndex;
}
}
else
{
this.LastTabSelectedIndex = this.SelectedIndex;
base.OnClick(e);
}
}
}
}

This snippet works just fine. You need to make sure you have added pages to your tab control in order for the tabs to display and accessible to the user to click.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var mTab = new MyTab();
mTab.Location = new System.Drawing.Point(100, 100);
// The OnClick will only work on the
// Tabs themselves so Pages must be added to display the Tabs.
var mtabPage1 = new System.Windows.Forms.TabPage();
mTab.Controls.Add(mtabPage1);
this.Controls.Add(mTab);
}
class MyTab : TabControl
{
protected override void OnClick(EventArgs e)
{
MessageBox.Show("I was clicked.");
base.OnClick(e);
}
}
}

I found out that using Deselecting and Selecting instead of creating a descendent of TabControl fixed the problem. Somehow, when using the TabControl descendant, adding the Reactive UI binding of TabControlModified .SelectedIndex messed things up and the overridden OnClick was no longer callerd.

Related

How to disable tab headers in tabcontrol

I have a wizard to make a project, I make us e of a tabcontrol. I have buttons to go to the next tab or the previous. My problem now is that even thought there is validation on the buttons for required field, you can still switch between the tabs by clicking the tab headers. If I disable the tabcontrol, the user can't use whatever is inside the tabs either. Can I solve this?
UPDATE: all the code of the wizard form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorldEstablisher
{
public partial class ProjectWizard : Form
{
#region variables
public Form1 MainForm { get; set; }
#endregion
#region constructor and page load
public ProjectWizard(Form1 form)
{
InitializeComponent();
MainForm = form;
}
private void ProjectWizard_Load(object sender, EventArgs e)
{
}
#endregion
#region navigation
private void nextButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex == 0)//field validation tab 1
{
if (folderLocationTextBox.Text != "" && worldNameTextBox.Text != "")
{
backButton.Visible = true;
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 1)//field validation tab 2
{
if (authorTextBox.Text != "")
{
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 2)
{
finishButton.Visible = true;
}
}
private void backButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex != 0)
{
tabs.SelectedIndex = tabs.SelectedIndex - 1;
if (tabs.SelectedIndex == 0)//Make the back button invisible
{
backButton.Visible = false;
}
if (tabs.SelectedIndex != 2)//Make the finish button invisible
{
finishButton.Visible = false;
}
}
}
private void finishButton_Click(object sender, EventArgs e)
{
World world = new World("test");
MainForm.CurrentWorld = world;
this.Close();
}
#endregion
private void selectFolderButton_Click(object sender, EventArgs e)
{
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
folderLocationTextBox.Text = folderBrowser.SelectedPath;
}
}
}
}
There should be a better way to do this, but in the SelectedIndexChanged event for the TabControl you can set the SelectedTab to be whichever TabPage you want it to be. Track the current TabPage when the user clicks on your navigation buttons.
private TabPage currentTabPage;
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
tabControl1.SelectedTab = currentTabPage;
}
Since you'd be basically eliminating the built-in navigation of a TabControl, I'd rethink your design. Use a stack of Panel objects that you show or hide based on your button navigation mechanism. Don't confuse the user by showing them tab buttons that they can't ever use.

How to change user control label.text from a form

Right so I have a user control called "ModbusMaster" and a form with literally a single button on it..
When I click the button I want to change the text of a label on my control..
However nothing happens..
Here is the main form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ModbusMaster_2._0
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
ModbusMaster mb = new ModbusMaster();
public void button1_Click(object sender, EventArgs e)
{
mb.openPort("wooooo");
}
}
}
I am calling the method openPort and passing the string "wooo" to it..
here is my control
The text does not get updated :(:(:(
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace ModbusMaster_2._0
{
public partial class ModbusMaster : UserControl
{
string portName = "COM1"; //default portname
int timeOut = 300; //default timeout for response
SerialPort sp = new SerialPort();
public ModbusMaster()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
portLabel.Text = portName;
}
public void openPort(string port)
{
statusLabel.Text = port;
}
/*
* Properties
*/
public string SerialPort //Set portname
{
get { return portName; }
set { portName = value;}
}
public int TimeOut //Set response timeout
{
get { return timeOut; }
set { timeOut = value; }
}
}
}
I think you must have two instances of ModbusMaster.
One of them is the one you can see on the display, and is NOT being updated.
The other one is the one you create in class Form1 with the line of code:
ModbusMaster mb = new ModbusMaster();
That is the one you are modifying, but it isn't the displayed one (I cannot see anywhere that you can be displaying that).
What you need to do is use the reference to the actual displayed one instead when you call mb.openPort("wooooo");
[EDIT]
Thinking about it - it's possible that you haven't instantiated another user control at all.
Did you use Visual Studio's Form Designer to add the user control to your main form? I had assumed that you did, but now I realise that might not be the case.
If not, you should do that, give it the name mb and remove the line that says ModbusMaster mb = new ModbusMaster(); and it might work without you having to make more extensive changes.
You are creating your UserControl but not assigning it to your Form's Control Collection. Try something like this in your Constructor.
namespace ModbusMaster_2._0
{
public partial class Form1 : Form
{
ModbusMaster mb = new ModbusMaster();
public Form1()
{
InitializeComponent();
this.Controls.Add(mb); //Add your usercontrol to your forms control collection
}
public void button1_Click(object sender, EventArgs e)
{
mb.openPort("wooooo");
}
}
}

Windows Forms variables generated automatically

I'm following the example from "Programming C# 4.0, 6th edition" for working with Windows Forms, but I get to a point where I can't understand what actually happens. I have 3 files
One with Main() :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace ToDoList
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
one to work with the fields of the form:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class ToDoEntry
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime DueDate { get; set; }
}
And the form itself :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ToDoList
{
public partial class Form1 : Form
{
private BindingList<ToDoEntry> entries = new BindingList<ToDoEntry>();
public Form1()
{
InitializeComponent();
entriesSource.DataSource = entries;
CreateNewItem();
}
private void bindingSource1_CurrentChanged(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void CreateNewItem()
{
ToDoEntry newEntry = (ToDoEntry)entriesSource.AddNew();
newEntry.Title = "New entry";
newEntry.DueDate = DateTime.Now;
entriesSource.ResetCurrentItem();
}
private void entriesSource_ListChanged(object sender, ListChangedEventArgs e)
{
switch (e.ListChangedType)
{
case ListChangedType.ItemAdded:
MakeListViewItemForNewEntry(e.NewIndex);
break;
case ListChangedType.ItemDeleted:
RemoveListViewItem(e.NewIndex);
break;
case ListChangedType.ItemChanged:
UpdateListViewItem(e.NewIndex);
break;
}
}
private void MakeListViewItemForNewEntry(int newItemIndex)
{
ListViewItem item = new ListViewItem();
item.SubItems.Add("");
entriesListView.Items.Insert(newItemIndex, item);
}
private void UpdateListViewItem(int itemIndex)
{
ListViewItem item = entriesListView.Items[itemIndex];
ToDoEntry entry = entries[itemIndex];
item.SubItems[0].Text = entry.Title;
item.SubItems[1].Text = entry.DueDate.ToShortDateString();
}
private void RemoveListViewItem(int deletedItemIndex)
{
entriesListView.Items.RemoveAt(deletedItemIndex);
}
}
}
My problem is with saying that The name entriesListView does not exist in the current context. Which is true, but for example entriesSource.DataSource = entries;
I don't have entriesSource either but for some reason I can use it. Now I'm not sure if the missing class(at least I think it should be a class) is something that VS2010 (Yes I'm using Visual Studio 2010) should generate but then - why it hasn't.
Is something that I should write manually but then, there's nothing about this in the example and I too have no idea how to define entriesListView.
These variables are being created by Visual Studio in a designer file. This is done through the use of the partial keyword, which is a feature in C# that allows you to split a class definition into two or more files.
The designer file will be called Form1.designer.cs in this instance. You can see which controls have been created by opening this file, or, if using Visual Studio, by opening the form in the Visual Studio Designer. To open the designer file in Visual Studio, expand the Form1.cs entry in the Solution Explorer TreeView, and double-click the designer file.
In your code above, the problem appears to be that the entriesListView variable does not exist. It is possible that it was created at one point, and then deleted (this can even happen inadvertently due to Visual Studio bugs). Adding a new ListView to your form and setting the Name property to entriesListView should correct the problem.

Flashing ToolStripButton

I am using a ToolStrip with a number of ToolStripButtons.
What I would like is to be able to flash one of the buttons to get the user's attention.
For example, if they have made changes to information and need to click the Save button.
If this were a normal button I could do this using a Timer and periodically changing the BackColor however this doesn't work with a ToolStrip.
I could create a Renderer subclass and assign it to the ToolStrip but this appears to only get used in specific situations - i.e. it's event driven.
Does anyone have any ideas?
Well, just use a custom renderer so you can change the color of the button's background. With a timer that blinks it. Add a new class to your project and paste this code:
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
class BlinkingButtonRenderer : ToolStripProfessionalRenderer {
public BlinkingButtonRenderer(ToolStrip strip) {
this.strip = strip;
this.strip.Renderer = this;
this.strip.Disposed += new EventHandler(strip_Disposed);
this.blinkTimer = new Timer { Interval = 500 };
this.blinkTimer.Tick += delegate { blink = !blink; strip.Invalidate(); };
}
public void BlinkButton(ToolStripButton button, bool enable) {
if (!enable) blinkButtons.Remove(button);
else blinkButtons.Add(button);
blinkTimer.Enabled = blinkButtons.Count > 0;
strip.Invalidate();
}
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) {
var btn = e.Item as ToolStripButton;
if (blink && btn != null && blinkButtons.Contains(btn)) {
Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size);
e.Graphics.FillRectangle(Brushes.Black, bounds);
}
else base.OnRenderButtonBackground(e);
}
private void strip_Disposed(object sender, EventArgs e) {
blinkTimer.Dispose();
}
private List<ToolStripItem> blinkButtons = new List<ToolStripItem>();
private bool blink;
private Timer blinkTimer;
private ToolStrip strip;
}
Sample usage in a form with a Toolstrip containing a button:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
blinker = new BlinkingButtonRenderer(toolStrip1);
}
private void toolStripButton1_Click(object sender, EventArgs e) {
blink = !blink;
blinker.BlinkButton(toolStripButton1, blink);
}
private bool blink;
private BlinkingButtonRenderer blinker;
}

c# event handling between two forms

I have two forms and I am trying to capture an event generated from frmEventGenerate.cs in frmEventReceive.cs.
In this example I can receive the event from frmEventGenerate.cs but not sure how I can catch this in frmEventReceive.cs? frmEventReceive.cs is my startup form which creates frmEventGenerate.cs.
Can someone point me in the right direction, I think I am being stupid!
Thank you
frmEventGenerate.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Events
{
public delegate void LinkToEventHandler();
public partial class frmEventGenerate : Form
{
public static event LinkToEventHandler Evt;
public frmEventGenerate()
{
InitializeComponent();
Evt += new LinkToEventHandler(ReceiveEvent);
SendEvent();
}
public static void SendEvent()
{
if (Evt != null)
{
Evt();
}
}
public void ReceiveEvent()
{
System.Console.WriteLine("Received Event - This works ok");
}
}
}
frmEventReceive.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Events
{
public partial class frmEventReceive : Form
{
public frmEventReceive()
{
InitializeComponent();
frmEventGenerate frmGen = new frmEventGenerate();
}
public void ReceiveEvent()
{
System.Console.WriteLine("I want to be able to receive the even here!");
}
}
}
In your constructor, after instantiating frmEventGenerate:
frmGen.Evt += ReceiveEvent;
You don't need new LinkEventHandler(...) any more - as of C# 2, there's a method group conversion available which you can use to convert from a method group (the name of a method) to a delegate type.
EDIT: I hadn't seen that your event was static. That suggests you should actually use:
frmEventGenerate.Evt += ReceiveEvent;
... and you don't need the frmGen variable at all.
However, I would strongly discourage you from this - why do you want the event to be static in the first place? (I'd also urge you to name your types more sensibly - something like "EventGenerator" would be better here, for example. Ignoring the convention that type names should be in Pascal case leads to confusing code.)
//Receiver
using System;
using System.Windows.Forms;
namespace eTest
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public void ReceiveEvent(int i)
{
MessageBox.Show("Event Received from Form: " + i.ToString());
}
private void btnNew_Click(object sender, EventArgs e)
{
int num = 0;
int x = 0;
num = Convert.ToInt32(txtForms.Text);
for (x = 0; x < num; x++)
{
frmDL f = new frmDL();
f.Evt += ReceiveEvent;
f.iID = x;
f.Text = x.ToString();
f.Show();
f.Activate();
Application.DoEvents();
}
}
}
}
//Sender
using System;
using System.Windows.Forms;
namespace eTest
{
public delegate void myEventHandler(int i);
public partial class frmDL : Form
{
public event myEventHandler Evt;
public int iID = 0;
public frmDL()
{
InitializeComponent();
}
public void SendEvent()
{
if (Evt != null)
{
Evt(this.iID);
}
}
private void btnEvent_Click(object sender, EventArgs e)
{
SendEvent();
}
}
}

Categories

Resources