I have a really strange set of circumstances that I just can't seem to get to work. I will let you know what I have and see If you can put me right. (The below represents the closest I have been able to get to what I want).
The idea is that when a day is selected I show a usercontrol that has a lorry's deliveries for that day.
The thing is that the date may be a range. Therefore I have the following setup thus far:
I have a
TableLayoutPanel (Dock = Fill; 1 column (100percent); 1 Row (Autosize).
Then each selected has a user control (ucSchedulerDay) this is added as a row to the TableLayoutPanel. So take a single day for example, you would have this:
TableLayoutPanel (Dock = Fill; 1 Column (100%); 1 Row (Autosize).
- (Row1 Column1) ucSchedulerDay
So the ucSchedulerDay is just a user control that houses a GroupBox (Dock=Fill) and a FlowLayoutPanel (also dock=fill inside the groupbox)
For each lorry I have another usercontrol added to the FlowLayoutPanel (these have a fixed width) so essentially what I have is the following for one single day
TableLayoutPanel (as above (also forgot to mention that AutoScrollBars=True)
- (Row 1 Column 1) ucSchedulerDay (Dock=Fill(done in code when added))
- GroupBox (Dock=Fill)
- FlowLayoutPanel (Dock=Fill)
- ucLorryDay1
- ucLorryDay2
This works fine as long as all the lorries fit on the screen (see above), so for one day with 2 lorries(or even up to 5 on my monitor) then it's ok. However, if I select two days or make the screen smaller, instead of showing the scroll bars but generally having the same layout, it cuts some of the ucLorryDays up and just doesn't display others.
Note on the above pic how the grey lorry is cut off, even the scroll bar doesn't extend that far.
I don't understand why this isn't working. I would really appreciate any help on this, please let me know if you need more information.
Ok, So I think that the nested GroupBox/UserControl-GroupBox idea was where it all went wrong. I have fixed this by updating the original form to do the following:
pnlLorries.Controls.Clear();
DateTime dt_start = monthView1.SelectionStart;
DateTime dt_end = monthView1.SelectionEnd;
int rowCounter = 0;
for (DateTime dt = dt_start; dt.Date <= dt_end; dt = dt.AddDays(1))
{
Label lbl = new Label();
Font ft = new System.Drawing.Font("Calibri", 12);
lbl.Text = dt.ToShortDateString();
lbl.Font = ft;
pnlLorries.Controls.Add(lbl, 0, rowCounter);
rowCounter++;
FlowLayoutPanel pnl = new FlowLayoutPanel();
pnl.Dock = DockStyle.Fill;
pnl.AutoSize = true;
DataTable tbl = cDALSettings.DB.GetCannedTable("select * from lorry");
// Now we simply add these controls to the panel...
foreach (DataRow row in tbl.Rows)
{
ucLorryDay ld = new ucLorryDay(dt, cTypes.ToInt(row["id"]), this);
pnl.Controls.Add(ld);
}
pnlLorries.Controls.Add(pnl, 0, rowCounter);
rowCounter++;
}
So I create it all and add in the label. The downside is of course that it is not in a neat little box but even when I did it this way with a groupbox it came back with the same results I was experiencing before. I suppose the problem was the panel inside a panel (inception style).
Related
I'm working wth .NET 4.7.2, Windowsform.
I have a datagridview and I manage to generate a powerpoint file pptx.
I made a first ppt slide and I'd like to add the datagridview content into the second ppt slide given that I need to have the option to change the data within the PPt slide.
Microsoft.Office.Interop.PowerPoint.Application pptApp = new Microsoft.Office.Interop.PowerPoint.Application();
pptApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Slides slides;
Microsoft.Office.Interop.PowerPoint._Slide slide;
Microsoft.Office.Interop.PowerPoint._Slide slide2;
Microsoft.Office.Interop.PowerPoint.TextRange objText;
// Create File
Presentation pptPresentation = pptApp.Presentations.Add(Microsoft.Office.Core.MsoTriState.msoTrue);
CustomLayout customLayout = pptPresentation.SlideMaster.CustomLayouts[PpSlideLayout.ppLayoutText];
// new Slide
slides = pptPresentation.Slides;
slide = slides.AddSlide(1, customLayout);
slide2 = slides.AddSlide(1, customLayout);
// title
objText = slide.Shapes[1].TextFrame.TextRange;
objText.Text = "Bonds Screner Report";
objText.Font.Name = "Haboro Contrast Ext Light";
objText.Font.Size = 32;
Shape shape1 = slide.Shapes[2];
slide.Shapes.AddPicture("C:\\mylogo.png", Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue, shape1.Left, shape1.Top, shape1.Width, shape1.Height);
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text = "Disclaimer";
dataGridViewBonds.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
dataGridViewBonds.SelectAll();
DataObject obj = dataGridViewBonds.GetClipboardContent();
Clipboard.SetDataObject(obj, true);
Shape shapegrid = slide2.Shapes[2];
I know I'm not so far by now but I miss smething. Any help would be appreciated !
I am familiar with Excel interop and have used it many times and most likely have become numb to the awkward ways in which interop works. Using PowerPoint interop can be very frustrating for numerous reasons, however, the biggest I feel is the lack of documentation and the differences between the different MS versions.
In addition, I looked for a third-party PowerPoint library and “Aspose” looked like the only option, unfortunately it is not a “free” option. I will assume there is a free third-party option and I just did not look in the right place… Or there may be a totally different way to do this possibly with XML. I am confident I am preaching to the choir.
Therefore, what I have been able to put together may work for you. For starters, looking at your current posted code, there is one part missing that you need to get the “copied” grid cells into the slide…
slide.Shapes.Paste();
This will paste the “copied” cells from the grid into an “unformatted” table into the slide. This will copy the “row header” if it is displayed in the grid in addition to the “new row” if the grids AllowUserToAddRows is set to true. If this “unformatted paste” works for you, then you are good to go.
If you prefer to have at least a minimally formatted table and ignore the row headers and last empty row… It may be easier to simply “create” a new Table in the slide with the size we want along with the correct number of rows and columns. Granted, this may be more work, however, using the paste is going require this anyway “IF” you want the table formatted.
The method (below) takes a power point _Slide and a DataGridView. The code “creates” a new Table in the slide based on the number of rows and columns in the given grid. With this approach, the table will be “formatted” using the default “Table Style” in the presentation. So, this may give you the formatting you want by simply “creating” the table as opposed to “pasting” the table.
I have tried to “apply” one of the existing “Table Styles” in power point, however, the examples I saw used something like…
table.ApplyStyle("{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}");
Which uses a GUID id to identify “which” style to use. I am not sure why MS decided on this GUID approach… this is beyond me, and it worked for “some” styles but not all.
Also, more common-sense solutions that showed something like…
table.StylePreset = TableStylePreset.MediumStyle2Accent2;
Unfortunately using my 2019 version of Office PowerPoint, this property does not exist. I have abandoned further research on this as it appears to be version dependent. Very annoying!
Given this, it may be easier if we format the cells individually as we want. We will need to add the cells text from the grid into the individual cells anyway, so we could also format the individual cells at the same time. Again, I am confident there is a better way, however, I could not find one.
Below the InsertTableIntoSlide(_Slide slide, DataGridView dgv) method takes a slide and a grid as parameters. It will add a table to the slide with data from the given grid. A brief code trace is below.
First a check is made to get the number of total rows in the grid (not including the headers) totRows. If the grids AllowUsersToAddRows is true, then the total rows variable is decremented by 1 to ignore this new row. Next the number of columns in the grid is set to the variable totCols. The top left X and Y point is defined topLeftX and topLeftY to position the table in the slide along with the tables width and height.
ADDED NOTE: Using the AllowUserToAddRows property to determine the number of rows … may NOT work as described above and will “miss” the last row… “IF” AllowUserToAddRows is true (default) AND the grid is data bound to a data source that does NOT allow new rows to be added. In that case you do NOT want to decrement the totRows variable.
Next a “Table” “Shape” is added to the slide using the previous variables to define the base table dimensions. Next are two loops. The first loop adds the header cells to the first row in the table. Then a second loop to add the data from the cells in the grid… to the table cells in the slide.
The commented-out code is left as an example such that you want to do some specific formatting for the individual cells. This was not need in my case since the “default” table style was close to the formatting I wanted.
Also, a note that “ForeColor” is the “Back ground” color of the cell/shape. Strange!
I hope this helps and again, sympathize more about having to use PowerPoint interop… I could not.
private void InsertTableIntoSlide(_Slide slide, DataGridView dgv) {
try {
int totRows;
if (dgv.AllowUserToAddRows) {
totRows = dgv.Rows.Count - 1;
}
else {
totRows = dgv.Rows.Count;
}
int totCols = dgv.Columns.Count;
int topLeftX = 10;
int topLeftY = 10;
int width = 400;
int height = 100;
// add extra row for header row
Shape shape = slide.Shapes.AddTable(totRows + 1, totCols, topLeftX, topLeftY, width, height);
Table table = shape.Table;
for (int i = 0; i < dgv.Columns.Count; i++) {
table.Cell(1, i+1).Shape.TextFrame.TextRange.Text = dgv.Columns[i].HeaderText;
//table.Cell(1, i+1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.Blue);
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.White);
}
int curRow = 2;
for (int i = 0; i < totRows; i++) {
for (int j = 0; j < totCols; j++) {
if (dgv.Rows[i].Cells[j].Value != null) {
table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Text = dgv.Rows[i].Cells[j].Value.ToString();
//table.Cell(curRow, j + 1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.LightGreen);
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.Black);
}
}
curRow++;
}
}
catch (Exception ex) {
MessageBox.Show("Error: " + ex.Message);
}
}
So I've been wanting to create something like CoreTemp
As you can see at the bottom it has 4 cores it's checking the temperature of.
And it will adapt depending on how many cores you have. How does that work? How does it create new slots depending on how many cores you have? How would I do this using WinForms and not WPF?
You need to dynamically add controls to a panel. You can use TableLayoutPanel and dynamically add rows with controls to it.
TableLayoutPanel layoutPanel = new TableLayoutPanel();
layoutPanel.ColumnCount = 5; //Core Name, Temperature, Low, High, Load
layoutPanel.RowCount = 1;
// Adding controls to columns
layoutPanel.Controls.Add(new Label() { Text = "Core #0" }, 1, 0);
//... and so on
// Adding more rows
layoutPanel.RowCount = layoutPanel.RowCount + 1;
I am fairly new to both C# and WPF, but the project I am working on seems to be a great fit for them. I have a dynamic grid of labels set up based off of a database. I need to set them up for drag and drop. I have some labels set up as header, and they are populated based off the next seven fridays. I made a function to pull the data from the database and compare each date to the content of the header label content. Then I make labels containing a job number and state for each of the dates that match with the header.
Kind of like this on load
content = Job# + " " + JobState
-Date+7n----Date+7n----Date+7n----Date+7n----Date+7n----Date+7n-----Date+7n
Content------Content-----Content-----Content-----Content-----Content------Content
Content------Content-----Content-----Content-----Content-----Content------Content
Content------Content-----Content--------------------Content-----Content------Content
Content---------------------Content--------------------Content-----Content------Content
Content---------------------Content--------------------Content---------------------Content
Pretty much when the date runs out of jobs it just doesn't make anymore labels for that column.
I tried to make it as dynamic and expandable as possible so I have a forward and backwards button that push all the dates up a week or back a week then repopulates the grid based off of the new date at the top.
private void AddLabel(ref int rowNum, string val, List<string>[] datGrid)
{
int rowLen;
int margin1;
int margin2;
var lb = new Label();
lb.HorizontalAlignment = HorizontalAlignment.Left;
lb.VerticalAlignment = VerticalAlignment.Top;
lb.Height = 32;
lb.Width = 155;
rowLen = datGrid[rowNum].Count;
lb.AllowDrop = true;
margin1 = 25 + (200 * (rowNum));
margin2 = 10 + (40 * (rowLen));
var gName = "grid" + (rowNum + 1) + (rowLen + 1);
lb.Margin = new Thickness(margin1, margin2, 0, 0);
lb.Content = val;
lb.Name = gName;
dyGrid.Children.Add(lb);
}
Where I put the row number that the label needs to be added to as rowNum, the actual content of the label as val and datGrid as the array of lists that holds the names for all the labels. I just can't figure out how to add a mouseDown or other drag and drop events to the labels as I make it.
The syntax you are looking for is
lb.MouseDown += new MouseButtonEventHandler(lb_MouseDown);
...
void lb_MouseDown(object sender, MouseButtonEventArgs e)
{
// Handle MouseDown event here.
Label lb = sender as Label;
}
But that said, I would recommend rethinking your design.
From what I can guess from your code, it sounds like you have a Grid (which allows it's children to overlap), and you are placing all the labels in the first cell of the Grid, and using the Margin property to position them. Its not a very efficient design, and will likely cause you problems in the future.
I would instead switch to using an ItemsControl bound to your collection of items, and drawing it's ItemsPanelTemplate as a Grid and your ItemTemplate as the Label. Then you can create data items so they represent the actual data item being represented by that item. Check out this similar answer if you want an example for what I am talking about.
Also if you do this, look up Bea Stollnitz's code for dragging/dropping databound items. Or some variation of it... I'm sure there's a copy of the code somewhere in my answer history too if you have questions.
I'm trying to make a row of a TableLayoutPanel appear as highlighted when the user selects a cell. The row contains, for example, a Label in one column and a TextBox in the other. I've got this so far (when adding a Label):
Label label = new Label();
label.Text = text;
label.BackColor = Color.Blue;
label.Dock = DockStyle.Fill;
label.Margin = new Padding(0);
label.Anchor = AnchorStyles.Top | AnchorStyles.Left;
table.Controls.Add(label, col, row);
This works well, except there is a bit of whitespace above and to the left of the label. Any idea how to make it so that the entire cell is coloured?
I know about cell painting, but I prefer the above-mentioned approach.
Never mind, I realised that the TableLayoutPanel's border setting was "Inset", so it appeared as though there was whitespace. Changing it to "Single" fixed the issue.
in one of my forms a datagridview displays data from a database (of course the number of data (so number of rows) can change). The database connection is in the form load event. I just cant figure out how the height of the whole datagridview is autosized, depending on the number of rows it displays.
This is what I managed to find, and it runs fine so far :
int GetDataGridViewHeight(DataGridView dataGridView)
{
var sum = (dataGridView.ColumnHeadersVisible ? dataGridView.ColumnHeadersHeight : 0) +
dataGridView.Rows.OfType<DataGridViewRow>().Where(r => r.Visible).Sum(r => r.Height);
return sum;
}
Thanks to this, I encapsulated my DataGridView in a UserControl so I could implement AutoSize correctly :
// This is in a user control where the datagrid is inside (Top docked)
protected override void OnResize(EventArgs e)
{
if (AutoSize)
{
var height = this.GetDataGridViewHeight(this.dataBoxGridView);
this.dataBoxGridView.Height = height;
this.Height = height +this.Padding.Top + this.Padding.Bottom;
}
}
I did not try (yet) to build a Custom Control directly from the DataGridView to implement this.
If you set DataGridView.AutoSize == true then as you add more rows the grid gets longer. Otherwise you get scrollbars. Unless you've set ScrollBars == Null || Horizontal, in which case the rows just disappear of of the end.
For some reason, DataGridView.AutoSize can only be set programmatically. And there're some odd behaviours observable when you put the grid inside an autosizable control. It doesn't seem to respond to the size of the grid.
I ended up calculating the expected size of the grid from the column, row, header, margin, padding and border sizes, and then sizing the control containing the grid and anchoring the grid on four sides. Felt really clunky but it's the best I could come up with. If you're still around, comment and I'll see if I can find the code, I don't have it on hand.
MSDN says "This property is not relevant for this class."
MSDN: DataGridView.AutoSize Property
This is how i did. to set height of DataGridView you can use its Set Height property.On form load you can use this code to hide datagridview.
dataGridViewName.Height = 0;
Then While fetching Rows from Database. we can use below method to get datagridview Height according to number of Rows.
private int dataGridViewHeight()
{
int sum = this.dataGridViewName.ColumnHeadersHeight;
foreach (DataGridViewRow row in this.dataGridViewName.Rows)
sum += row.Height + 1; // I dont think the height property includes the cell border size, so + 1
return sum;
}