I am using the Infragistics XamDialogWindow to approximate an MDI window in WPF.
Basically I have a user control that I assign to the content of the window and then add the XamDialogWindow as a child of a Grid.
I then have two buttons which display the windows as either "Tile" or "Cascade".
The cascade works really well but I am having problems with the Tile method.
I get the available width and height using the "ActualWidth" and "ActualHeight" properties of the container.
I then calculate the number of rows and columns needed.
Next I iterate through the columns and I calculate how many rows there are in a column, (this means that if there are an odd number of windows such as 5 I will have two rows in column 0 and 3 in column 1).
The width of each window is worked out as "AvailableWidth / numColumns"
The height of each window is worked out as "AvailableHeight / WindowsForThisColumn"
I then iterate through the rows and calculate the top left coordinate of the window we are positioning as follows:
left = columnIndex * width
top = rowIndex * height
The Left, Top, Width and Height properties of the window are then set.
PROBLEM
The Left, Top, Width and Height properties seem to be calculated correctly so for example
AvailableWidth = 1000;
AvailableHeight = 1000;
Window1 = 0, 0, 500, 500
Window2 = 0, 500, 500, 500
Window3 = 500, 0,500, 500
Window4 = 500, 500, 500, 500
This should give a nice grid of 4 windows with no margins filling all available space, however this is not the result.
The windows seem to be offset when displayed (see image below).
If I look at the Left, Top, Width and Height properties they all appear to be correct.
The weird thing is, if I click "Tile" a second time they will all appear correctly.
If I move a window after clicking tile the second time, they will appear correctly.
I have tried using a Canvas and setting the Canvas.Left and Top properties, A grid, stackpanel and wrappanel with the same results every time.
Can anyone help me? I think it may be a problem with how WPF measures sizes and locations at Render Time
var childrenToArrange = Children.Where(a => a.WindowState != Infragistics.Controls.Interactions.WindowState.Minimized).ToList();
var availableWidth = Panel.ActualWidth;
var availableHeight = Panel.ActualHeight;
if (Layout == MDILayout.TILE)
{
//get the number of rows and columns
int rows = (int)Math.Sqrt(childrenToArrange.Count);
int columns = childrenToArrange.Count / rows;
var index = 0;
var width = availableWidth / columns;
//iterate through the columns
for (int x = 0; x < columns; x++)
{
//get the number of windows for this column
var windowsForThisColumn = rows;
if (childrenToArrange.Count % columns > (columns - x - 1))
windowsForThisColumn++;
var height = availableHeight / windowsForThisColumn;
//iterate through the rows
for (int y = 0; y < windowsForThisColumn; y++)
{
//get the X and Y coordinates
var left = x * width;
var top = y * height;
//get the window
var mdiChild = childrenToArrange[index] as XamDialogWindow;
mdiChild.Margin = new Thickness(0);
mdiChild.Left = left;
mdiChild.Top = top;
mdiChild.Width = width;
mdiChild.Height = height;
index++;
}
}
}
I've reproduced the issue with your snippet. Please click the following hyperlink to the following forum post at Infragistics for further instructions.
Michael DiFilippo
Related
I have an issue with TableLayoutPanel. Despite using a single column with 100% width, the control I add keeps the size assigned to it in layout creator.
TableLayoutPanel panel = someTableLayoutPanel;
panel.RowStyles.Clear();
panel.ColumnStyles.Clear();
panel.Controls.Clear();
ColumnStyle columnStyle = new ColumnStyle(SizeType.Percent, 100F);
panel.ColumnStyles.Add(columnStyle);
int count = capabilityListManager.DeviceCount();
panel.RowCount = deviceCount;
panel.ColumnCount = 1;
for (int index = 0; index < count; index++)
{
RowStyle rowStyle = new RowStyle(SizeType.Absolute, 120F);
panel.RowStyles.Add(rowStyle);
SomeCustomControl cell = new SomeCustomControl();
panel.Controls.Add(cell, 0, index);
}
I assumed that would be enough for the control to always be resized to have fixed 120 pixels height and take 100% of width provided by TableLayoutPanel, however the result looks like this:
My custom control is the one with the border, and the TableLayoutPanel has blueish hue - clearly there's space left for the control to occupy.
SomeCustomControl has its AutoSize property set to false. What am I missing?
I created a Line Chart control in Windows Forms.
I divided the ChartArea, AxisX into four intervals but I want to apply back color (unique color) to each interval.
Can someone help me on this?
You could paint those areas, but this would always show above all chart elements including grid and data points.
So, as NLindborn suggests, the best way are StripLines.
They are under all elements and will scale nicely.
Note that their properties are in data values, so you need to know the values, or rather the x-axis range, in your chart.
Here is complete code example using StripLines:
// set up the chart:
ChartArea ca = chart.ChartAreas[0];
chart.Series.Clear();
for (int i = 0; i < 5; i++)
{
Series s = chart.Series.Add("Series" + (i+1));
s.ChartType = SeriesChartType.Line;
s.BorderWidth = 2;
}
// add a few test data
for (int i = 0; i <= 360; i++)
{
chart.Series[0].Points.AddXY(i, Math.Sin(i * Math.PI / 180f));
chart.Series[1].Points.AddXY(i, Math.Cos(i * Math.PI / 180f));
chart.Series[2].Points.AddXY(i, Math.Sin(i * Math.PI / 90f));
chart.Series[3].Points.AddXY(i, Math.Cos(i * Math.PI / 90f));
chart.Series[4].Points.AddXY(i, Math.Sin(i * Math.PI / 30f));
}
// set up the chart area:
Axis ax = ca.AxisX;
ax.Minimum = 0;
ax.Maximum = 360;
ax.Interval = 30;
// a few semi-transparent colors
List<Color> colors = new List<Color>() { Color.FromArgb(64, Color.LightBlue),
Color.FromArgb(64, Color.LightSalmon), Color.FromArgb(64, Color.LightSeaGreen),
Color.FromArgb(64, Color.LightGoldenrodYellow)};
Now we are ready to create the StripLines:
// this is the width of the chart in values:
double hrange = ax.Maximum - ax.Minimum;
// now we create and add four striplines:
for (int i = 0; i < 4; i++)
{
StripLine sl = new StripLine();
sl.Interval = hrange; // no less than the range, so it won't repeat
sl.StripWidth = hrange / 4f; // width
sl.IntervalOffset = sl.StripWidth * i; // x-position
sl.BackColor = colors[i];
ax.StripLines.Add(sl);
}
Note that you will need to adapt the stripline data whenever you change the axis range!
Also note the StripLine use axis values.
Update:
One common issue is to move the striplines when zooming. Without a little help they will stick to the original positions. Codeing the AxisViewChanged will help, maybe like so:
For each of your striplines calculate an IntervalOffset; in the simplest case of the 1st one this should work:
chart1.ChartAreas[0].AxisX.StripLines[0].IntervalOffset =
chart1.Series[0].Points[0].XValue - e.NewPosition;
For the others add the correct multiple of the width as above!
AxisX into four intervals but I want to apply back color (unique color)
These intervals are created with colored StripLine(s). Either via code:
var stripLine = new System.Windows.Forms.DataVisualization.Charting.StripLine()
{
BackColor = Color.Blue,
IntervalOffset = 4, // This is where the stripline starts
StripWidth = 2 // And this is how long the interval is
};
chart1.ChartAreas[0].AxisX.StripLines.Add(stripLine);
You need to add data points for the interval to show.
Or, StripLines can also be added from VS design mode from (Properties) -> ChartAreas -> Select a ChartArea -> Axes -> Select the Axis you want it to show on -> StripLines, then Add StripLine. You have to set a BackColor, IntervalOffset and StripWidth for it to show. If you set StripLine.Interval it will repeat by that interval.
The results I want are one main chart and several sub charts which are synchronized with the main chart. (Sub charts must share the same x-axis with the main chart.)
At first, I tried it using multiple chartAreas. I can sync sub chartAreas with the main chartArea. (Using this: How to align two (or more) charts in size, scroll and grid) This one is what I want exactly. But I can't only vertical scroll the sub-chartAreas. If I scroll, all the charts are scrolled. I want to vertical scroll only sub-chartAreas. (to show the main chartArea at top anytime even when the scroll bar is down)
So, I reverse a decision to use multiple charts (not chart Areas). I can place them into TableLayoutPanel. One chart per row. Then, I cannot sync their x-axes...
Is there any way to sync an x-axis with multiple charts?
Or to scroll only sub-chartAreas using multiple chartAreas?
By 'scroll' I mean Vertical scroll. See here:
From the image and the comments I gather that you actually want to scroll down a chart with several ChartArea but keep one fixed at the top.
((If that is so you should correct the question title!))
By default a Chart can only scroll the data within a zoomed ChartArea, not several ChartAreas within the Chart.
Here is an example of faking both ChartArea scrolling and freeezing the top ChartArea.
Here are the necessary steps:
We need a VerticalScrollbar control and anchor it to the right of the Chart.
We need to set its Minumum and Maximum fields to suitable values; I left the minimum at 0 and calculate the max with a few params..
Then we can code its Scroll event..:
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
float h = (100 - yOffTop - yOffBottom) / visibleCount;
ChartArea main = chart1.ChartAreas[0];
for (int i = 1; i < chart1.ChartAreas.Count; i++)
{
ChartArea ca = chart1.ChartAreas[i];
ca.Position = new ElementPosition(0, h * i - vScrollBar1.Value + mainExtra, 80, h);
ca.Visible = ca.Position.Y >= main.Position.Bottom ;
}
}
visibleCount controls how many ChartAreas are visible. In this example I have one for the year, fixed at the top and 12 more for the months..:
For this to work you need to set up the chart so that the chartareas are intialized in a suitable way.
I used this piece of code, do use your own code for stuff like right space (I left 20% for the legend) etc..:
int visibleCount = 5;
float yOffTop = 0; // no extra top space for a chart title
float yOffBottom = 0; // no extra bottom space either
float mainExtra = 6; // a few extra% for the main CA's axis labels
private void Init_button_Click(object sender, EventArgs e)
{
chart1.Series.Clear();
chart1.ChartAreas.Clear();
chart1.BackColor = Color.Snow;
float h = (100 - yOffTop - yOffBottom) / visibleCount;
for (int i = 0; i < 13; i++)
{
float yOff = i != 0 ? mainExtra : 0;
float yExtra = i == 0 ? mainExtra : 0;
ChartArea ca = chart1.ChartAreas.Add("ca" + i);
ca.Position = new ElementPosition(0, h * i + yOff , 80, h + yExtra);
ca.BackColor = Color.FromArgb(i * 20, 255 - i * 3, 255);
ca.AxisX.IntervalOffset = 1;
Series s = chart1.Series.Add("s" + i);
s.ChartArea = ca.Name;
for (int j = 1; j < 30; j++)
{
s.Points.AddXY(j, rnd.Next(100) - rnd.Next(20));
}
chart1.ChartAreas[0].BackColor = Color.Silver;
ca.AxisY.Title = i == 0 ? "Year" :
DateTimeFormatInfo.CurrentInfo.GetMonthName(i);
ca.AxisX.Enabled = (i == 0) ? AxisEnabled.True : AxisEnabled.False;
}
vScrollBar1.Minimum = 0;// (int)( h);
vScrollBar1.Maximum = (int)((chart1.ChartAreas.Count - visibleCount + 0.5f) * h
+ mainExtra );
}
I draw extra rectangles around each CA, just for testing; do ignore them!
Note: Working with those percentages is always a little tricky and may take some tweaking!
I am building a DataGridView table programmatically and placing it within a GroupBox on a form.
I insert the data, then resize the columns automatically to fit the data. I then want to resize the groupbox to the size of the DataGridView. For each column and row, I get their respective width and height, which technically should be accurate, and update the panel and overall DataGridView sizes.
The problem is that Column.Width always returns 100 pixels regardless of its actual size (see screenshot: actual column width is around 30px, not 100). If I manually enter width = 90 pixels, the resizing is quite accurate!
matrix = new DataGridView();
//modify behaviour
matrix.ColumnHeadersVisible = false;
matrix.AllowUserToResizeColumns = false;
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
matrix.RowHeadersVisible = false;
matrix.AllowUserToResizeRows = false;
//modify positioning
matrix.Location = new Point(10, 20);
//matrix.Anchor = (AnchorStyles)(AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right);
matrix.Dock = DockStyle.Fill;
//set the size of the matrix
matrix.ColumnCount = col;
matrix.RowCount = row;
//Data now inserted...
matrix.AutoResizeColumns(); //correctly resizes the columns
int height = 0;
foreach (DataGridViewRow row in matrix.Rows)
{
height += row.Height;
}
height += matrix.ColumnHeadersHeight;
int width = 0;
foreach (DataGridViewColumn col in matrix.Columns)
{
width += col.Width;
//PROBLEM: Width always = 100 pixels.
}
width += matrix.RowHeadersWidth;
//width = 90; //override width manually
matrix.Size = new Size(width + 2, height + 2);
panel.Size = new Size(matrix.Width, matrix.Height);
the panel is so large because width isn't 90px but 357 roughly, which is wrong!
EDIT: PARTIAL FIX
I have found a way to get the correct width of a cell:
DataGridView.Rows[0].Cells[0].ContentBounds.Width
//ContentBounds = a rectangle with the exact dimensions of that cell
I can now set the DataGridView the correct size, but only if it's not Docked to Fill. Setting matrix.Dock = DockStyle.Fill prevents the resizing to occur correctly.
I guess that is because you're autosizing your columns in row:
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
try to change it to:
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
I have figured out what the problem is:
DataGridView.Dock = DockStyle.Fill prevented the DataGridView.AutoResizeColumns() method from doing anything.
Rectangle rect = matrix.Rows[0].Cells[0].ContentBounds getting the dimensions of the rectangle containing the cell values didn't update after AutoResizeColumns()
I tried setting the columns manually, but as heq suggested, the AutoSizeColumnsMode must be set to None in order to be able to modify the width.
I adjusted the width of each column manually, to be 10px for each character inside the cell
Final Solution
/* Create the DataGridView */
matrix = new DataGridView();
//modify behaviour
matrix.ColumnHeadersVisible = false;
// matrix.columnhea
matrix.AllowUserToResizeColumns = false;
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
matrix.RowHeadersVisible = false;
matrix.AllowUserToResizeRows = false;
matrix.ReadOnly = true;
//modify positioning
matrix.Location = new Point(10, 20);
//matrix.Dock = DockStyle.Fill;
matrix.Anchor = (AnchorStyles)(AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right);
//set the size of the matrix
matrix.ColumnCount = cols;
matrix.RowCount = rows;
panel.Controls.Add(matrix);
/* Set the data contents (not shown) */
...
/* Adjust the width of columns */
int width = 0;
int height = 0;
for(int c = 0; c < cols; c++)
{
int largest = 0; //largest number of characters in cell
for(int r = 0; r < rows; r++)
{
int len = matrix.Rows[r].Cells[c].Value.ToString().Length;
if (len > largest)
{
largest = len;
}
}
matrix.Columns[c].Width = largest * 10;
width += largest * 10;
}
/* Get height of table */
foreach (DataGridViewRow row in matrix.Rows)
{
height += row.Height;
}
/* Set the DataGridView Size and Parent Panel size */
//Note: first set the parent panel width, the table is anchored to all of its edges
// as such table wont resize correctly if the panel isn't up to size first
panel.Size = new Size(width + 20, height + 30);
matrix.Size = new Size(width + 3, height + 3); //is it 1px per row and col?
/* Parent Panel settings kept to default, no modifications */
I am trying to create rectangles and the number of rectangles is depend on data passed from database.
For example, if number = 5, the program will generate 5 rectangles. Also, these rectangles must be able to follow my rectangle property settings, like height, width,color...put them in one line at the end.
Is there a way to do that?
I am using WPF and C#.
Thank you.
To create the rectangle in code dynamically:
int number = 5;
int width = 10;
int height = 10;
int top = 20;
int left = 20;
for (int i = 0; i < number; i++)
{
// Create the rectangle
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Green,
Stroke = Brushes.Red,
StrokeThickness = 2,
};
// Add to a canvas for example
canvas.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left);
}