I want to do something like this with windows forms:
Something like tags, but I only want label without colors and "x" value, I just want label with click event, how can I do it without using Telerik? it's not possible to do in datagrid view or something like that? Regards
Tag is a very common .Net property, so the question is bit unclear. But looking at the image and taking a wild guess on what you may want..:
If you want to have the ability to add Labels, let's call them TagLabels during runtime you may want to use a FlowLayoutPanel as their container. It will allow adding more and will take care of the layout no matter what sizes they have.
Example:
To create them we can use a TextBox, which we add to the FLP first. Then we code its PreviewKeyDown event and let the user create a new TagLabel by pressing enter..:
private void textBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Enter && textBox1.Text != "")
{
Label lbl = new Label {
Text = " " + textBox1.Text, /* some room for the image */
BorderStyle = BorderStyle.Fixed3D,
TextAlign = ContentAlignment.MiddleCenter,
AutoSize = true,
Margin = new Padding(2),
ImageIndex = flowLayoutPanel1.Controls.Count %
imageList1.Images.Count,
ImageList = imageList1,
ImageAlign = ContentAlignment.MiddleLeft,
MinimumSize = new Size(100, 20),
BackColor = Color.LightGoldenrodYellow,
Name = "TagLabel" + (flowLayoutPanel1.Controls.Count)
};
lbl.MouseClick +=lbl_MouseClick ;
flowLayoutPanel1.Controls.Add(lbl);
flowLayoutPanel1.Controls.SetChildIndex(lbl,
flowLayoutPanel1.Controls.Count - 2);
textBox1.Text = "";
}
else
if (e.KeyCode == Keys.Escape)
{
textBox1.Text = "";
}
}
The Click event should be generic for all labels; so we first cast sender to Label and can then code the processing..:
private void lbl_MouseClick(object sender, MouseEventArgs e)
{
Label lbl = sender as Label;
//...
MessageBox.Show(lbl.Name + " : Ouch! You clicked on " + lbl.Text.Trim());
}
This is just a basic piece of code. You can style the labels any way you want and of course also include code for deleting, maybe with a context menu. If those labels shall carry more responsibility, you can and should create a class, probably a Label subclass to hold further data and methods..
I'm also using an ImageList to display images to the left. Do change these details to suit your needs!
Note that there is no reasonable way to add the FLP to an ordinary DataGridView. You may be able to workaround but depending on your needs it may be best to keep them separate. DGVs have Cells and while these can hold special controls this is complicated and will always be restricted by the cells' i.e. the Columns' and the Rows' Size. As an alternative you can check out this example to see how you can insert virtual space to a row to hold a control but neither DGV nor its Cells are containers.
Related
I am using Combo box while creating Desktop Application using C# in Visual Studio, I increased the font size to "20" now when I run the application and click the dropdown button list elements font-size also increased.
That's fine. But, when I write something in the combo box it gives suggestions as shown in the picture below.
I, also want to increase the font size of this suggestion list, I have set "AutoCompleteMode" property set to "suggest". Is anybody can help me with this?
The font of the auto-complete text presented is fixed. There is no corresponding properties to set it.
A workaround is that you can create a custom control and replace the suggest-drop-down with custom listbox.
public Form1()
{
InitializeComponent();
comboBox1.Size = new Size(120, 30);
listBox.SelectedIndexChanged += ListBox_SelectedIndexChanged;
}
private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
{
comboBox1.Text = listBox.Text;
listBox.Visible = false;
}
ListBox listBox = new ListBox();
// event triggered when the user enters text
private void comboBox1_TextUpdate(object sender, EventArgs e)
{
// set the font of listbox to be consistent with combobox
listBox.Font = comboBox1.Font;
// add listbox below combobox
listBox.Location = new Point(comboBox1.Location.X, comboBox1.Location.Y + comboBox1.Height);
// filter suggest items
listBox.Items.Clear();
foreach (string item in comboBox1.Items)
{
if (item.StartsWith(comboBox1.Text) && comboBox1.Text != string.Empty)
{
listBox.Items.Add(item);
}
}
listBox.Visible = listBox.Items.Count > 0;
this.Controls.Add(listBox);
}
Test result:
Those are actually two areas which you can configure independently. According to this MSDN article,
To change settings:
Go to Tools – Options – Environment - Fonts and Colors
Under Show settings for: select either Statement Completion or Editor Tooltips (for Parameter Info and Quick Tips)
Change either the font or font size
I'm trying to find a a way to be able to essentially dynamically generate code based on an input.
For example I could type something like:
int Number = 22;
Button<Number>.Text = "X";
So in this case it would set button22 to have its text be an "X".
And I could change it so that I could input, for example 24 into the program and it would then set button24 to be an "X", instead of setting up a bunch of if statements to cover every potential button press.
For further context I have a Grid of 64 buttons and I need to be able to edit them individually to show to the user which buttons have been pressed, it is possible to do it with a lot of if statements but I thought it might be worth trying to find a more elegant solution.
You could have a list of buttons:
private List<Button> _buttons = new List<Button>();
Populate it like this:
for (int i = 0; i < 10; i++)
{
var b = new Button();
b.Text = $"Button #{i}";
b.Click += HandleButtonClick;
}
And you could even set an event handler on one of its events which doesn't even need to use the list (the sender is the source of the event):
private void HandleButtonClick(object sender, EventArgs e)
{
(sender as Button).Text = "X";
}
Buttons have a Tag property that can be used to hold arbitrary data about a button, this is described for WinForms, WPF and UWP.
Simple usage that is similar to OP's requirement is demonstrated in this SO post
This situation is in a practical sense the very reason that .Tag exists at all in user interface controls pretty much from the birth of c#.
So you do not need to use a custom class for a button, just simply assign your value to the .Tag property on the Button class that you are creating programmatically:
in this example a list is used to create the buttons and separate the creation from the layout, it is not necessary to do this, but may be useful. Instead, you could assign this button to it's parent container and/or set the layout margins or coordinates without keeping a reference to the Button object at all.
If OP updates the post to include implementation examples, we can update this response with more specific and complete code.
private List<Button> _buttons = new List<Button>();
// ... iteration or switching logic
var nextButton = new Button
{
Text = "x",
Tag = 22
};
nextButton.Click += DynamicButton_Click;
_buttons.Add(nextButton);
// ... later push the buttons into the parent container or bind to the UI
Then the button click handler you can access this Tag property:
this is presented from WinForms, the only difference in UWP or WPF is the method signature, change EventArgs to RoutedEventArgs
private void DynamicButton_Click(object sender, EventArgs e)
{
if(int.TryParse((sender as Button).Tag?.ToString(), out int buttonValue))
{
// use buttonValue
Console.Out.WriteLine(buttonValue);
}
else
{
// Otherwise, sender was not a button, or the button did not have an integer tag value
// either way, handle that error state here...
}
}
Using these concepts, once the buttons are created, let's say in some simple grid alignment, you could allow the user to set this Tag value at runtime if you have a TextBox (or other) input field that can be accessed from the code.
I recommend that you use MVVM style bindings for this rather than directly referencing a TextBox control, but this is simply to demonstrate the point.
private void DynamicButton_Click(object sender, EventArgs e)
{
// assign the string value from the ButtonValueTextbox control to this button
string value = this.ButtonValueTextBox.Text;
if(sender is Button button)
{
button.Tag = value;
}
else
{
// Otherwise, sender was not a button
// handle the error state here if you need to...
}
}
Now that each button has a tag, you could easily add logic to maintain unique tag values by iterating through the other buttons and clearing the tag if it was previously assigned to a different button.
Maybe you could keep a List of Button References:
var myButtons = new List<Button>();
myButtons.Add(firstButton);
myButtons.Add(secondButton);
// ... etc
// ... then somewhere else
int number = 3;
myButtons[number].Text = "xxx";
I have a List<Appointment> where an Appointment is.
public class Appointment
{
public string Title { get; set; }
public string Start { get; set; }
public string End { get; set; }
}
I want to dynamically add each list item on a separate line on the form like so:
item.Title + " between" + item.Start + " and " + item.End;
I want to be able to click each item (the text), then with each click it can toggle the colour of the text between red and black (that is, if black it turns red, if red it turns black when you click).
I come from a web background, but I am just struggling with Windows Forms data binding. I've tried with table layout panel but just don't know where to begin with changing the color of an item on click.
PS: If it helps, the number of items in the list will probably not be more than 10.
I've gotten a bit further as per Jamie Ide's comment:
var appts = GetAllCalendarItems();
foreach (var item in appts)
{
Label label = new Label();
label.Text = item.Title + " between" + item.Start + " and " + item.End;
label.Click += new EventHandler(label_Click);
flowLayoutPanel1.Controls.Add(label);
}
...
private void label_Click(object sender, EventArgs e)
{
// This is wrong - what goes here??
((Label)sender).ForeColor = Color.Red;
}
Dynamically laying out Windows Forms is a huge pain. I don't have time to code this but the steps are:
Add FlowLayoutPanel to form as a container
Look through your Appointments and create label controls for each
Add the label controls to the panel's Controls collection
Assign an OnClick handler to each label control to toggle the color
Don't bother with databinding for this.
If you haven't changed the label's initial color from the default, this will toggle it:
private void label1_Click(object sender, EventArgs e)
{
var lbl = (Label)sender;
var toggle = lbl.ForeColor == SystemColors.ControlText;
lbl.ForeColor = toggle ? Color.Red : SystemColors.ControlText;
}
You could add each text field as a member of a Label or List view item. Then handle the "OnClick" or "SelectedIndexChanged" event. To create an an OnClick event handler double click on the control in the design view. Edit the handler like this:
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
listView1.SelectedItems[0].ForeColor = Color.Red;
}
If you are unsure about event handlers, don't be put off they are quite easy, just look them up here perhaps. If the list view is not what you are looking for, try the same approach on a different control.
I guess I can't comment on Jamie's answer, but raklos you can programmatically add the OnClick method by doing:
label.Click += new EventHandler(label_Click);
Visual Studio should auto-generate the stubs for you when you start typing that out.
Something like this could get you started:
private void label_Click(object sender, EventArgs e) { ToggleTextColor((Label)sender); }
private void ToggleTextColor(Control control)
{
var currentColor = control.ForeColor;
control.ForeColor = currentColor == Color.Red ? Color.Black : Color.Red;
}
You can cheat and make create it in a WebBrowserControl.
You will be in familiar ground.
Use ObjectforScripting for WeBbrowser <=> winforms communication.
http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.objectforscripting.aspx
I have a windows form application with a bound datagridview in C#. I am finding a way to increase the tooltip duration for the cells of a particular column. The tooltip is being set in CellMouseEnter handler by setting cell.TooltipText property. From the research I did on the internet, it seems like I should have an external tooltip object to increase the delay. But for that, it looks like, ShowCellToolTips should be set to false. I need to have the ShowCellToolTips set to true since cell.ToolTipText is being set for other column cells in other various functions. Is there a way around for overriding the default cell.ToolTipText property for cells of a particular column and have the external Tooltip object display tooltip? The goal is to have ShowCellToolTips set to true and increase the delay for the tooltip or give user the control to close the tooltip. Is there a way to do this? Any help will be very much appreciated. Thanks.
You can use a regular ToolTip control with your DataGridView which will allow you to use the ToolTip's duration property to set the amount of time the ToolTip will be displayed.
Try this:
Add a ToolTip control to your form
Use your DGV's CellToolTipTextNeeded (or CellMouseEnter)
event to determine if you want to
display your ToolTip and if so call
the ToolTip's ToolTip.Show
method.
Here's an example:
private void dataGridView1_CellToolTipTextNeeded(object sender, DataGridViewCellToolTipTextNeededEventArgs e) {
if (e.ColumnIndex == 2) { // Display the tool tip only on DGV ColumnIndex 2.
Rectangle cellRect = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
toolTip1.Show("This is my ToolTip text",
this,
dataGridView1.Location.X + cellRect.X + cellRect.Size.Width,
dataGridView1.Location.Y + cellRect.Y + cellRect.Size.Height,
5000); // Duration: 5 seconds.
} else if (e.ColumnIndex >= 0 && e.RowIndex >= 0) {
toolTip1.Hide(this);
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].ToolTipText = " Hello from column: " + e.ColumnIndex.ToString();
}
}
Notice that we are not using the CellToolTipTextNeeded event to set the DataGridViewCell's ToolTipText property.
An advantage to using a ToolTip control is that you can customize the appearance and behavior of your ToolTip through the ToolTip control's properties.
I think that you can just turnoff the DataGridView.ShowToolTip and handle the MouseEnter event.
See this microsoft article on using tooltip:
http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.cellmouseenter.aspx
One of the problem I had with using JayRiggs's solution is that when the grid has a column that does not show the full text, there are two tooltip that are shown. One is the custom tooltip that was shown and the other is the default tooltip.
Probably someone still interested... I just made it like this, based on previous answer.
ToolTip toolTip = new ToolTip();
DGViewMain.CellMouseEnter += new DataGridViewCellEventHandler(DGViewMain_CellMouseEnter);
DGViewMain.CellMouseLeave += new DataGridViewCellEventHandler(DGViewMain_CellMouseLeave);
private void DGViewMain_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
Rectangle cellRect = DGViewMain.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
toolTip.Show("ToolTip message",
DGViewMain,
DGViewMain.Location.X + cellRect.X + cellRect.Size.Width,
DGViewMain.Location.Y + cellRect.Y + cellRect.Size.Height,
10000); // Duration: 10 seconds.
}
private void DGViewMain_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
Rectangle cellRect = DGViewMain.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
toolTip.Hide(DGViewMain);
}
Using winforms in vs2008. I have a DataGridView and I would like to detect when the vertical scroll bar is visible. What event should I register for?
I am adding the summing the each cell value in the last column of the grid and displaying that value in a textbox at the bottom of the DataGridView.
I would like this textbox to stay lined up with the cell values (I have made them right aligned since it is $$ values) even after the scroll bar is present.
var vScrollbar = dataGridView1.Controls.OfType<VScrollBar>().First();
if (vScrollbar.Visible)
{
}
Overriding DGV behavior is usually a huge pain in the neck. Got this going pretty quickly though. Add a new class to your form and paste the code shown below. Compile. Drop the new control from the top of the toolbar onto a form. Implement the ScrollbarVisibleChanged event.
using System;
using System.Windows.Forms;
class MyDgv : DataGridView {
public event EventHandler ScrollbarVisibleChanged;
public MyDgv() {
this.VerticalScrollBar.VisibleChanged += new EventHandler(VerticalScrollBar_VisibleChanged);
}
public bool VerticalScrollbarVisible {
get { return VerticalScrollBar.Visible; }
}
private void VerticalScrollBar_VisibleChanged(object sender, EventArgs e) {
EventHandler handler = ScrollbarVisibleChanged;
if (handler != null) handler(this, e);
}
}
Set the DGV last column's "AutoSizeMode" property to "Fill" and set the TextBox's Width property equal to dgv.Columns["lastcolumn"].Width.
Instead of using Linq (Adam Butler) you can just iterate through the controls and sign up an event handler that will be called each time the scrollbar visibility changes. I implemented it that way and it works pretty smoothly:
private System.Windows.Forms.DataGridView dgCounterValues;
private Int32 _DataGridViewScrollbarWidth;
// get vertical scrollbar visibility handler
foreach (Control c in dgCounterValues.Controls)
if (c.GetType().ToString().Contains("VScrollBar"))
{
c.VisibleChanged += c_VisibleChanged;
}
do this somewhere after the InitializeComponent()
In the handler, do whatever you need to do in response to the visibility change of the vertical scrollbar. Same works for the horizontal scrollbar (replace VScrollBar with HScrollBar):
void c_VisibleChanged(object sender, EventArgs e)
{
VScrollBar vb = sender as VScrollBar;
if (vb.Visible) _DataGridViewScrollbarWidth = vb.Width;
else _DataGridViewScrollbarWidth = 0;
}
I think there´s no event for that... but you can try with something like this in all the places where the grid can grow:
Obtain the number of actual rows in the gridview + the header
Multiply that number by the height of each row
If the result is greater than the height of the DataGrid, there must be a vertical scrollbar.
I gave Hans Passant the Check mark since he answered the question asked... However, I went another route for the solution. Since the dialog box is modal, the list of items will not change from the time it is created. So I am able to call the code below to ensure that the textboxes are in the correct location when the dialog first displays.
/// <summary>
/// Horizontally shifts the label and text boxes that display the total
/// values so that the totals remain aligned with the columns.
/// </summary>
private void ShiftTotalsDisplay(DataGridView grid, Label firstLabel,
TextBox secondTextBox, TextBox thirdTextBox)
{
//Note if you have a rowheader add the width here also.
int nameRightLoc = grid.Location.X +
grid.Columns[0].Width;
int fpRightLoc = nameRightLoc +
grid.Columns[0].DividerWidth +
grid.Columns[1].Width;
int dlRightLoc = fpRightLoc +
grid.Columns[1].DividerWidth +
grid.Columns[2].Width;
Point loc = firstLabel.Location;
loc.X = nameRightLoc - firstLabel.Width - 2;
firstLabel.Location = loc;
loc = secondTextBox.Location;
loc.X = fpRightLoc - secondTextBox.Width - 2;
secondTextBox.Location = loc;
loc = thirdTextBox.Location;
loc.X = dlRightLoc - thirdTextBox.Width - 2;
thirdTextBox.Location = loc;
}
If your dgv is inside a panel then you can compare panel and dgv height properties. If dgv's is bigger than panel's then there must be a scrollbar, right?
Like :
int panel_height = pnl.Height;
int dgv_height = (dgv.RowCount + 1) * 24; // You can play around with this 24 according to your cell styles
if (dgv_height > panel_height) MessageBox.Show("Voila!");