i am developing tab button as user control, how can i make it to behave as radiobutton. "When the user selects one option button (also known as a radio button) within a group, the others clear automatically". thank you.
Say your custom Tab button Control is named MyTabButton,
Override and implement Equals, and
then in the Click event handler in your custom control class,
if (this.Checked)
foreach(Control myBut in Parent.Controls)
if (myBut is MyTabButton && !myBut.Equals(this))
myBut.Checked = false;
If you want the behavior of radio buttons, use radio buttons.
Use javascript to hide the radio buttons and create your tab buttons in place of the original radio buttons. Feed the click events from your tab button to the original radio button. You might also want to have a common event to deselect the other tab buttons.
Your buttons will also degrade nicely if javascript is disabled.
Since you have mentioned you are using winforms, instead of using Javascript, you can override the paint method a derived RadioButton class to paint your Radiobutton as a tab. Here is a basic example
public class ButtonRadioButton : RadioButton {
protected override void OnPaint(PaintEventArgs e) {
PushButtonState state;
if (this.Checked)
state = PushButtonState.Pressed;
else
state = PushButtonState.Normal;
ButtonRenderer.DrawButton(e.Graphics, e.ClipRectangle, state);
}
}
obviously you need a container for the button, and whenever a usercontrol is selected, fire the event to container, and container deselect the other usercontrol
Need more information as to if this is WindowsForms, WPF, ASP.NET etc.. but
If it is WPF I have written a post that explains my approach to solving this problem:
Grouping and Checkboxes in WPF
Edited to included the answer
You can achieve it by overriding OnClick or OnMouseClick event. I don't get what you mean by "clearing a button", so I just change the Backcolor of it. You can easily adapt it to your Property or other needs.
using System;
using System.Linq;
using System.Windows.Forms;
namespace StackOverflow
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
}
public partial class MyRadioButton : Button
{
//Override OnClick event. - THIS IS WHERE ALL THE WORK IS DONE
protected override void OnClick(EventArgs e)
{
do
{
/*
This is where you select current MyRadioButton.
I'm changing the BackColor for simplicity.
*/
this.BackColor = System.Drawing.Color.Green;
/*
If parent of current MyRadioButton is null,
then it doesn't belong in a group.
*/
if (this.Parent == null)
break;
/*
Else loop through all other MyRadioButton of the same group and clear them.
Include System.Linq for this part.
*/
foreach (MyRadioButton button in this.Parent.Controls.OfType<MyRadioButton>())
{
//If button equals to current MyRadioButton, continue to the next RadioButton
if (button == this)
continue;
//This is where you clear other MyRadioButton
button.BackColor = System.Drawing.Color.Red;
}
}
while (false);
//Continue with the regular OnClick event.
base.OnClick(e);
}
}
}
Related
I have 20 iconbuttons in my form. when clickevent is performed i want to change buttons(backcolor,forecolor,Iconcolor) and remaining buttons should revert to it's default color.
public void btn1()
{
foreach (Control c in this.Controls)
{
if (c is Button)
{
(c as Button).ForeColor = Color.White;
(c as Button).BackColor = Color.FromArgb(46, 51, 73);
}
}
}
You can make each button listen to the same Click event and do some conditional logic to achieve this.
First, make each of your buttons reference the same click event. You can do this from the Form Designer by clicking the button and in the Properties window, click the lightening bolt icon (the events icon) and change the Click event to be the method you wish:
Then, make your event method do something like this:
public void ChangeButtonColors(object sender, EventArgs e)
{
//change the color of the clicked button to desired color
Button clickedButton = (System.Windows.Forms.Button)sender;
clickedButton.BackColor = Color.Red;
//change all other buttons to defualt color
foreach (Control c in this.Controls)
{
if (c is Button && c != clickedButton)
{
c.BackColor = Color.Green;
}
}
}
This gets the type of the sender (the clicked button in this case), and sets the BackColor property of it. Then, the foreach loop loops over all of the controls in the form, checks if the current item in the list is a button and is not the button that was clicked, it will change the BackColor property. Obviously, you can change other properties of both the clicked button, and the other buttons in the foreach.
If done properly, this is how it should behave:
This is what RadioButton is good for. Just set its Appearance to Button so it will look like a regular button but will act as a radio button: clicking it unchecks all the other buttons. The Checked property will be true for only the lastly clicked button:
I have a few radio buttons in my form that should all be connected, but I want one of them in a separate container to group it specifically with some other related inputs. Here is the general look:
Is there a way to group the fourth radio button with the other 3 even though it is in its own group box?
Fact that radio buttons are mutually exclusive only when they are within same parent. Being said that I can think of two options.
Let them be in same parent, handle the Paint event of the parent and draw yourself manually giving the impression that they are different groups.
You have to manually manage the mutual exclusion :(
RadioButtons are no magic, they just set the Checked property of other radio's to false behind the scenes.
private List<RadioButton> radioButtons = new List<RadioButton>();
//Populate this with radios interested, then call HookUpEvents and that should work
private void HookUpEvents()
{
foreach(var radio in radioButtons)
{
radio.CheckedChanged -= PerformMutualExclusion;
radio.CheckedChanged += PerformMutualExclusion;
}
}
private void PerformMutualExclusion(object sender, EventArgs e)
{
Radio senderRadio = (RadioButton)sender;
if(!senderRadio.Checked)
{
return;
}
foreach(var radio in radioButtons)
{
if(radio == sender || !radio.Checked)
{
continue;
}
radio.Checked = false;
}
}
They're in different containers, so you're not able to. I think this is a dupe of this question: Radiobuttons as a group in different panels
This question talks about it:
VB.NET Group Radio Buttons across different Panels
You can handle the CheckedChanged event of your radio buttons to accomplish what you need:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.radioButton1.CheckedChanged += new System.EventHandler(this.HandleCheckedChanged);
this.radioButton2.CheckedChanged += new System.EventHandler(this.HandleCheckedChanged);
this.radioButton3.CheckedChanged += new System.EventHandler(this.HandleCheckedChanged);
}
private bool changing = false;
private void HandleCheckedChanged(object sender, EventArgs e)
{
if (!changing)
{
changing = true;
if (sender == this.radioButton1)
{
this.radioButton2.Checked = !this.radioButton1.Checked;
this.radioButton3.Checked = !this.radioButton1.Checked;
}
else if (sender == this.radioButton2)
{
this.radioButton1.Checked = !this.radioButton2.Checked;
this.radioButton3.Checked = !this.radioButton2.Checked;
}
else if (sender == this.radioButton3)
{
this.radioButton1.Checked = !this.radioButton3.Checked;
this.radioButton2.Checked = !this.radioButton3.Checked;
}
changing = false;
}
}
}
All the radio buttons can be in the same parent container. You just need the radio button in question to draw over the top of the group box.
You can put all the radio buttons in the same parent container (group box would go into the parent as well), but configure the location and the z-order of the radio button in question so it's located over the group box and draws on top of it. That will give you the behaviour you want.
To do this in the designer, to get the radio button on top of the group box (you can't drag/drop it or the designer will put it in the group box), first add the group box, then add the radio button to the parent contjust drop it in the parent container, select it, then move it into position using the arrow keys on your keyboard.
Make sure to right click on it and "bring to front" and/or send the group box to the back, so they draw in the correct order.
** a more hacky way is just to edit the designer generated code - delete the line of code that adds the radio button to the group box's control collection, add line of code to add it to the parent container's control collection instead, and handle location and z-oder with similar lines of code.
I have set up UserControls in a FlowPanelLayout with the help in this question:
For Each DataTable Add UserControl to FlowLayoutPanel
I am now trying to implement a click event which allows me to put a border around the UserControl that has been selected. I have done this:
private void User_Load(object sender, EventArgs e)
{
flowlayoutpanelUsers.HorizontalScroll.Visible = false;
// Load and Sort Users DataTable
DataTable datatableUsers = UserMethods.GetUsers().Tables["Users"];
datatableUsers.DefaultView.Sort = "Name";
DataView dataviewUsers = datatableUsers.DefaultView;
// Loop Through Rows and Add UsersGrid to FlowLayoutPael
foreach (DataRowView datarowviewUsers in dataviewUsers)
{
var UsersGrid = new UsersGrid
{
Username = datarowviewUsers["Username"].ToString(),
User = datarowviewUsers["Name"].ToString(),
Admin = datarowviewUsers["Administrator"].ToString(),
};
flowlayoutpanelUsers.Controls.Add(UsersGrid);
UsersGrid.MouseClick += new MouseEventHandler(user_click);
}
}
private UsersGrid selectedUser;
void user_click(object sender, EventArgs e)
{
if (selectedUser != null)
selectedUser.BorderStyle = BorderStyle.None;
selectedUser = (UsersGrid)sender;
selectedUser.BorderStyle = BorderStyle.FixedSingle;
}
My issue is that it only works when I click in a white space in the UserControl but not when the user clicks on the two labels or image. How do I make it work for all child objects too?
Also, how can I then use the selected UserControl to do other things like open a form which shows all the details for that selected user?
I have a few suggestions for you. At the bottom of my response I included code that demonstrates my suggestions.
Suggestion 1: Fixing MouseClick in your UC
When you register the MouseClick event for a UserControl (UC) you're doing so for the UserControl itself, not for any controls that you place on the UserControl such as your Labels, etc. If you click one of these child controls the click won't be 'seen' by the underlying UC.
To fix this register the MouseClick event for all your child controls; you can even register the same MouseClick event handler you have for the UserControl itself.
Suggestion 2: Setting your UC's BorderStyle
I'd move your code for setting the UC's BorderStyle into the UC itself. Create public property IsSelected that's set to true when the UC is selected. In the property's setter update the UC's BorderStyle property depending on the value of the property.
Exposing an IsSelected property for your UC can be handy: you can query a group of these UCs to see which ones are selected rather than trying to track this status outside of the control like through a Form-level variable.
Edit in response to your comment:
Here's an example of how you might query the UCs in a FlowLayoutPanel to see if any are selected and if one is found how you might take some action. In this case the action is to call an EditUser method that takes as parameters values you get from properties in the selected UC:
var selectedUC = flowLayoutPanel.Controls.Cast<UserControl1>().FirstOrDefault(uc => uc.IsSelected);
if (selectedUC != null) {
// Use the properties of the UC found to be selected as parameters is method EditUser.
EditUser(selectedUC.Name, selectedUC.Username, selectedUC.Administrator);
}
Suggestion 3: Managing selection in a group of your UCs
If you want to unselect all UCs in a group except for the one that the user clicks (i.e. selects) you'll need to create an event in your UC that fires when a UC is clicked. The handler for this event explicitly sets IsSelected to false for all UCs in a set (such as in a container type control such as a Form, FlowLayoutPanel, etc.), the MouseClick handler in the UC that was clicked will then set the clicked UC's IsSelected to true.
It's worth considering creating another UserControl type that manages a group of your UCs. This new UserControl can encapsulate the code for the creation and state management of sets of your UC and would faciliate using your UCs in other projects as well as keeping the code of Forms hosting your UCs a bit cleaner.
I figured that rather than include a series of disjointed code snippets for each of my suggestions I'd include what I'm hoping is the minimum amount of code to allow you to reproduce what I'm talking about.
Create a new Visual Studio Winform project and use the following for class Form1:
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
flowLayoutPanel = new FlowLayoutPanel {
Dock = DockStyle.Fill,
};
this.Controls.Add(flowLayoutPanel);
// Add several sample UCs.
for (int i = 0; i < 10; i++) {
var uc = new UserControl1();
uc.WasClicked += UsersGrid_WasClicked;
flowLayoutPanel.Controls.Add(uc);
}
}
FlowLayoutPanel flowLayoutPanel;
// Event handler for when MouseClick is raised in a UserControl.
void UsersGrid_WasClicked(object sender, EventArgs e) {
// Set IsSelected for all UCs in the FlowLayoutPanel to false.
foreach (Control c in flowLayoutPanel.Controls) {
if (c is UserControl1) {
((UserControl1)c).IsSelected = false;
}
}
}
}
Next add a UserControl to the project. Keep the name UserControl1 and add a couple Labels and a PictureBox. Use this code for class UserControl1:
public partial class UserControl1 : UserControl
{
public UserControl1() {
InitializeComponent();
this.Load += UsersGrid_Load;
}
// Event fires when the MouseClick event fires for the UC or any of its child controls.
public event EventHandler<EventArgs> WasClicked;
private void UsersGrid_Load(object sender, EventArgs e) {
// Register the MouseClick event with the UC's surface.
this.MouseClick += Control_MouseClick;
// Register MouseClick with all child controls.
foreach (Control control in Controls) {
control.MouseClick += Control_MouseClick;
}
}
private void Control_MouseClick(object sender, MouseEventArgs e) {
var wasClicked = WasClicked;
if (wasClicked != null) {
WasClicked(this, EventArgs.Empty);
}
// Select this UC on click.
IsSelected = true;
}
private bool _isSelected;
public bool IsSelected {
get { return _isSelected; }
set {
_isSelected = value;
this.BorderStyle = IsSelected ? BorderStyle.Fixed3D : BorderStyle.None;
}
}
}
I know this is old, but I landed here looking for some pointers on how to do UC selection in a Container.
Jay's answer works well.
Just one update: The UsersGrid_Load method will only engage the top level controls, children of containers will not participate in the WasClicked event.
private void UsersGrid_Load(object sender, EventArgs e) {
// Register the MouseClick event with the UC's surface.
this.MouseClick += Control_MouseClick;
// Register MouseClick with all child controls.
RegisterMouseEvents(Controls);
}
Add recursive method RegisterMouseEvents to the UserControl
private void RegisterMouseEvents(ControlCollection controls)
{
foreach (Control control in controls)
{
// Subscribe the control to the
control.Click += Control_MouseClick;
if (control.HasChildren) RegisterMouseEvents(control.Controls);
}
}
You could possibly try subscribing to the GotFocus event on the UserControl.
Caveat:
Because this event uses bubbling routing, the element that receives focus might be a child element instead of the element where the event handler is actually attached. Check the Source in the event data to determine the actual element that gained focus.
UIElement.GotFocus Event
UPDATE
This question may be appropriate: Click event for .Net (Windows Forms) user control
please be patience to read this... briefly what i want is " when i
click on the DataGrid row those values have to be displayed in another
form(here userControl).."
I am using one Wpf Ribbon window.. And that window is divided in to two rows. In first row i filled with Wpf Ribbon and in Second row i filled one Dock panel to fill the Usercontrols based on the Action..
The Ribbon Has Two Button
Display All employees
To Add Employee
I created two Usercontrol Forms one is to enter the employee Details , second one is to Display all the Records in DataGrid..
Now i First Click on Ribbon Button1 to Display all the Employee Details ,then userControl "CtlGridDisplay "is loaded in to the DockPanel(which is in Ribbon Window) and displaying the all the Details in DataGrid.
Now i Double Click on row and take the selected row employeeId And then i wish to Display that Details in Another Usercontrol "CtlAddEmployee" And this userControl has to be Displayed in that DockPanel only.. for that i trying to Clear the Dockpanel and created the object for the Control "CtlAddEmployee" and added that control to the DockPanel.. But the DockPanel Remains fill with
DataGRid only i.e filled with "CtlGridDisplay" ... i can't understand why...
To do this i have also used the Events and Delegates ,it executes all the code well but the Dockpanel is not cleared and didn't loaded with new control..
But when i click one ribbon button1 it loads the "CtlGridDisplay" and when i click on Ribbon button2, its loading "CtlAddEmployee" in Dock panel well...
but when i trying to display the Data what i have click on DataGrid in one userControl to display in Another control doesn't Work..
Events Delegate approach Code is like below...
In User Control "CtlDisplayEmployeeList " the Code is
public event MyOrganizationDetails.MainRibbonWinodw.DisplayEmployeeHandler DisplayemployeeEvent;
private void GridList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
MainRibbonWinodw mainRibbonobj = new MainRibbonWinodw();
this.DisplayemployeeEvent += new MainRibbonWinodw.DisplayEmployeeHandler(mainRibbonobj.fnDisplayEmployeeDetails);
if (GridList.SelectionUnit == DataGridSelectionUnit.FullRow)
{
if (GridList.SelectedItems.Count > 0)
{
for (int i = 0; i < GridList.SelectedItems.Count; i++)
{
DataGrid dg = sender as DataGrid;
EmployeeDetail detail = dg.SelectedItem as EmployeeDetail;
string selectedEmp = detail.EmpId; //it gives Employee Id
if (DisplayemployeeEvent != null)
DisplayemployeeEvent(selectedEmp);
}
}
}
}
In Ribbon Window.. the Code will be... which handles the Event here...
public partial class MainRibbonWinodw : RibbonWindow
{
public delegate void DisplayEmployeeHandler(string str);
public void fnDisplayEmployeeDetails(string str)
{
CtlAddEmployee frm2 = new CtlAddEmployee(str);
DockPanelInRibbon.Children.Clear();
DockPanelInRibbon.Children.Add(frm2);
}
}
In another User Control..
Constructor...
public CtlAddEmployee(string str)
{
InitializeComponent();
fnDisplayingEmployee(str);
}
when i debugging all the Code is fired well but "CtlAddEmployee" form is not loaded to DockPanel... the Dock panel remains with that DataGRid... i Don't know why please tell me the solution .....
Thank you for your patience...to read this...
You must call event for dockpanel too. For example leftMouseButtonUp and there dock yours usercontrol.
I have a listbox that uses a UserControl as the item template. Inside the UserControl, I have an image (an X) that when clicked, sends out event to remove the UserControl(listitem) from the listbox.
Is there a way to prevent the listbox from selecting that item when a user clicks on the image but still allows listitem selection for everything else in the control?
Make sure you mark the event as handled when the user clicks the Image:
private void ImageClicked(object sender, RoutedEventArgs e)
{
//send out event to remove UserControl
//ensure the event doesn't bubble up further to the ListBoxItem
e.Handled = true;
}