Resizing special fonts - winforms

I am resizing my controls and texts at runtime based on screen resolution using
the routine below.
This is working fine except for fonts such as "Wingdings 3".
"Wingdings 3" is bringing the actual character "Å" instead of the "left arrow".
Any ideas why?
public void ResizeControls(Control objCtl)
{
foreach (Control cChildren in objCtl.Controls)
{
if (cChildren.HasChildren)
{
ResizeControls(cChildren);
}
else
{
cChildren.Size = new Size(Screen.PrimaryScreen.Bounds.Width * cChildren.Width / DesignWidth, Screen.PrimaryScreen.Bounds.Height * cChildren.Height / DesignHeight);
cChildren.Location = new Point(Screen.PrimaryScreen.Bounds.Width * cChildren.Left / DesignWidth, Screen.PrimaryScreen.Bounds.Height * cChildren.Top / DesignHeight);
if ((cChildren.GetType() == typeof(System.Windows.Forms.Label) | (cChildren.GetType() == typeof(System.Windows.Forms.Button))))
cChildren.Font = new Font(cChildren.Font.FontFamily.Name, Screen.PrimaryScreen.Bounds.Height * cChildren.Font.Size / DesignHeight, cChildren.Font.Style, cChildren.Font.Unit, ((byte)(0))); // <-- HERE RESIZING A CONTROLS FONT PROPERTY
}
}
objCtl.Size = new Size(Screen.PrimaryScreen.Bounds.Width * objCtl.Width / DesignWidth, Screen.PrimaryScreen.Bounds.Height * objCtl.Height / DesignHeight);
objCtl.Location = new Point(Screen.PrimaryScreen.Bounds.Width * objCtl.Left / DesignWidth, Screen.PrimaryScreen.Bounds.Height * objCtl.Top / DesignHeight);
if ((objCtl.GetType() == typeof(System.Windows.Forms.Label)) | (objCtl.GetType() == typeof(System.Windows.Forms.Button)))
objCtl.Font = new Font(objCtl.Font.FontFamily, Screen.PrimaryScreen.Bounds.Height * objCtl.Font.Size / DesignHeight, objCtl.Font.Style, objCtl.Font.Unit, ((byte)(0))); // <-- HERE RESIZING A CONTROLS FONT PROPERTY
}

Got it. I was setting the GDI character set to 0 instead of copying the fonts original GDI character set.
"objCtl.Font.GdiCharSet"

Related

Scale components in FlowLayout as big as possible

How can I scale PictureBox components to best fit the given space on the screen while keeping their aspect ratio (interdependent of the actual image or its SizeMode) ?
I tested setting the Dock of the FlowLayout and the PictureBox to Fill. I also tested using a Panel as a wrapper and tested different settings for AutoSize and AutoSizeMode.
To give more information about the background: I want to dynamically add and remove images in the viewport of the application, so a TableLayout is in the first step sort of to static. I have to admit I'm was also thinking of calculating the size an position manually - or to dynamically adapt the row and column count of the TableLayout - but it seems to me prone to errors. I thought having a FlowLayout and automatically sized components should be the correct way - but it seems not to work that way. (To speak as web developer, I simply want to "float the images left", "with width and height set to 'auto'" and no scrolling.)
The images should visualize this a bit: the first figure should point out the layout, if there is only one PictureBox - it takes the whole space (or as big as possible with the given aspect ratio). The second shows how I would like the Layout to be, if there are two (three or four) images. The third figure is showing basically a resized window with three (to six) images.
Is there some point I'm missing?
This code snippet do this:
It arranges the visible controls inside a container in respect of the aspect ratio (see R variable in the code), and uses the container margin values to get horizontal and vertical gaps between items. The padding of the container is also handled.
public static void Arrange(Control container)
{
var H = container.DisplayRectangle.Height;
var W = container.DisplayRectangle.Width;
var N = container.Controls.OfType<Control>().Count(c => c.Visible);
var R = 4 / 3d; // item aspect ratio
var margin = container.Margin;
var padding = container.Padding;
var horizontalGap = margin.Left + margin.Right;
var verticalGap = margin.Top + margin.Bottom;
if (N == 0)
return;
var bestSizedItem = (
// Try n rows
Enumerable.Range(1, N).Select(testRowCount =>
{
var testItemHeight = (H - verticalGap * (testRowCount - 1)) / testRowCount;
return new
{
testColCount = (int)Math.Ceiling((double)N / testRowCount),
testRowCount = testRowCount,
testItemHeight = (int)testItemHeight,
testItemWidth = (int)(testItemHeight * R)
};
})
// Try n columns
.Concat(
Enumerable.Range(1, N).Select(testColCount =>
{
var testItemWidth = (W - horizontalGap * (testColCount - 1)) / testColCount;
return new
{
testColCount = testColCount,
testRowCount = (int)Math.Ceiling((double)N / testColCount),
testItemHeight = (int)(testItemWidth / R),
testItemWidth = (int)testItemWidth
};
})))
// Remove when it's too big
.Where(item => item.testItemWidth * item.testColCount + horizontalGap * (item.testColCount - 1) <= W &&
item.testItemHeight * item.testRowCount + verticalGap * (item.testRowCount - 1) <= H)
// Get the biggest area
.OrderBy(item => item.testItemHeight * item.testItemWidth)
.LastOrDefault();
Debug.Assert(bestSizedItem != null);
if (bestSizedItem == null)
return;
int x = container.DisplayRectangle.X;
int y = container.DisplayRectangle.Y;
foreach (var control in container.Controls.OfType<Control>().Where(c => c.Visible))
{
control.SetBounds(x, y,
bestSizedItem.testItemWidth,
bestSizedItem.testItemHeight);
x += bestSizedItem.testItemWidth + horizontalGap;
if (x + bestSizedItem.testItemWidth - horizontalGap > W)
{
x = container.DisplayRectangle.X;
y += bestSizedItem.testItemHeight + verticalGap;
}
}
}
I put this snippet on Gist so you can contribute if you wish.

Wrong coordinates on multiple monitors

I have a Datagrid and I want to know the position of a datacell for overlaying it with a window.
It works fine with only one monitor, but with multiple monitors, the window is displaced.
Here's the code:
Point point = cell.PointToScreen(new Point(0, 0));
...
Window myWindow = new Window();
myWindow.Top = point.Y;
myWindow.Left = point.X;
Somebody has some experience with positioning on multiple monitors?
EDIT:
I made following test:
public MyWindow()
{
...
this.LocationChanged += MyWindow_LocationChanged;
}
void MyWindow_LocationChanged(object sender, EventArgs e)
{
Console.WriteLine(this.Top + " <--> " + this.PointToScreen(new Point(0, 0)).Y);
}
Results:
- Single-Monitor: 0 <--> 30; 20 <--> 50; 100 <--> 130
==> Always difference of 30 (may be caused by title bar)
- Dual-Monitor: 0 <--> 30; 20 <--> 55; 100 <--> 153
==> At 0,0 difference of 30. But the more I moved the window away from 0,0. the greater becomes the difference, but should stay the same. Very strange!
EDIT2:
Here's my solution, thanks to CodeNaked for the hint:
Point point = cell.PointToScreen(new Point(0, 0));
...
Window myWindow = new Window();
PresentationSource source = PresentationSource.FromVisual(this);
myWindow.Top = point.Y / source.CompositionTarget.TransformToDevice.M22;
myWindow.Left = point.X / source.CompositionTarget.TransformToDevice.M11;
This may have to do with a non-standard DPI setting, but I'm pretty sure that setting affects all monitors. This blog shows how to get the correct position. But the code is effectively:
PresentationSource source = PresentationSource.FromVisual(control);
double dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
double dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
window.Left = point.X * 96.0 / dpiX;
window.Top = point.Y * 96.0 / dpiY;
The behavior you described is not correct and I can't reproduce it.
I created a simple Window with the following code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LocationChanged += (s, e) =>
{
var screen = PointToScreen(new Point(0, 0));
var window = new Point(Left, Top);
var diff = screen - window;
textbox.Text = window.ToString() + Environment.NewLine +
screen.ToString() + Environment.NewLine + diff;
};
}
}
The last line (= the difference between the two coordinates) never changes.
I'm unable to reproduce the problem your experience. The upper left corner of the client area of the window (the point returned by PointToScreen) is always translated 8 pixels horizontally and 30 pixels vertically from the upper left corner of the window. This is on a two-monitor setup.
You should be able to compute the values 8 and 30 from the SystemParameters class, however I must admit that I'm not sure exactly what parameters to use to arrive at the actual values on my system.

DataGridView row: Semi-transparent selection or row border on selection

I have a DataGridView where the background of each row is different depending on the data bound item. Though, when I select a row, I can no longer see its original background color.
To solve this, I have thought of two solutions:
I can make the selections semi-transparent, making it possible to see if two selected rows have different background colors.
Or; I can remove the selection colors entirely, and draw a border around the selected rows.
What option is easier and how can I do this?
It's a WinForm app.
Edit: I ended up using some of your code, adrift
private void dgv_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (dgv.Rows[e.RowIndex].Selected)
{
var row = dgv.Rows[e.RowIndex];
var bgColor = row.DefaultCellStyle.BackColor;
row.DefaultCellStyle.SelectionBackColor = Color.FromArgb(bgColor.R * 5 / 6, bgColor.G * 5 / 6, bgColor.B * 5 / 6);
}
}
This gives the impression of a semi-transparent selection color. Thanks for your help!
If you want to draw a border around selected rows, you can use the DataGridView.RowPostPaintEvent, and to 'clear' the selection colors, you can use the DataGridViewCellStyle.SelectionBackColor and DataGridViewCellStyle.SelectionForeColor properties.
For example, if I set the row cell style like this
row.DefaultCellStyle.BackColor = Color.LightBlue;
row.DefaultCellStyle.SelectionBackColor = Color.LightBlue;
row.DefaultCellStyle.SelectionForeColor = dataGridView1.ForeColor;
I can add this code to the RowPostPaintEvent
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (dataGridView1.Rows[e.RowIndex].Selected)
{
using (Pen pen = new Pen(Color.Red))
{
int penWidth = 2;
pen.Width = penWidth;
int x = e.RowBounds.Left + (penWidth / 2);
int y = e.RowBounds.Top + (penWidth / 2);
int width = e.RowBounds.Width - penWidth;
int height = e.RowBounds.Height - penWidth;
e.Graphics.DrawRectangle(pen, x, y, width, height);
}
}
}
and a selected row will display like this:

Making a ToolStripSeparator horizontal in a vertical toolbar in WinForms

have Googled and cannot find out how to make a ToolStripSeparator "draw" an horizontal line in a toolbar that is aligned vertical.
The separator is drawn vertically which makes it awful.
Eg.
* - item
*
*
| <- separator
*
*
when it should be
*
*
- <- separator
*
*
You can create your own ToolStripRenderer and override the OnRenderSeparator to draw the line yourself.
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{
using (var pen = new Pen(borderColor))
{
e.Graphics.DrawLine(pen, 5, e.Item.Size.Height / 2, e.Item.Size.Width - 5, e.Item.Size.Height / 2);
}
}
Then you set the Renderer property of your toolstrip to the renderer you just made.
toolStrip.Renderer = new MyToolStripRenderer();

Silverlight specific Image shifting to the right

I am generating a set images to form a human body so that I can use for a physics engine.
The images generated are in a specific user control in where I set the dimentions and co-ordinates of each image. That usercontrol is then loaded in another user control but for some reason when the images are loaded, one specific image which I named (rightBicep) is shifting to the right. Here is a screenshot :
alt text http://img193.imageshack.us/img193/592/imageshift.jpg
I illustrated the positions of the images with dotted lines, the green dotted line is refering to where the image should be located, and the red dotted line is where the image is being shown.
The weird thing is the image beneath it (called rightForearm) take's it's LeftPosition from it, and when during debugging they have the exact same leftProperty value. Here's the syntax :
public void generateRightBicep(string imageUrl)
{
rightBicep = new Image();
rightBicep.Name = CharacterName + "rightbicep";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightBicep);
rightBicep.Source = new BitmapImage(imageUri);
rightBicep.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightBicepLoaded()
{
var bi = waitTillImageLoad(rightBicep.Name);
rightBicep.Height = elbowToArmpit + (2 * palm);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height); // to be determined
Vector2 topVector;
topVector.X = (float)(Convert.ToDouble(torso.GetValue(Canvas.LeftProperty)) - palm);
topVector.Y = (float)(Convert.ToDouble(neck.GetValue(Canvas.TopProperty)) + neck.Height);
if (!faceRight)
{
perspectiveVectorHeight(ref topVector, ref rightBicep, torso.Width);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height);
}
rightBicep.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightBicep.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightBicep.SetValue(Canvas.ZIndexProperty, rightBicepZindex);
generateRightShoulder();
}
public void generateRightForearm(string imageUrl)
{
rightForearm = new Image();
rightForearm.Name = CharacterName + "rightforearm";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightForearm);
rightForearm.Source = new BitmapImage(imageUri);
rightForearm.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightForearmLoaded()
{
var bi = waitTillImageLoad(rightForearm.Name);
rightForearm.Height = (elbowToHandTip - handLength) + palm;
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
Vector2 topVector;
if (faceRight)
{
topVector.X = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.TopProperty)) + rightBicep.Height - palm);
}
else
{
topVector.X = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.TopProperty)) + leftBicep.Height - palm);
perspectiveVectorHeight(ref topVector, ref rightForearm, torso.Width);
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
}
rightForearm.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightForearm.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightForearm.SetValue(Canvas.ZIndexProperty, rightForearmZIndex);
generateRightElbow();
}
Now all the values I am adding together are a group of doubles I preset, and the property faceRight is to dertmine if the human body is facing right or left to determine where the positions of the body parts (since if the right hand looks on the left hand side when the human body turns the other way).
If you notice the rightforearm is taking the leftproperty of the rightbicep, so technically it should display direcrly underneath which it isn't. I also debugged the user control and both have the left property of -3.
PS. I call the methods rightbicepLoaded and rightforearmLoaded when an event is called when all the imageOpened events all have been triggered.
Any ideas on why this is happening?
Found out why , in my method ratio it should take hieght and width, and I put and i put 2 hieghts instead

Resources