how to increase the tabs width to the mouse position - c#

I'm developing a browser relying on webbrowser winform component.
I used xtratabcontrol in order to surf with tabs (DevExpress component) which has close button in its tabs.
I'm trying to achieve what internet explorer(its last version) does when closing a tab ; the other tabs are extended(their width increases) to reach the mouse poisiton (to enable closing tab after tab without moving the mouse)
this is the code which I added ( in close button click event)
Point cursor = MousePosition;
int x = cursor.X;
int count = browserTabControl.TabPages.Count -1;// I don't want to include the last tab (which opens another tabs)
int width = x / count;
for (int i = 0; i < browserTabControl.TabPages.Count -1 ; i++)
browserTabControl.TabPages[i].TabPageWidth = width;
I also tried to get the tabs whole width before removing the last tab, then to divide it on the new tabs count , and to set the result to each tab :
int current_width = (browserTabControl.TabPages.Count - 1) * browserTabControl.TabPages[0].TabPageWidth;
//..............some code removing the last tab (actually the tab before the last tab)
//after removing the last tab
int count = browserTabControl.TabPages.Count - 1;
int width = current_width / count;
for (int i = 0; i < browserTabControl.TabPages.Count - 1; i++)
browserTabControl.TabPages[i].TabPageWidth = width;
the first code result
the second code result :
Probably the problem is when I divide int/int I lose the rest of the division, I can get a double result , but the TabPageWidth is int.

Try the following solution:
// Initialization
foreach(XtraTabPage page in xtraTabControl.TabPages) {
if(page == addNewTabPage) continue;
page.TabPageWidth = 100; // turn off headers auto-size
}
}
void xtraTabControl_CloseButtonClick(object sender, EventArgs e) {
ClosePageButtonEventArgs ea = e as ClosePageButtonEventArgs;
if(ea.Page != addNewTabPage) {
xtraTabControl.BeginUpdate();
((XtraTabPage)ea.Page).Dispose();
int totalWidth = 0;
var visiblePages =((IXtraTab)xtraTabControl).ViewInfo.HeaderInfo.VisiblePages;
int totalHeadersGrow = 0;
for(int i = 0; i < visiblePages.Count; i++) {
var pageInfo = visiblePages[i];
if(pageInfo.Page == addNewTabPage) continue;
totalWidth += pageInfo.Bounds.Width;
totalHeadersGrow += (pageInfo.Bounds.Width - pageInfo.Page.TabPageWidth);
}
int count = xtraTabControl.TabPages.Count - 1;
int width = totalWidth / count - totalHeadersGrow / (count + 1);
foreach(XtraTabPage page in xtraTabControl.TabPages) {
if(page == addNewTabPage) continue;
page.TabPageWidth = width;
}
xtraTabControl.EndUpdate();
}
}
P.S. You can contact DevExpress support directly (and I believe that this is the best way when running into issues when using their product) to get official answer in this regard.

You can use HeaderAutoFill feature for this. which will automatically fill the tabs to the client area; so there is no need for the user to move the mouse for closing multiple
this.xtraTabControl1.HeaderAutoFill = DevExpress.Utils.DefaultBoolean.True;

Related

Finding index of clicked image in a dynamically generated array of pictureboxes

I have code that creates a row of 5 pictureboxes at runtime. I have added (I think) the code to add a click event handler to each picturebox as it is created.
int xPos = 95;
for (int index = 0; index < 5; index++)
{
keepImage[index] = new PictureBox();
keepImage[index].Width = 120;
keepImage[index].Height = 41;
keepImage[index].Left = xPos;
keepImage[index].Top = 360;
keepImage[index].Click += new EventHandler(keepImage_Click);
keepImage[index].BackColor = Color.Transparent;
keepImage[index].SizeMode = PictureBoxSizeMode.CenterImage;
this.Controls.Add(keepImage[index]);
xPos += 125;
}
The code works - it creates and displays the pictureboxes. I have been looking on here to find out how to find which one of the pictureboxes is clicked on...
public void keepImage_Click(object sender, EventArgs e)
{
PictureBox index = sender as PictureBox;
// identify which button was clicked and perform necessary actions
Debug.Write(index);
}
This code was taken from a solution found on here, but how do I adapt it for my needs? I have tried but so far, no luck.
At runtime the debug shows System.Windows.Forms.PictureBox, SizeMode: CenterImage but not the actual index.
Thanks for any suggestions.
EDIT
After trying one of the solutions mentioned in the comments, I now get the following error...
You can follow second approach from Get the index of array of picturebox clicked. But there is a typo issue with this answer it should be (sender as PictureBox).
So in your case, you can use Control.Tag property to store index of pictures as:
int xPos = 95;
for (int index = 0; index < 5; index++)
{
//Other codes
keepImage[index].Tag = index; //Set tag from index
this.Controls.Add(keepImage[index]);
xPos += 125;
}
Then click event would be like:
public void keepImage_Click(object sender, EventArgs e)
{
int index = int.Parse((sender as PictureBox).Tag.ToString());
Debug.Write(index);
}

scrolling issue with huge points of stackedbar graph

I have chart with multiple chart areas and every area contain a bunch of series that need to be updated with scrolling, all chart areas working well except the one below , it takes alot of resources from the memory i think and not scrolling smoothly.
I want to achieve this chart area effectively with smooth scrolling
i achieved same results using stacked bar graph, i added the points from data table, here is how the data table looks like
And as i know displaying only the portion that is needed is certainly the option the takes least resources and time,
so here is the code i use to add my points from data table
private void GraphDemo_Load(object sender, EventArgs e)
{
System.Drawing.Rectangle workingRectangle =
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
this.chart.Size = new System.Drawing.Size(workingRectangle.Width - 50, 1000);
scroller = new VScrollBar();
scroller.Dock = DockStyle.Left;
croller.Maximum = 6000 - windowSize; //windowSize = 180;
scroller.LargeChange = 180;
scroller.Scroll += scroller_Scroll;
}
void scroller_Scroll(object sender, ScrollEventArgs e)
{
for (int s = 0; s < chart.Series.Count; s++)
{
LoadCore.chart.Series[s].Points.Clear();
}
for (float ii = 0; ii < dt.Rows.Count; ii++)
{
row = dt.Rows[(int)ii];
ts11 = Convert.ToDouble(row.ItemArray[0]) + 1; //top.value column + 1
ts12 = Convert.ToDouble(row.ItemArray[1]); //base.value column
for (double t = LoadCore.ts11; t <= LoadCore.ts12; t++)
{
//k is the index no. of each series column
for (int k = 2; k <= dt.Columns.Count - 1; k++)
{
if (scroller.Value < t)
{
if (scroller.Value + windowSize > t) //windowSize = 180;
{
//tempindex1 is the index of specific row value at specific base.value columnn
chart.Series[Convert.ToString(dt.Columns[k].ColumnName)].Points.AddXY(t, dt.Rows[tempIndex1][k]);
chart.Series[Convert.ToString(dt.Columns[k].ColumnName)]["PointWidth"] = "1";
}
}
}
}
}
}
With the above code i still can not scroll smoothly and i think it's because of the huge number of points needed to be added with the portion of 180 (window size), ...
so my question is there an alternative effective way to achieve the first image with smooth scrolling?.
should i use any other types of graph instead of stacked bar graph ? what is it?

Detect element and click this element on tag <canvas> in Selenium?

The website only has one tag is it contains all element.
I want to click on an element in this canvas.
I tried with code:
public void ClickCanvas(QA.IWebElement wbCanvas, int x, int y)
{
try
{
QA.Interactions.Actions actionBuilder = new QA.Interactions.Actions(wd);
Actions hoverClick = actionBuilder.MoveToElement(wbCanvas).MoveByOffset(x, y).ContextClick();
hoverClick.Build().Perform();
}
catch { throw; }
}
And I use this function:
var width = int.Parse(browser.FindElementById("easCanvas").GetAttribute("width"));
var height = int.Parse(browser.FindElementById("easCanvas").GetAttribute("height"));
for (int i = 0; i < height; i += 5)
{
for (int j = 0; j < width; j += 5)
{
browser.ClickCanvas(browser.FindElementById("easCanvas"), j,i);
}
}
It scans at browser only centre of the screen. Must start at Point(0, 0) of the screen instead of start at the centre of the screen.
And I don't know when an element is clicked.
RESOLVED:
In selenium document,
moveToElement(WebElement toElement) : Moves the mouse to the middle of the element.
moveToElement(WebElement toElement, int xOffset, int yOffset) : Moves the mouse to an offset from the top-left corner of the element.
There is the reason for my question:
It scans at browser only centre of the screen
I need at position (0, 0) to it start at begin of an element.
Should be changed to:
Actions hoverClick = actionBuilder.MoveToElement(wbCanvas, 0, 0).MoveByOffset(x, y).ContextClick();
And I don't know when an element clicked.
In my site, I test when clicking on the element it will open a new popup, this popup is contained <iframe> tag.
I need add code:
while(browser.FindElementById("iframe_D") == null)
Thread.Sleep(50);
if(browser.FindElementById("iframe_D") != null)
browser.GoToFrame(browser.FindElementById("iframe_D"));
It's working for me.
This may be weird, but try using a minus y figure and see what happens.
It worked for me recently on one of my projects.

How can I instantiate large number of buttons in Windows Forms?

I'm developing a theatre reservation software. I'm using Windows Forms, the seats is represented by a 2-dimensioned array. And I draw the buttons as following:
public void DrawSeats()
{
// pnl_seats is a Panel
pnl_seats.Controls.Clear();
// Here I store all Buttons instance, to later add all buttons in one call (AddRange) to the Panel
var btns = new List<Control>();
// Suspend layout to avoid undesired Redraw/Refresh
this.SuspendLayout();
for (int y = 0; y < _seatZone.VerticalSize; y++)
{
for (int x = 0; x < _seatZone.HorizontalSize; x++)
{
// Check if this seat exists
if (IsException(x, y))
continue;
// Construct the button with desired properties. SeatSize is a common value for every button
var btn = new Button
{
Width = SeatSize,
Height = SeatSize,
Left = (x * SeatSize),
Top = (y * SeatSize),
Text = y + "" + x,
Tag = y + ";" + x, // When the button clicks, the purpose of this is to remember which seat this button is.
Font = new Font(new FontFamily("Microsoft Sans Serif"), 6.5f)
};
// Check if it is already reserved
if (ExistsReservation(x, y))
btn.Enabled = false;
else
btn.Click += btn_seat_Click; // Add click event
btns.Add(btn);
}
}
// As said before, add all buttons in one call
pnl_seats.Controls.AddRange(btns.ToArray());
// Resume the layout
this.ResumeLayout();
}
But already with a seat zone of 20 by 20 (400 buttons), it spent almost 1 minute to draw it, and in debug I checked that the lack of performance, is the instantiation of the buttons.
There is a way to make it faster? Perhaps disable all events during the instatiation or another lightweight Control that has the Click event too?
UPDATE:
lbl was a test, the correct is btn, sorry.
UPDATE 2:
Here is the IsException and ExistsReservations methods:
private bool IsException(int x, int y)
{
for (var k = 0; k < _seatsExceptions.GetLength(0); k++)
if (_seatsExceptions[k, 0] == x && _seatsExceptions[k, 1] == y)
return true;
return false;
}
private bool ExistsReservation(int x, int y)
{
for (var k = 0; k < _seatsReservations.GetLength(0); k++)
if (_seatsReservations[k, 0] == x && _seatsReservations[k, 1] == y)
return true;
return false;
}
Suppose that you change your arrays for reservations and exclusions to
public List<string> _seatsExceptions = new List<string>();
public List<string> _seatsReservations = new List<string>();
you add your exclusions and reservations in the list with something like
_seatsExceptions.Add("1;10");
_seatsExceptions.Add("4;19");
_seatsReservations.Add("2;5");
_seatsReservations.Add("5;5");
your checks for exclusions and reservations could be changed to
bool IsException(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsExceptions.Contains(key);
}
bool ExistsReservation(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsReservations.Contains(key);
}
of course I don't know if you are able to make this change or not in your program. However consider to change the search on your array sooner or later.
EDIT I have made some tests, and while a virtual grid of 20x20 buttons works acceptably well (31 millisecs 0.775ms on average), a bigger one slows down noticeably. At 200x50 the timing jumps to 10 seconds (1,0675 on average). So perhaps a different approach is needed. A bound DataGridView could be a simpler solution and will be relatively easy to handle.
I also won't use such a myriad of controls to implement such a thing. Instead you should maybe create your own UserControl, which will paint all the seats as images and reacts on a click event.
To make it a little easier for you i created such a simple UserControl, that will draw all the seats and reacts on a mouse click for changing of the state. Here it is:
public enum SeatState
{
Empty,
Selected,
Full
}
public partial class Seats : UserControl
{
private int _Columns;
private int _Rows;
private List<List<SeatState>> _SeatStates;
public Seats()
{
InitializeComponent();
this.DoubleBuffered = true;
_SeatStates = new List<List<SeatState>>();
_Rows = 40;
_Columns = 40;
ReDimSeatStates();
MouseUp += OnMouseUp;
Paint += OnPaint;
Resize += OnResize;
}
public int Columns
{
get { return _Columns; }
set
{
_Columns = Math.Min(1, value);
ReDimSeatStates();
}
}
public int Rows
{
get { return _Rows; }
set
{
_Rows = Math.Min(1, value);
ReDimSeatStates();
}
}
private Image GetPictureForSeat(int row, int column)
{
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
return Properties.Resources.emptySeat;
case SeatState.Selected:
return Properties.Resources.choosenSeat;
default:
case SeatState.Full:
return Properties.Resources.fullSeat;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
var row = (int)(e.X / widthPerSeat);
var column = (int)(e.Y / heightPerSeat);
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
_SeatStates[row][column] = SeatState.Selected;
break;
case SeatState.Selected:
_SeatStates[row][column] = SeatState.Empty;
break;
}
Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int row = 0; row < Rows; row++)
{
for (int column = 0; column < Columns; column++)
{
var seatImage = GetPictureForSeat(row, column);
e.Graphics.DrawImage(seatImage, row * widthPerSeat, column * heightPerSeat, widthPerSeat, heightPerSeat);
}
}
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
private void ReDimSeatStates()
{
while (_SeatStates.Count < Rows)
_SeatStates.Add(new List<SeatState>());
if (_SeatStates.First().Count < Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count < Columns)
columnList.Add(SeatState.Empty);
while (_SeatStates.Count > Rows)
_SeatStates.RemoveAt(_SeatStates.Count - 1);
if (_SeatStates.First().Count > Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count > Columns)
columnList.RemoveAt(columnList.Count - 1);
}
}
This will currently draw forty rows and columns (so there are 800 seats) and you can click on each seat to change its state.
Here are the images i used:
EmtpySeat:
ChoosenSeat:
FullSeat:
If you anchor this control and resize it or you click on a seat to change its state there can be some minor lacking for the repainting if you further increase the number of rows or columns, but that is still somewhere far below one second. If this still hurts you, you have to improve the paint method and maybe check the ClipRectangle property of the paint event and only paint the really needed parts, but that's another story.
Rather than using actual button controls, just draw the image of the seats then when the user clicks on a seat translate the mouse X,Y coordinates to determine which seat was clicked. This will be more efficient. Of course, the drawback is that you have to write the method to translate x,y coordinates to a seat, but that really isn't that difficult.
EDIT; it has been pointed out to me this will not work in Windows Forms!
Well, you are Sequentially working through it.
if one iteration costs 1 sec, the full process will take 400*1 in time.
Perhaps you should try and make a collection of your objects, and process it 'parallel'.
try the .Net framework (4 and above) 'parallel foreach' method:
http://msdn.microsoft.com/en-s/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx
edit: so, if you have a list buttonnames, you can say
buttonNames.ForEach(x=>CreateButton(x));
while your CreateButton() method is as following:
private Button CreateButton(string nameOfButton) { Button b = new
Button(); b.Text = nameOfButton; //do whatever you want...
return b; }

Remove controls of flowlayoutpanel and recreating it in C#

I had been experimenting on writing a code to generate images inside a FlowLayoutPanel.
This is what i had done so far, when i click on a button for the first time (by using a checkboxes - read in number of images to open), it will generate the images, when i click on the button on second try, it will not update the flowlayoutpanel.
Even though i tried to remove the controls inside the FlowLayoutPanel, it still doesn't show the second try of the images.
This is the code snippet of the method:
FlowLayoutPanel fwPanel = null;
private void btnOpenFile_Click(object sender, EventArgs e)
{
//if there is content inside the flowpanel, dump it
if (fwPanel != null)
{
listOfFile.Clear();
}
//create a new FLP
fwPanel = new FlowLayoutPanel();
int panelWidth = width * 4 + 50;
int panelHeight = height * 4 + 50;
fwPanel.Size = new Size(panelWidth, panelHeight);
fwPanel.Location = new Point(0, 0);
this.Controls.Add(fwPanel);
//each checked item would be stored into an arraylist
foreach(object itemChecked in clbFile.CheckedItems)
{
listOfFile.Add((clbFile.Items.IndexOf(itemChecked)+1).ToString());
}
int noOfCheckedFile = listOfFile.Count;
PictureBox[] listOfPicture = new PictureBox[noOfCheckedFile];
int positionX = 0, positionY = 0;
int maxPaddingX = (width * MATRIX_SIZE) - 1;
int maxPaddingY = (height * MATRIX_SIZE) - 1;
//dynamically create images.
for (int i = 0; i < noOfCheckedFile; i++)
{
listOfPicture[i] = new PictureBox();
listOfPicture[i].Image = resizeImage((Image)show_picture(Convert.ToInt32(listOfFile[i])), new Size(width, height));
listOfPicture[i].Size = new Size(width, height);
if (positionX > maxPaddingX)
{
positionX = 0;
positionY += height;
}
if (positionY > maxPaddingY)
{
positionY = 0;
}
listOfPicture[i].Location = new Point(positionX,positionY);
listOfPicture[i].Visible = true;
fwPanel.Controls.Add(listOfPicture[i]);
positionX += width;
}
}
show_picture is a method that takes in and integer and returns a bitmap image.
listOfFile is to trace which file to return.
listOfPicture is to store each images.
i tried replacing this lines
//if there is content inside the flowpanel, dump it
if (fwPanel != null)
{
listOfFile.Clear();
}
i have added this line into it, when i do a second click, everything just gone missing, but this is not what i want, because it does not re-populating the FlowLayoutPanel.
if (fwPanel != null)
{
fwPanel.SuspendLayout();
if (fwPanel.Controls.Count > 0)
{
for (int i = (fwPanel.Controls.Count - 1); i >= 0; i--)
{
Control c = fwPanel.Controls[i];
c.Dispose();
}
GC.Collect();
}
fwPanel.ResumeLayout();
listOfFile.Clear();
}
I also tried this, but on second click, nothing will happen.
if (fwPanel != null)
{
List<Control> listControls = fwPanel.Controls.Cast<Control>().ToList();
foreach (Control control in listControls)
{
fwPanel.Controls.Remove(control);
control.Dispose();
}
listOfFile.Clear();
}
I wonder if i miss out anything, can someone enlighten me on what did i miss out ? Or guide me for the best practice of doing this.
as Suggested, i shifted the creation outside (credit to Sinatr for spotting it)
FlowLayoutPanel fwPanel = new FlowLayoutPanel();
private void createFLP()
{
int panelWidth = width * 4 + 50;
int panelHeight = height * 4 + 50;
fwPanel.Size = new Size(panelWidth, panelHeight);
fwPanel.Location = new Point(0, 0);
this.Controls.Add(fwPanel);
}
that solves the nothing happen part. Followed by using this to remove controls
if (fwPanel != null)
{
List<Control> listControls = fwPanel.Controls.Cast<Control>().ToList();
foreach (Control control in listControls)
{
fwPanel.Controls.Remove(control);
control.Dispose();
}
listOfFile.Clear();
}
and everything works like a charm, hope that this answer will be able to help others who are facing the same problem too.

Categories