Here and there I've seen nicely styled dialog controls, similar to this one which appears in Beyond Compare v4:
My own implementation of this gets vaguely close, and consists of a listbox on the left and usercontrols which change when the listbox selected item changes. However no amount of putting lipstick on that pig will get it looking like the above. I can picture how I might undertake this with custom painting of things and so forth, but my real intent is to generate the left hand entries at runtime not design time (there will be one for each column in a datafile).
I was wondering if anyone had any ideas on how to reasonably easily implement such a thing, either with a component (commercial is fine) or some other ingenious method.
Thanks!
Here is an example of mimicking your design. I have one large panel1 to house both sides and in it one panel2 to house the left side. Inside panel2 there are the search controls and the listview.
The search controls are a Label1 containing a TextBox and another Label2. The Label.Images are aligned and the Textbox has no Border. The Labels are AutoSize=false and Label1 has a 3D-border.
Panel1 has a singleLine border, panel2 and the ListView have no borders. The ListView has View=Details and one column, HeaderStyle=None. It also has OwnerDraw=true.
I have added a Paint event for the ListView but have to call it in code.
Please note that I haven't taken the time to create nice images. Also note: Their height will determine the Items' Height (!) so leave a little transparent border above and below; their good looks will be key to the overall looks!
They are contained in an Imagelist with appropriate Size and BitDepth. You may need to adapt the DrawIamge numbers..
The stuff on the right side is pretty much standard; for the horizontal bar I use a Panel with height=1 and Border=Single. If you have more than one group of RadioButtons make sure to put each group in a separate Panel, transparent and no Borders, of course..
public Form1()
{
InitializeComponent();
listView1.Width = panel2.ClientSize.Width;
listView1.Columns[0].Width = listView1.ClientSize.Width;
listView1.Paint += listView1_Paint;
listView1.BackColor = panel2.BackColor;
leftBrush = new SolidBrush(panel2.BackColor);
rightBrush = new SolidBrush(panel1.BackColor);
}
Pen borderPen = new Pen(SystemColors.ActiveBorder);
SolidBrush leftBrush, rightBrush;
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (e.ItemIndex == 0) listView1_Paint(
null, new PaintEventArgs(e.Graphics, listView1.ClientRectangle));
if (!e.Item.Selected)
{
e.Graphics.FillRectangle(leftBrush, e.Bounds);
e.Graphics.DrawLine(borderPen,
listView1.Width-1, e.Bounds.Y, listView1.Width-1, e.Bounds.Bottom);
}
else
{
e.Graphics.FillRectangle(rightBrush , e.Bounds);
e.Graphics.DrawLine(borderPen, 0, e.Bounds.Top, e.Bounds.Width, e.Bounds.Top);
e.Graphics.DrawLine(borderPen,
0, e.Bounds.Bottom-1, e.Bounds.Width, e.Bounds.Bottom-1);
}
e.Graphics.DrawString( e.Item.Text, listView1.Font,
Brushes.Black, 35, e.Bounds.Y + 5 );
e.Graphics.DrawImage(imageList1.Images[e.Item.ImageIndex], 2, e.Bounds.Y );
}
void listView1_Paint(object sender, PaintEventArgs e)
{
int hh = listView1.Items.Count * imageList1.ImageSize.Height;
e.Graphics.DrawLine(borderPen,
listView1.Width - 1, hh, listView1.Width - 1, listView1.Height);
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(borderPen, panel2.Width-1, 0, panel2.Width-1, listView1.Top);
}
}
Here is a screenshot of my Q&D version:
Related
I have created a ListBox to which I add elements during code compilation. and I want to record its color when adding one element (so that each added element has a different color)
listBox1.Items.Add(string.Format("Місце {0} | В роботі з {1} | ({2} хв)", temp[7].Substring(6, 4), temp[8].Substring(11, 5), rezult)); `
I tried everywhere possible to embed this change
BackColor = Color.Yellow;ForeColor = Color.Yellow;
I am working with listbox because I have seen so many answers about ListView.
Set the listbox DrawMode to either OwnerDrawFixed or OwnerDrawVariable and set this as the DrawItem event handler:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e){
if(e.Index == 1) e.DrawBackground(); //use e.Index to see if we want to highlight the item
else e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), e.Bounds); //This does the same thing as e.DrawBackground but with a custom color
e.DrawFocusRectangle();
if(e.Index < 0) return;
TextRenderer.DrawText(e.Graphics, (string)listBox1.Items[e.Index], listBox1.Font, e.Bounds, listBox1.ForeColor, TextFormatFlags.Left);
}
Well, best idea I have is to not use list box, but flowLayoutPanel and add usercontrols where you will have labels.
flowLayoutPanel works as list of controls which you can scroll, so we will just create a usercontrol, where we will put label and change the usercontrol background
Don't forget to turn on the AutoScroll feature to flowLayoutPanel, otherwise the scroll bar wont work and wont even show up.
If you want to be able to be clickable just add to the label click event.
public void CreateItem(Color OurColor, string TextToShow)
{
Label OurText = new Label()
{
Text = "TextToShow",
Font = new Font("Segoe UI", 8f),
Location = new Point(0, 0),
AutoSize = true,
};
UserControl OurUserControl = new UserControl();
OurUserControl.Size = new Size((int)((double)flowLayoutPanel1.Width * 0.9) , OurText.Font.Height);
OurUserControl.BackColor = OurColor;
OurUserControl.Controls.Add(OurText);
flowLayoutPanel1.Controls.Add(OurUserControl);
}
I use a WPF to display a shape on an Office panel.
I can size the shape when application is loading. But I would like to resize this shape if the user resize the panel containing the shape.
My problem is margin defined at the beginning is not changed after loading, so the size keep its initial margin.
I have a UserControl containing the WPF Shape and the resize event handler:
private void UserControlA_Resize(object sender, EventArgs e)
{
myWPF.SetSizeOfShape(sizeOfPanel); // I collect sizeOfPanel, and it is OK, it is changed when panel is resized
}
xaml of the WPF is:
<Border Name="myShape" Background="blue" CornerRadius="8" Margin="10,126,139,199" />
the WPF .cs is:
public void SetSizeOfShape(int widthOfPanel)
{
myShape.Margin = new Thickness(widthOfPanel/3, 100, widthOfPanel/6, 100);
SetSizeOfShape is called when the application is loading and the size is correctly set - but if size is changed, it is called again, but doesn't change the margin displayed.
Do you know what is wrong and how to correct it ?
----- EDIT -----
There is probably a problem with my event handler. Indeed, if I put the event with a button click, it works - but if I use the Resize (or sizeChanged) event it doesn't : the event is called, but there is no effect on the shape. Do you know how to solve it ?
I don't see a Resize event for the Border Control. It can size to its contents. Maybe give us more detail about what you're trying to do.
public void MyMouseOver()
{
Ellipse myShape = new Ellipse() { Width = 200, Height = 100, Stroke = Brushes.Yellow, };
MyCanvas.Children.Add(myShape);
Canvas.SetTop(myShape,10);
Canvas.SetLeft(myShape,10);
myShape.MouseEnter += MyShape_MouseEnter;
myShape.MouseLeave += MyShape_MouseLeave;
}
private void MyShape_MouseLeave(object sender, MouseEventArgs e)
{
((Ellipse)sender).RenderTransform = new ScaleTransform(1, 1); // return scale to normal
}
private void MyShape_MouseEnter(object sender, MouseEventArgs e)
{
((Ellipse)sender).RenderTransform = new ScaleTransform(1.1, 1.1, Width / 2, Height / 2);
}
I am setting the ForeColor of all items in my ListView to a different color, but this get's overrided when the item is selected (changes to Black again; changes back to custom color on deselection).
I want my items to retain my custom color, even in selection.
I'm basically asking the same question that was asked here 7 years ago, and doesn't seem to have any satisfactory answer.
I tried searching in SO and elsewhere, and no luck. The only solution provided so far is to draw the whole thing (the DrawItem method), which I gave a try but is ridiculously complicated for such a petty requirement...
Is this the only way? Say it ain't so.
Enable your ListView OwnerDraw mode, then subscribe its DrawItem and DrawColumnHeader events.
If your design requires it, also subcribe the DrawSubitem event.
At this point, you can draw anything in the related areas of your ListView.
In the example, I've painted a little symbol in the Header area.
The Header text needs to be painted too.
If the Background color doesn't change (same as in design mode), you just need to use the DrawListViewItemEventArgs e parameter function e.DrawBackground();
If not, use e.Graphics.FillRectangle() to color the Item area, defined by e.Bounds.
The Item Text is drawn using e.Graphics.DrawString().
The item Text is e.Item.Text, the text area is defined by e.Bounds again.
If you don't need any specific details/settings for the item's text, you can simply use e.DrawText();, which uses the default properties (defined at design-time).
Here, the item color complex logic is that the color is specified inside the item text. Could be anything else. The item tag, its Index position, a List<Parameters>, you name it.
This is how it might look like:
(I added e.Graphics.TextRenderingHint = [] to show how you can control the quality of the rendered text. e.Graphics.TextContrast can be also used to enhance the contrast).
Note: this code sample only draws a generic image, if the ListView has an ImageList. You should also verify whether the SmallIcon/LargeIcon ImageLists are defined and draw the related Image in the specified size. It's the same procedure, though.
protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.Item.UseItemStyleForSubItems = true;
int imageOffset = 0;
Rectangle rect = e.Item.Bounds;
bool drawImage = !(e.Item.ImageList is null);
Color itemColor = Color.FromName(e.Item.Text.Substring(e.Item.Text.LastIndexOf(" ") + 1));
using (var format = new StringFormat(StringFormatFlags.FitBlackBox)) {
format.LineAlignment = StringAlignment.Center;
if (drawImage) {
imageOffset = e.Item.ImageList.ImageSize.Width + 1;
rect.Location = new Point(e.Bounds.X + imageOffset, e.Item.Bounds.Y);
rect.Size = new Size(e.Bounds.Width - imageOffset, e.Item.Bounds.Height);
e.Graphics.DrawImage(e.Item.ImageList.Images[e.Item.ImageIndex], e.Bounds.Location);
}
if (e.Item.Selected) {
using (var bkgrBrush = new SolidBrush(itemColor))
using (var foreBrush = new SolidBrush(e.Item.BackColor)) {
e.Graphics.FillRectangle(bkgrBrush, rect);
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
e.DrawFocusRectangle();
}
else {
//e.DrawDefault = true;
using (var foreBrush = new SolidBrush(itemColor)) {
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
}
}
}
// Draws small symbol in the Header beside the normal Text
protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawBackground();
string extra = (e.ColumnIndex == 1) ? (char)32 + "\u2660" + (char)32 : (char)32 + "\u2663" + (char)32;
using (var brush = new SolidBrush(e.ForeColor)) {
e.Graphics.DrawString(extra + e.Header.Text, e.Font, brush, e.Bounds, StringFormat.GenericTypographic);
}
}
I'm not entirely sure this is possible using C# and Windows Forms, but here goes:
I want to split my window into two. On the left is a series of buttons arranged vertically.
On clicking each button, the stuff on the right side has to change. I want different controls displaying for different buttons clicks.
(Kind of like web browser tabs, just on the left pane instead of on top)
I'm guessing I have to use split container, but most of the split container tutorials I've seen have them using the same control on the right, just with different data displaying every time something is clicked.
I hope I'm being clear with what I need. Any help or pointers would be greatly appreciated.
You can get the job done with a TabControl, as explained on MSDN. It shows an example on how to put the tabs on the right side but switching "right" with "left" should provide the behavior you seek.
I tried it in a WinForms app and it seems to work fine, despite the ugly colors used in the example... The following takes ~30 secs to implement.
Quoting from the link:
To display right-aligned tabs
Add a TabControl to your form.
Set the Alignment property to Right. if you set Left it goes on the left no problem
Set the SizeMode property to Fixed, so that all tabs are the same width.
Set the ItemSize property to the preferred fixed size for the tabs. Keep in mind that the ItemSize property behaves as though the tabs were on top, although they are right-aligned. As a result, in order to make the tabs wider, you must change the Height property, and in order to make them taller, you must change the Width property.
Set the DrawMode property to OwnerDrawFixed.
Define a handler for the DrawItem event of TabControl that renders the text from left to right.
And this is the code (converted to C# since MSDN only shows VB):
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _TextBrush = default(Brush);
// Get the item from the collection.
TabPage _TabPage = tabControl1.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _TabBounds = tabControl1.GetTabRect(e.Index);
if ((e.State == DrawItemState.Selected))
{
// Draw a different background color, and don't paint a focus rectangle.
_TextBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.Gray, e.Bounds);
}
else
{
_TextBrush = new System.Drawing.SolidBrush(e.ForeColor);
e.DrawBackground();
}
// Use our own font.
Font _TabFont = new Font("Arial", 10, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _StringFlags = new StringFormat();
_StringFlags.Alignment = StringAlignment.Center;
_StringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_TabPage.Text, _TabFont, _TextBrush, _TabBounds, new StringFormat(_StringFlags));
}
Just put your content in the TabControl and you're (sorta) good to go.
It is possible and one way is to create new user controls for each of the right-hand interfaces. When you click a button it can load a new instance of one of these controls into the right-hand panel:
if (splitContainer.Panel2.Controls.Count > 0)
{
splitContainer.Panel2.Controls[0].Dispose(); // Edit
splitContainer.Panel2.Controls.Clear();
}
splitContainer.Panel2.Controls.Add(new RightHandControl());
This seems to work when I have tried in the past.
EDIT: As has been pointed out in the comments, this example does not properly dispose of the user controls when cleared! To solve this, call Dispose() prior to clearing the list.
The exact layout and the controls you need will be up to you.This is meant as an simplistic starting point to make you see the options you have(here of course 1 option is provided):
1) add splitcontainer to the form.
2) add flowLayoutPanel to left panel of splitcontainer and dock in parent container
3) since you asked for vertical layout set FlowDirection on the
flowLayoutPanel to topdown
4) in my example i used 6 buttons and named them(instead could be
their text) btncontrol1,2,3...so on, do the same.
5) set all buttons click handler to the same(in this case
buttons_Click)
6) copy this code and paste
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttons_Click(object sender, EventArgs e)
{
Button b = sender as Button;
switch (b.Name)
{
//of course after each ClearPanel what i do is only for
//demonstration.
case "btnControl1":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new ListBox());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl2":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new RadioButton());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl3":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new Button());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl4":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new DateTimePicker());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl5":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new DataGridView());
splitContainer1.Panel2.ResumeLayout();
break;
case "btnControl6":
splitContainer1.Panel2.SuspendLayout();
ClearPanel();
splitContainer1.Panel2.Controls.Add(new TextBox());
splitContainer1.Panel2.ResumeLayout();
break;
default:
break;
}
}
private void ClearPanel()
{
if (splitContainer1.Panel2.HasChildren)
{
foreach (Control c in splitContainer1.Panel2.Controls)
{
c.Dispose();
}
splitContainer1.Panel2.Controls.Clear();
}
}
}
Hope it help you.
I know that similar questions have already been asked here before, but they all lead to the same codeproject article that doesn't work. Does anybody know of a working ListBox with icons?
Will a ListView work for you? That is what I use. Much easier and you can make it look just like a ListBox. Also, plenty of documentation on MSDN to get started with.
How to: Display Icons for the Windows Forms ListView Control
The Windows Forms ListView control can display icons from three image
lists. The List, Details, and SmallIcon views display images from the
image list specified in the SmallImageList property. The LargeIcon
view displays images from the image list specified in the
LargeImageList property. A list view can also display an additional
set of icons, set in the StateImageList property, next to the large or
small icons. For more information about image lists, see ImageList
Component (Windows Forms) and How to: Add or Remove Images with the
Windows Forms ImageList Component.
Inserted from How to: Display Icons for the Windows Forms ListView Control
If you don't want to change ListBox to a ListView you can write a handler for DrawItemEvent. for example:
private void InitializeComponent()
{
...
this.listBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.listBox_DrawItem);
...
}
private void listBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1)
return;
// Draw the background of the ListBox control for each item.
e.DrawBackground();
var rect = new Rectangle(e.Bounds.X+10, e.Bounds.Y+8, 12, 14);
//assuming the icon is already added to project resources
e.Graphics.DrawIconUnstretched(YourProject.Properties.Resources.YouIcon, rect);
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
e.Font, Brushes.Black, new Rectangle(e.Bounds.X + 25, e.Bounds.Y + 10, e.Bounds.Width, e.Bounds.Height), StringFormat.GenericDefault);
// If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle();
}
you can play around with the rectangle to set the location of the icon right
If you're stuck working in WinForms, then you'll have to owner-draw your items.
See the example for the DrawItem event.
A little different approach - don't use a list box.
Instead of using that control that bounds me to its limited set of properties and methods I am making a listbox of my own.
It's not as hard as it sounds:
int yPos = 0;
Panel myListBox = new Panel();
foreach (Object object in YourObjectList)
{
Panel line = new Panel();
line.Location = new Point(0, Ypos);
line.Size = new Size(myListBox.Width, 20);
line.MouseClick += new MouseEventHandler(line_MouseClick);
myListBox.Controls.Add(line);
// Add and arrange the controls you want in the line
yPos += line.Height;
}
Example for myListBox event handlers - selecting a line:
private void line_MouseClick(object sender, MouseEventArgs)
{
foreach (Control control in myListBox.Controls)
if (control is Panel)
if (control == sender)
control.BackColor = Color.DarkBlue;
else
control.BackColor = Color.Transparent;
}
The code samples above were not tested but the described method was used and found very convenient and simple.