OwnerDraw ComboBox with VisualStyles - c#

I have a ComboBox that I have set DrawMode = DrawMode.OwnerDrawFixed. Then I handle the OnDrawItem event and everything works perfectly. However, it looks very different from a standard ComboBox because mine doesn't seem to be rendered using VisualStyles. Do I need to do something to specifically enable VisualStyle rendering for my owner drawn control? I have tried SetWindowTheme on my control, but I'm not sure what theme class to send. Any help would be much appreciated. Thanks!

The down side of owner-draw is that when you turn it on, the owner (you) has to draw everything. You are almost completely on your own.
If you want visual styles, then you have to call the VisualStyles APIs directly to do what you want. If you want to show selected, focussed, enabled/disabled states, then you have to write code to deal with them all.
This isn't a direct answer for your combo-box issues, but as an example of how to use VisualStyles, here is how I've used VisualStyles in an owner-drawn TreeView to draw the Plus/Minus icon:
// Draw Expand (plus/minus) icon if required
if (ShowPlusMinus && e.Node.Nodes.Count > 0)
{
// Use the VisualStyles renderer to use the proper OS-defined glyphs
Rectangle expandRect = new Rectangle(iconLeft-1, midY - 7, 16, 16);
VisualStyleElement element = (e.Node.IsExpanded) ? VisualStyleElement.TreeView.Glyph.Opened
: VisualStyleElement.TreeView.Glyph.Closed;
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
renderer.DrawBackground(e.Graphics, expandRect);
}

Related

Resize Controls with Form Resize

I have read several stack overflow questions without finding a good working solution to my problem. How can I resize my controls whenever the form is resized? I would like them to get larger or smaller when the form becomes larger or smaller.
In visual basic this was quite easy to do with the form.Zoom property (which did't really require resizing controls of course, but solved what I needed). Unfortunately this is not available in C# winforms.
Here is some other things I have tried without luck:
private void formMain_Resize(object sender, EventArgs e)
{/*
double scale;
this.scaleWidth = (float)this.Width / (float)this.origWidth;
this.scaleHeight = (float)this.Height / (float)this.origHeight;
if (this.scaleHeight > this.scaleWidth)
{
scale = this.scaleHeight;
}
else
{
scale = this.scaleWidth;
}
foreach (Control control in this.Controls)
{
control.Height = (int)(control.Height * this.scaleHeight);
control.Width = (int)(control.Width * this.scaleWidth);
this.Refresh();
// control.Font = new Font("Verdana", control.Font.SizeInPoints * heightRatio * widthRatio);
}
///////This scaling didnt work for me either
//this.Scale(new SizeF(this.scaleWidth, this.scaleHeight));
//this.Refresh();
*/
}
If I overlooked an actualy working sample of code on another stack overflow question I would love to see it, but the ones I found were similar to those above which are not working.
Perhaps I was misusing it and someone could post sample code to show for those of us who keep asking this question how to go about solving the problem.
Also, I have tried using some of the anchor/docking tools thinking they would automatically allow it but it didn't.
The best option is to use a TableLayoutPanel. Put TableLayoutPanel on the form, set the Dock property to Fill, create required rows and columns and put the controls inside the cells. Of course you need to set Dock/Anchor on the controls inside the cells, so they respond to changes to the cell size. In some situations you may need to put a Panel into a cell and drop the controls inside it, because every cell can only contain a single control. You may also need to set RowSpan/ColumnSpan on the controls.
By using a TableLayoutPanel, you have complete control over how your cotrols should be arranged. You can set absolute or percentage size for rows and columns.
Use Anchor of the control. There's an option on anchoring the top, bottom, left and right. And you're good to go.
I found an alternative solution that is working well for me, appreciate any negative or positive comments on the solution.
Using several Split Containers and Split Containers inside of Split Containers in different regions I am able to section off the primary pieces of the layout, and within there utilizing Docking and Anchoring I am able to accomplish exactly what I wanted to do - it works beautifully.
I would point out I am aware that some folks online mention split containers use lots of resources.
If your controls are in a group box, be sure to set the group boxes properties to resize. Controls inside the box are controlled by the box. The box size (unless it is inside another box) is controlled by the form.
What you are trying to do in your code is to change the sizes of the controls which isn't so good approach. Generally, the size of the Buttons and TextBoxes shouldn't be changed when you re-size your form, but they often need to move (change location). Some controls do need to change size according to the re-sized form and but in most cases only one dimension. The central controls that are used for working area (if you are developing the tool for drawing for instance) should change sizes of both dimensions. All this you can accomplish by properly setting Dock and/or Anchor properties of the controls.
textBox1.Dock = DockStyle.Bottom;
textBox1.Anchor = AnchorStyles.Bottom & AnchorStyles.Left;
All these are also easily set in the Properties panel when using designer.
But if that isn't enough for you, in rare cases, you will most definitely want to only change the location of the control:
textBox1.Location = new Point(newX, newY);

User Control with transparent background

I've a simple user control that actually is just a panel, when I click in that panel, there will be a child user control added. that child is just another user control where I set width = 150px & height = 100px and the background color to transparent. also it has a textbox in the center that is 100 x 100 px.
this base structure will be a Node Based Interface in the future, where each box will have connection anchors and logic btn's or something like that in it.
my problem is that if i click a few times in the panel and the added box overlapy another, the transparency wont take effect.
here's a screenshot
how can i fix that issue ? is there a allowTransparency or something like that ?
there is also an issue with the order of the drawing, the new added blocks are always behind the other one's.
if you wish to see the code for this, let me know , but i don't think that there is anything relevant for this.
also, if you know a better way to implement a node graph, please feel free to tell me.
EDIT
following code was the first thing I've tried before I've even thought of posting a question in StackOverFlow.
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
MakeTransparentControls(this);
so please don't take this as a duplicate question or posting that code as answer
This is in general a very fundamental restriction in Windows. A window overlaps another window and obstructs whatever is behind it. Everybody's favorite answer, use ControlStyles.SupportsTransparentBackColor, is no workaround, it is already turned on for UserControl so setting it again makes no difference.
The style flag simulates window transparency. It does so by altering the way the window paints itself. It first asks the Parent to draw itself inside the window to provide the background, then draws itself on top of it. So by setting the BackColor property to Color.Transparent, you can expect to see the Parent window's pixels as the background.
And you do. The Parent of your user control is the form. It dutifully draws itself first and that produces the gray edges on the left and the right. What you hoped for was to also see the pixels of the other user controls. But that doesn't work, it only asks the Parent to draw, not any other control that happens to be overlapped as well. That certainly isn't impossible, it is just ugly and slow. This KB article shows the approach. Rather emphasizing the "ugly" moniker.
Clearly you can improve your UserControl, there's on the face of it no point in having these transparent edges at the left and right of the Panel. So set the panel's Location property to (0, 0) and the Dock property to Fill. Now every user control is nothing but a "node" and you don't need nor want to see pieces of any overlapped other "node". Drawing any lines between the nodes is going to require implementing the Paint event of the form.
If you truly need this kind of transparency then you are going to have to do this differently. You have to give up on the idea of using a window for each node. One way to do so is to just paint them. If you do that in the right order then you'll have no problem simulating true transparency, created by layering paint and just not draw where you need to see the "background". It will also be a lot faster, controls are expensive. You however will to give up on the convenience of using controls, you'll have to write code instead. Things like mouse hit testing become more complicated. And the text box will certainly be a hangup if it isn't actually supposed to be a Label (surely it does?) Or use a GUI class library that has given up on using a window as a control. Like WPF. And don't forget that there are plenty of libraries that already do this for you, connected nodes are a very common user interface paradigm. Visio for example.
try this in the constructor of your UserControl:
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
You also can try this :
public partial class UCTransparent : UserControl
{
public UCTransparent()
{
InitializeComponent();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
}
}
In one of my projects I had to set transparent background to almost every child control on the Form, so I created this method:
private void MakeTransparentControls(Control parent)
{
if (parent.Controls != null && parent.Controls.Count > 0)
{
foreach (Control control in parent.Controls)
{
if ((control is PictureBox) || (control is Label) || (control is GroupBox) || (control is CheckBox))
control.BackColor = Color.Transparent;
if (control.Controls != null && control.Controls.Count > 0)
MakeTransparentControls(control);
}
}
}
And in the form's constructor I added those lines:
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
MakeTransparentControls(this);
You can try something like this in your own context.

How to get Windows 7 aero selection color?

How can I retreive system colors related to current Aero style? I especially need the colors used in the selection gradient. SystemColors structure does not contain required colors.
Alternatively: How can I use WinAPI to draw selection on the specific canvas (Graphics object)?
OK, so the answer to the initial question is: there is no way to determine the specific colors I asked for. It is evaluated by internal theming routines provided by the OS.
Fortunately, there is a way to ask the OS to draw a themed piece of control, so called part. .NET provides two classes for drawing UI:
System.Windows.Forms.VisualStyles for themed UI and
System.Windows.Forms.ControlPaint for non-themed "Windows Classic".
Selection I asked for may be drawn by the following code:
// Graphics g = ...
VisualStyleRenderer selectionRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Item.Selected);
selectionRenderer.DrawBackground(g, someRectangle);
Unfortunately, neither TreeView.Item.Selected, nor ListView.Item.Selected is supported by the default window theme. However, one may switch theme to the Explorer using WinAPI via p/invoke:
// C++
SetWindowTheme(hwnd, L"Explorer", nullptr);
And then P/invoke his way through few UXTheme.h routines, which works perfectly.
The system-defined color of the background of the selected items which includes the selected text and selected menu items as well is located at System.Drawing.KnownColor.Highlight
You may then use the Color struct to get a color from a KnownColor
Example
System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Highlight);
Thanks,
Happy Holidays! :)

Create animation similar to AppBar

I'm trying to create an animation similar to the AppBar - basically make a control come in from the bottom edge of the screen. The problem with this is that the height of the control can change depending on its content, so I can't set an initial TranslateTransform.Y value in the XAML or in the Loaded event because the contents get generated AFTER the page is loaded.
So, in other (and fewer) words. I need to animate a control for which I don't know its size into the screen. Any ideas?
Thanks in advance.
You can use a Transition.
E.g. PaneThemeTransition or EntranceThemeTransition.
I'm not sure which property you should use to add a transition because it depends on your case. But you can do something like this:
Popup.ChildTransitions = new TransitionCollection { new EntranceThemeTransition() };
The Popup is not mandatory:
<uielement>
<uielement.Transitions>
oneOrMoreTransitions
</uielement.Transitions>
</uielement>
Animations

How to make text/labels smooth?

Does anyone know how to make labels, or text, smoother? At the moment they look quite jagged. As I want to make the label dynamic, I can't just insert the text from Photoshop.
You'll have to dynamically generate images representing your text if you want to anti-alias it. Here is an example on msdn: http://msdn.microsoft.com/en-us/library/a619zh6z.aspx
EDIT: Editing per comment below.
The link describes using the OnPaint event of your control to use a different TextRenderingHint. If you're wanting something a little more re-useable what you can do is create a Custom Label class that extends the Label class, and use this in your forms:
public partial class CustomLabel : Label
{
private TextRenderingHint _hint = TextRenderingHint.SystemDefault;
public TextRenderingHint TextRenderingHint
{
get { return this._hint; }
set { this._hint = value; }
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.TextRenderingHint = TextRenderingHint;
base.OnPaint(pe);
}
}
Add a new Custom Control called CustomLabel (or whatever you want to call it) and use the code above. Rebuild your project and you should then see the CustomLabel control appear in your toolbox at the top, under the "MyProject Components" category. In the properties pane for this custom label you'll see the new TextRenderingHint property. Set this to "AntiAlias." Add another label to your form and compare how they look.
If you want to default it to AntiAlias simply change the default value of the private variable.
Are you referring to ClearType? Then ClearType has to be enabled in Windows, and you must use a modern font, such as Tahoma or Segoe UI, not MS Sans Serif.
Update
You posted an example of the problem. I magnified it to 400 %. Clearly ClearType subpixel antialiasing is enabled. Personally I do not think the text looks jagged. If you want higer quality on-screen text you could buy a screen with a higher physical resolution (pixels per inch), and then draw the text at a (correspondingly) larger size. Then the text will have the same size on your screen, but will look much more smooth.
You could also give up on ClearType and use some other font-smoothing algorithm, but that is far from trivial, because ClearType is the font-smoothing system on Windows.
Update 2
If you are running Windows 7, you can fine-tune ClearType. Just open the start menu, write "ClearType" and start the guide. I think there are guides for Vista and XP too, but perhaps not installed by default, but available as PowerToys or something like that...
Make sure you have ClearType enabled.
Install MacType program, it smoothes your Windows font like MacOS, I don't know why Microsoft don't fix its Windows smoothness font?
If Microsoft fixed its fonts, I don't need to install third party app to such things like that which is fundamental for Windows.

Categories

Resources