I am printing a panel with all its controls Inside. My problem is that I want to make a counter Inside this panel and increase its value for each pages printed. I tried incrementing the counter Inside my PrintPage event but this isn't working. Would it be possible to add multiple printpage events with increased counter for every page ? Here's my not working code. Thanks for giving me advices about how I could do this printing...
private void PrintPage(object o, PrintPageEventArgs e)
{
try
{
if (File.Exists(toPrint) && nbrPrint < nbrPages)
{
Rectangle m = panel1.ClientRectangle;
Bitmap imaag = new Bitmap(m.Width, m.Height);
panel1.DrawToBitmap(imaag, m);
e.Graphics.DrawImage(imaag, e.MarginBounds);
nbrPrint++;
compteurPrint++;
cpt.Text = compteurPrint.ToString();
}
}
catch (Exception)
{
}
}
nbrPrint is the pages printed, nbrPages the number of pages asked to print, compteurPrint the value I need to print in the page (which needs to be incremented), cpt is the label (Inside panel1) in which I show compteurPrint.
Since I do this:
pd.PrintPage += PrintPage;
pd.Print();
Can I do:
while (nbrPrint < nbrPages)
{
pd.PrintPage += PrintPage;
compteurPrint++;
}
pd.Print();
I think it would be the same result as what I'm currently doing…
Thanks for help !
You need to set the HasMorePages property on PrintPageEventArgs to tell it that there are more pages to print.
private void PrintPage(object sender, PrintPageEventArgs e)
{
try
{
if (File.Exists(toPrint) && nbrPrint < nbrPages)
{
Rectangle m = panel1.ClientRectangle;
Bitmap imaag = new Bitmap(m.Width, m.Height);
panel1.DrawToBitmap(imaag, m);
e.Graphics.DrawImage(imaag, e.MarginBounds);
nbrPrint++;
compteurPrint++;
cpt.Text = compteurPrint.ToString();
e.HasMorePages = true; // Set this to true as long as there are more pages to print. It defaults to false.
}
}
catch (Exception)
{
}
}
Checkout the Microsoft documentation for an example: PrintDocument.PrintPage
Related
I have records in my .NET WinForms app that I lay out with enhanced TextBox controls on panels when the records are editable, but I set the TextBoxes to ReadOnly when the records are not editable. Clicking the save button on an editable record saves the text to the database, and then it is displayed as an un-editable record (until the edit button is clicked). Please see the following screen grab:
As you can hopefully see, the first record is not editable, but the second one is. The problem I have is that I would like the TextBox to grow in Height if the text is too much to fit. It seems that the TextBox is doing the WordWrap, but it either only shows one line of the text or only the first two. Something is always cut off at the bottom.
I have looked at several other posts on this site, including, especially, Expandable WinForms TextBox.
Here is some sample code for the panel:
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
...
Field1 = new ExpandoField { Multiline = true, WordWrap = true };
Field1.Location = new System.Drawing.Point(42, 3);
if (CanEdit)
{
Field1.BackColor = System.Drawing.Color.White;
Field1.TabIndex = 20;
}
else
{
((ExpandoField) Field1).ReadOnly = true;
Field1.ForeColor = System.Drawing.Color.FromArgb(0, 50, 0);
Field1.BackColor = System.Drawing.Color.Snow;
Field1.TabIndex = 0;
Field1.TabStop = false;
}
Field1.Text = Text1;
Field1.Dock = DockStyle.None;
Field1.Size = new System.Drawing.Size(538 - 25, 34);
Field1.MinimumSize = Field1.Size;
Field1.AutoSize = true;
Controls.Add(Field1);
As you can see, I have AutoSize set to true for the panel. The code for Field2 is similar to Field1.
ExpandoField is based on sample code I saw from a response by dstran in Expandable WinForms TextBox. It seemed to be the most complete implementation of the suggestion marked as the answer to that post. Here's the code:
class ExpandoField : TextBox
{
private double m_growIndex = 0.0;
private Timer m_timer;
public ExpandoField()
{
AutoSize = false;
this.Height = 20;
// Without the timer, I got a lot of AccessViolationException in the System.Windows.Forms.dll.
m_timer = new Timer();
m_timer.Interval = 1;
m_timer.Enabled = false;
m_timer.Tick += new EventHandler(m_timer_Tick);
this.KeyDown += new KeyEventHandler(ExpandoField_KeyDown);
}
void ExpandoField_KeyDown(object sender, KeyEventArgs e)
{
if (e.Modifiers == Keys.Control && e.KeyCode == Keys.A)
this.SelectAll();
}
void m_timer_Tick(object sender, EventArgs e)
{
var sz = new System.Drawing.Size(Width, Int32.MaxValue);
sz = TextRenderer.MeasureText(Text, Font, sz, TextFormatFlags.TextBoxControl);
m_growIndex = (double)(sz.Width / (double)Width);
if (m_growIndex > 0)
Multiline = true;
else
Multiline = false;
int tempHeight = (int) (20 * m_growIndex);
if (tempHeight <= 20)
Height = 20;
else
Height = tempHeight;
m_timer.Enabled = false;
}
public override sealed bool AutoSize
{
get { return base.AutoSize; }
set { base.AutoSize = value; }
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
m_timer.Enabled = true;
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
m_timer.Enabled = true;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
m_timer.Enabled = true;
}
}
This is obviously not quite working. I have the panel set to AutoSize, but it is not growing to accomodate the second TextBox. Also, I need to somehow push the second TextBox down when the first one grows. Is there some good way for the panel to know when ExpandoField gets an OnSizeChanged event? It seems like the growth of that panel would then need to cause the remainder of the list of panels to be redrawn in lower locations. I'm not sure how to get this cascade effect to work right...
I also think the use of the timer seems like an inefficient kluge...
I'm still learning WinForms. Is there some well-designed way I can get the behavior that I want? Is there some event I can catch when the WordWrap takes place (or when the text exceeds the size of the TextBox)? That would allow me to resize the TextBox. And how does the TextBox let the panel know that it has changed? Does it need to call the OnSizeChanged handler for it's parent panel? Does the panel need to call the OnSizeChanged handler for it's parent list?
Any suggestions?
I believe I have the answer, after 3 or 4 failed attempts...
class ExpandoField : TextBox
{
private bool UpdateInProgress = false;
private static System.Text.RegularExpressions.Regex rgx = new System.Text.RegularExpressions.Regex(#"\r\n");
public delegate void CallbackFn();
CallbackFn VSizeChangedCallback;
public ExpandoField(CallbackFn VSizeChanged)
{
AutoSize = false;
VSizeChangedCallback = VSizeChanged;
this.KeyDown += new KeyEventHandler(ExpandoField_KeyDown);
}
public void ExpandoField_KeyDown(object sender, KeyEventArgs e)
{
if (e.Modifiers == Keys.Control && e.KeyCode == Keys.A)
this.SelectAll();
}
public void UpdateSize()
{
if (UpdateInProgress == false && Text.Length > 0)
{
UpdateInProgress = true;
int numLines = 0;
System.Drawing.Size baseSize = new System.Drawing.Size(Width, Int32.MaxValue);
System.Drawing.Size lineSize = baseSize; // compiler thinks we need something here...
// replace CR/LF with single character (paragraph mark '¶')
string tmpText = rgx.Replace(Text, "\u00B6");
// split text at paragraph marks
string[] parts = tmpText.Split(new char[1] { '\u00B6' });
numLines = parts.Count();
foreach (string part in parts)
{
// if the width of this line is greater than the width of the text box, add needed lines
lineSize = TextRenderer.MeasureText(part, Font, baseSize, TextFormatFlags.TextBoxControl);
numLines += (int) Math.Floor(((double) lineSize.Width / (double) Width));
}
if (numLines > 1)
Multiline = true;
else
Multiline = false;
int tempHeight = Margin.Top + (lineSize.Height * numLines) + Margin.Bottom;
if (tempHeight > Height || // need to grow...
Height - tempHeight > lineSize.Height) // need to shrink...
{
Height = tempHeight;
VSizeChangedCallback();
}
UpdateInProgress = false;
}
}
public override sealed bool AutoSize
{
get { return base.AutoSize; }
set { base.AutoSize = value; }
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
UpdateSize();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
UpdateSize();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
UpdateSize();
}
}
Note that on the constructor this subclass of TextBox now accepts a delegate callback to let the parent class know that the TextBox has changed its size. (I suppose I should have handled the possibility of a null value here...)
Thankfully, this solution no longer required a timer.
I have tested this code pretty well, and I have watched it both grow & shrink. It respects MaximumSize, and it even handles the presence of carriage-return/line-feed pairs. (This code assumes Windows; it should be trivial to modify it for Linux, etc.) Feel free to suggest improvements.
I have an application that prints how many bar codes you want, but if the amount of bar codes is bigger than the size of the PrintDocument it doesn't jump to the next page.
I'd like to know how can I add more pages or write in the next page of a PrintDocument.
I'm using a PrintPreview to display the PrintDocument in this Windows Form.
If you hookup the OnPrintPage event you can tell the PrintDocument if it needs to add another page on the PrintPageEventArguments.
IEnumerator items;
public void StartPrint()
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
items = GetEnumerator();
if (items.MoveNext())
{
pd.Print();
}
}
private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{
const int neededHeight = 200;
int line =0;
// this will be called multiple times, so keep track where you are...
// do your drawings, calculating how much space you have left on one page
bool more = true;
do
{
// draw your bars for item, handle multilple columns if needed
var item = items.Current;
line++;
// in the ev.MarginBouds the width and height of this page is available
// you use that to see if a next row will fit
if ((line * neededHeight) < ev.MarginBounds.Height )
{
break;
}
more = items.MoveNext();
} while (more);
// stop if there are no more items in your Iterator
ev.HasMorePages = more;
}
I've been trying to solve my issue for quite a while and to be honest am getting nowhere. What i would like is when the user clicks the 'top' button on my panel it automatically goes to the top( and swaps with the one there.) and when they click the bottom button it automatically goes to the bottom. I'm setting the index panel manually but of course this doesnt work because its only viable for one panel (i have ten). Greatly appreciate some help in finding a method that can send the panel to the top of the stack regardless of its position.
Here is a image (basic) to help understand
Control ctrlToMove = (Control)this.bookControls[bookName];
int ctrlToMoveIndex = bookPanel.Controls.IndexOf(ctrlToMove);
int ctrlToSwapIndex = ctrlToMoveIndex - 5;
Control ctrlToSwap = bookPanel.Controls[ctrlToSwapIndex];
this.bookPanel.Controls.SetChildIndex(ctrlToMove, ctrlToSwapIndex);
this.bookPanel.Controls.SetChildIndex(ctrlToSwap, ctrlToMoveIndex);
Based on your drawing, I made a UserControl with a button on it:
void uc_ButtonClicked(object sender, EventArgs e) {
UserControl1 uc = sender as UserControl1;
if (uc != null) {
int childIndex = flowLayoutPanel1.Controls.GetChildIndex(uc);
if (childIndex > 0) {
UserControl1 ucTop = flowLayoutPanel1.Controls[0] as UserControl1;
flowLayoutPanel1.Controls.SetChildIndex(uc, 0);
flowLayoutPanel1.Controls.SetChildIndex(ucTop, childIndex);
}
}
}
According to your picture you have one control per row in panel. Thus I suggest you to use TableLayoutPanel instead of FlowLayoutPanel. Also I'd create user control for items in panel. E.g. it will have name PriorityUserControl and four buttons to increase, decrease, maximize, minimize it's 'priority' (I placed buttons horizontally just to save place on screen):
Next, create four events in this user control:
public event EventHandler PriorityMaximized;
public event EventHandler PriorityIncreased;
public event EventHandler PriorityDecreased;
public event EventHandler PriorityMinimized;
And rise appropriate event when button clicked:
private void topButton_Click(object sender, EventArgs e)
{
if (PriorityMaximized != null)
PriorityMaximized(this, EventArgs.Empty);
}
That's it. We have user control which tells whether it want to move up or down. Now add user controls to TableLayoutPanel (either manually or dynamically) and subscribe same event handlers of these four events to ALL user controls. Something like:
// create user control and attach event handlers
PriorityUserControl control = new PriorityUserControl();
control.PriorityMaximized += priorityUserControl_PriorityMaximized;
control.PriorityMinimized += priorityUserControl_PriorityMinimized;
control.PriorityIncreased += priorityUserControl_PriorityIncreased;
control.PriorityDecreased += priorityUserControl_PriorityDecreased;
// add another row to table
panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
panel.RowCount = panel.RowStyles.Count;
// add control table layout panel
panel.Controls.Add(control);
panel.SetRow(control, panel.RowCount - 1);
Good. All you should do now is implement these event handlers. It's simple. E.g. decreasing priority (i.e. moving down):
private void priorityUserControl_PriorityDecreased(object sender, EventArgs e)
{
// sender is a control where you clicked Down button
Control currentControl = (Control)sender;
// get position in panel
var position = panel.GetPositionFromControl(currentControl);
// just to be sure control is not one at the bottom
if (position.Row == panel.RowCount - 1)
return;
// we want to switch with control beneath current
Control controlToSwitch = panel.GetControlFromPosition(0, position.Row + 1);
// move both controls
panel.SetRow(currentControl, position.Row + 1);
panel.SetRow(controlToSwitch, position.Row);
}
Now implementation of maximizing priority (i.e. moving to top):
private void priorityUserControl_PriorityMaximized(object sender, EventArgs e)
{
Control currentControl = (Control)sender;
var position = panel.GetPositionFromControl(currentControl);
if (position.Row == 0 || panel.RowCount < 2)
return;
Control topControl = panel.GetControlFromPosition(0, 0);
panel.SetRow(currentControl, 0);
panel.SetRow(topControl, position.Row);
}
I believe you will create rest two handlers by yourself.
The key of what you want is setting up a clear and extendable algorithm capable to deal with the different positions of the Panels. Here you have a simple code showing certain approach to this problem:
public partial class Form1 : Form
{
int[] panelLocations;
Point[] pointLocations;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
panelLocations = new int[5];
pointLocations = new Point[5];
panelLocations[1] = 1;
panelLocations[2] = 2;
panelLocations[3] = 3;
pointLocations[1] = new Point(panel1.Left, panel1.Top);
pointLocations[2] = new Point(panel2.Left, panel2.Top);
pointLocations[3] = new Point(panel3.Left, panel3.Top);
}
private void relocate(int curPanel, bool goTop)
{
int curLoc = panelLocations[curPanel];
int newLoc = curLoc - 1;
if (!goTop)
{
newLoc = curLoc + 1;
}
if (newLoc < 1) newLoc = 3;
if (newLoc > 3) newLoc = 1;
if (newLoc != curLoc)
{
int otherIndex = Array.IndexOf(panelLocations, newLoc);
panelLocations[curPanel] = newLoc;
relocatePanel(curPanel);
panelLocations[otherIndex] = curLoc;
relocatePanel(otherIndex);
}
}
private void relocatePanel(int curIndex)
{
if (curIndex == 1)
{
panel1.Location = pointLocations[panelLocations[1]];
}
else if (curIndex == 2)
{
panel2.Location = pointLocations[panelLocations[2]];
}
else if (curIndex == 3)
{
panel3.Location = pointLocations[panelLocations[3]];
}
}
private void buttonTop1_Click(object sender, EventArgs e)
{
relocate(1, true);
}
private void buttonBottom1_Click(object sender, EventArgs e)
{
relocate(1, false);
}
}
Open a new project, add 3 panels (Panel1, Panel2 and Panel3... better put different background colors) and include two buttons (buttonUp and buttonDown). This code will make the Panel1 to go up and down (by changing its position with the other panels).
The idea is pretty simple: at the start you store the positions of all the Panels in an array. In another array, you store where each panel is located every time (1 is the original position of Panel1, etc.).
It is a quite simple code which you can improve and extend as much as required, but the idea is pretty reliable and you can use it in any case: a set of fixed positions through which the panels will be moving.
I'm trying to add a button so that it will appear on top of a screen that I'm drawing, in an XNA/Silverlight Windows phone game.
Currently the map is drawing over it, so the button appears invisible. Does anyone know how I could fix this?
The Button switchScreen is created, but not initialized, earlier in the code.
Here is the code where I'm adding the button:
private void OnDraw(object sender, GameTimerEventArgs e)
{
#region CommonStuff
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
#endregion CommonStuff
if (is3D)
{
OnDraw3D(sender, e);
}
else
{
OnDraw2D(sender, e);
}
switchScreen = new Button();
switchScreen.Height = 20.0;
switchScreen.Width = 100.0;
switchScreen.Content = "Switch to Shooting Screen";
switchScreen.Margin = new Thickness(phoneScreen.Height - switchScreen.Width -
20.0, 20.0, 20.0, phoneScreen.Width - switchScreen.Height - 20.0);
switchScreen.Visibility = System.Windows.Visibility.Visible;
}
I'm only testing the OnDraw2D so here's the code for that:
private void OnDraw2D(object sender, GameTimerEventArgs e)
{
spriteBatch.Begin();
// TODO: Add your drawing code here
map.Draw(e.ElapsedTime, e.TotalTime);
// npc.Draw(gameTime);
}
and the map.Draw is here
public override void Draw(TimeSpan elapsedTime, TimeSpan totalTime)
{
// Draw the Sky
gamePage.getSpriteBatch().Draw(background, Position, Color.White);
foreach (Person person in people)
{
person.Draw(elapsedTime, totalTime);
}
base.Draw(elapsedTime, totalTime);
}
background is a Texture.2D and Position is a Vector2.
I think, since you're creating the button manually, you may need to use this code instead (I am assuming that you're using the Windows Forms Button control):
switchScreen.Location = new System.Drawing.Point(xLocation, yLocation);
switchScreen.Name = name;
switchScreen.Size = new System.Drawing.Size(xSize, ySize);
switchScreen.TabIndex = 0;
switchScreen.Text = text;
switchScreen.UseVisualStyleBackColor = true;
Controls.Add(switchScreen);
How are you implementing a Windows Form? As far as I know, you can't add a Button without an underlying form for it to be on.
Good day to all,
I have an application that creates a report for daily time record of an employee.
The problem is all of the pages in printpreview dialog were overwritting by every pages that available in printing. how can i possibly solve this kind of issue? The content of page 1 is for page 1, page 2 is for page 2 only and vise versa.
Here is my code for printing:
private void simpleButtonOk_Click(object sender, EventArgs e)
{
try
{
countPage = 0;
CoolPrintPreviewDialog printPreview = new CoolPrintPreviewDialog();
PrintDocument doc = new PrintDocument();
doc.PrintPage += new PrintPageEventHandler(doc_PrintPage);
printPreview.Document = doc;
Form p = (Form)printPreview;
p.WindowState = FormWindowState.Maximized;
p.ShowDialog();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void doc_PrintPage(object sender, PrintPageEventArgs e)
{
try
{
for (int i = 1; i <= countEmployee; i++)
{
Font fontName = new Font("Calibri", 12, FontStyle.Bold);
Font fontPosition = new Font("Calibri", 12, FontStyle.Regular);
Brush colorBrush = new SolidBrush(Color.Black);
e.Graphics.DrawString(empName, fontName, colorBrush, new Point(80, 120));
e.Graphics.DrawString(empPosition, fontPosition, colorBrush, new Point(80, 140));
e.Graphics.DrawString(empId, fontPosition, colorBrush, new Point(680, 120));
DataManipulation.PrintSelectEmployeeByMonth(i, e, comboBoxMonth, comboBoxYear, comboBoxDayFrom, comboBoxDayTo);
countPage++;
MessageBox.Show(countPage.ToString());
}
if (countPage <= countEmployee)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The PrintPage event is raised once for each page; your for loop suggests that you are trying to print all the pages whenever the event is raised.
You need to remove the for loop and promote the index variable i to an instance-level variable that you can use to track the current page number.
The remainder of your code appears to be correct; you must keep requesting more pages until the event has been fired countEmployee times.