|
Hi
If I add a panel graphically in VS2010 I can make it display an image but if I manually add a panel by writing code I can't make it visible. Do I have to associate it to the form?
I have given the manually defined panel an x,y,width and height and I true'ed the visible flag.
Painfully
Tom
|
|
|
|
|
hmmm, I as missing:
// Add the Panel control to the form.
this.Controls.Add(panel1);
RESOLVED
|
|
|
|
|
myForm.Controls.Add(myPanel);
is what either Visual Designer or you have to add to make the Panel show on the Form (assuming reasonable property values have been set, such as Location and Size).
|
|
|
|
|
Hey Thanks,
Will you let me in on how you manipulate the bitmapdata - that is ...working with the two spectrogram sections? I have the panel shown and can fill-in data.
I'm looking and looking and .NET is so huge and hard to get into I think.
I guess I can solve this by copying data around in a loop but that doesn't seem elegant!
Cheers
Tom
modified on Wednesday, September 22, 2010 8:50 PM
|
|
|
|
|
In my simple test I plotted a black sine wave on a yellow background; I did not use BitmapData, I kept a Graphics open on the Bitmap, and just did (plot is the double-buffered Panel):
private void AddNewData() {
int w=plot.Width;
int h=plot.Height;
int x=dataCount%w;
bmg.DrawLine(Pens.Yellow, x, 0, x, h);
int y=(int)(h*(0.5+0.45*Math.Sin(dataCount*0.07)));
if (oldy<0) oldy=y;
bmg.DrawLine(Pens.Black, x, oldy, x, y);
oldy=y;
dataCount++;
}
When you need fast access to the individual pixels of one column, you would not use SetPixel; instead enable unsafe code, do LockBits/UnlockBits, use a byte pointer, and advance it by Bitmap.Stride to go to another row. Examples are plenty, Bob Powell (Google!) has good ones.
|
|
|
|
|
Aaah, ok!
So you are doing a spectrum-analyzer (frequency vs gain) and not a spectrogram (frequency vs. time vs. gain) - like this one:
http://www.youtube.com/watch?v=UcBDSoVs42M[^]
Or am I missing something?
I have the bitmapdata ready and also the FFT data ready to be injected and then I need to move data around inside the bitmapdata to make the shift of the bitmap. I need to do this the fastest way!
for now I will just do it the heavy way using a loop. Would be better though to copy blocks around using a rectangle() suggestions are appriciated!
Thx
Tom
|
|
|
|
|
I wasn't doing any spectrumthingy, I was trying what resembles a curve plotter, or a continuous oscilloscope (one channel), however rather than just line plotting, I used the circular bitmap approach we discussed before (so nothing gets copied by me) and two Graphics.DrawImage().
If the new column needs more than a simple "set entire background"+"set a little foreground line segment", then you probably need to copy in an entire column, hence the LockBits and pointer stuff suggestion. Link.[^]
Turning a bitmap-based oscilloscope into something more complex should not affect flickering, as the panel is double-buffered anyway, so what happens on the screen remains the same.
BTW: nice movie!
|
|
|
|
|
Hi Luc
I'm about to give up on the panel approach and just use the dramimage-method and live with the flickering. Below you can see me panel approach...it does work but it flickers just a much as the drawimage approach.
Do you mind taking a look at my code and tell me what I'm doing wrong? I'm not into the terminology of windows programming so I'd appreciate a no-brainer description on how to do this
My drawing code looks like this:
g = pe.Graphics;
int H = myBitmap.Height;
int W = myBitmap.Width;
Rectangle rect = new Rectangle(0, 0, W, H);
myBitmapData = myBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, myBitmap.PixelFormat);
IntPtr ptr = myBitmapData.Scan0;
int bytes = myBitmapData.Stride * H;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int ii = 0; ii < H; ii++)
for (int jj = (ii + 1) * (3 * W) - 1; jj > (ii * W * 3) + 3; jj -= 3)
{
rgbValues[jj] = rgbValues[jj-3];
rgbValues[jj-1] = rgbValues[jj-4];
rgbValues[jj-2] = rgbValues[jj-5];
}
int offset = W * 3;
for (int counter = 0; counter < H; counter += 1)
{
rgbValues[counter * offset] = (byte)rnd.Next(255);
rgbValues[counter * offset + 1] = (byte)rnd.Next(255);
rgbValues[counter * offset + 2] = (byte)rnd.Next(255);
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
myBitmap.UnlockBits(myBitmapData);
myPanel.Location = new Point(XREF, YREF);
myPanel.Width = myBitmap.Width;
myPanel.Height = myBitmap.Height;
myPanel.BackgroundImage = myBitmap;
this.Controls.Add(myPanel);
My panel class (read that double buffering with a panel should be done like this):
public class DoubleBufferPanel : Panel
{
public DoubleBufferPanel()
{
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
this.UpdateStyles();
}
}
Hope to hear from you
Thanks
Tom
|
|
|
|
|
Hi Tom,
1.
in your DoubleBufferPanel constructor, all you need is this.DoubleBuffered = true;
I think it is equivalent to what you have though.
2.
I'm a bit confused by your code:
this.Controls.Add(myPanel);
if that really sits inside the Paint handler you are adding a panel each time, which may result in the Panel being repainted twice, thrice, etc later on, causing unnecessary screen activity, hence flickering.
3.
I'm horified by:
myPanel.BackgroundImage = myBitmap;
What you do here is, whenever the system thinks it needs to repaint the panel you also give it a new background image, causing it to need yet another repaint. So it is probably repainting continuously.
4.
And finally I'm disappointed by:
We already established there was absolutely no need to do that. With a circular bitmap, update "the next" column, then paint both halves at the right position.
In summary:
- create a doublebuffered panel once
- add it to your form once
- never use the BackgroundImage
- have an updateData() method that stores new pixels in one column of the bitmap (I kept a Graphics to do that)
- in your Paint handler, have only two statements, both e.Graphics.DrawImage() with the correct parameters.
Warning: you cannot keep LockBits open forever as DrawImage would then fail, so if you use LockBits, you must call UnlockBits at the end of the column update, otherwise you can't have it painted. You can keep a Graphics open on the bitmap though.
|
|
|
|
|
Hi Luc
Hmmm, maybe I should just realize that I'm not at a sufficient level to pull this off
1. I will try your suggestion also!
2. The reason for doing the this.controls.add(myPanel) was because if I instantiated a panel and didn't do this then I never got to see it in the windows form.
3. This was the only way for me to see the bitmap inside the panel. Searched google and didn't find any other options so I thought this was the right way. what is the right way to do it?
4. Yeah, I'm disappointed too. I read your replies again and again and didn't really understand the circular buffer handling scheme... Guess I should have asked for that explicitly.
If you have the full code I'd be happy to see it.
I'm thinking about going back to OpenGL I think that is easier for me since I once succeeded doing this in that framework.
Correction: I DO understand your circular buffer handling in principle (I basically do it that way every day for audio processing at work) but not within a panel in .NET.
Thanks for your effort
Tom
modified on Saturday, September 25, 2010 11:52 AM
|
|
|
|
|
OK, here are the most relevant chunks of my experiment, all is in the MainForm class; I'll probably crank out another little article by the end of next week:
private Bitmap bmPlot;
private int dataCount;
private BackgroundWorker bgw;
private bool running;
private int oldy=-1;
private Graphics bmg;
private void plotter(object sender, PaintEventArgs e) {
if (bmPlot!=null) {
Graphics g=e.Graphics;
int w=plot.Width;
int h=plot.Height;
int x=dataCount%w;
g.DrawImage(bmPlot, new Rectangle(0, 0, w-x, h), new Rectangle(x, 0, w-x, h), GraphicsUnit.Pixel);
g.DrawImage(bmPlot, new Rectangle(w-x, 0, x, h), new Rectangle(0, 0, x, h), GraphicsUnit.Pixel);
}
}
private void btnStart_Click(object sender, EventArgs e) {
log("btnStart_Click");
btnStop.Enabled=true;
btnStart.Enabled=false;
running=true;
dataCount=0;
oldy=-1;
int w=plot.Width;
int h=plot.Height;
bmPlot=new Bitmap(w, h);
bmg=Graphics.FromImage(bmPlot);
bmg.FillRectangle(Brushes.Yellow, 0, 0, w, h);
plot.Refresh();
bgw=new BackgroundWorker();
bgw.DoWork+=worker;
bgw.ProgressChanged+=reporter;
bgw.WorkerReportsProgress=true;
log("starting bgw");
bgw.RunWorkerAsync();
}
and all the code that belongs to the BGW, which takes care of modifying the bitmap content (one column at a time):
private void worker(object sender, object e) {
while (running) {
AddNewData();
bgw.ReportProgress(0);
Thread.Sleep(30);
}
}
private void reporter(object sender, object e) {
log("reporter");
plot.Invalidate();
}
private void AddNewData() {
int w=plot.Width;
int h=plot.Height;
int x=dataCount%plot.Width;
bmg.DrawLine(Pens.Yellow, x, 0, x, h);
int y=(int)(h*(0.5+0.45*Math.Sin(dataCount*0.07)));
if (oldy<0) oldy=y;
bmg.DrawLine(Pens.Black, x, oldy, x, y);
oldy=y;
dataCount++;
}
|
|
|
|
|
Hi Luc
You are a true gentleman..and fast responder too
Actually your bitmap manipulation looks the same as the code I did initially (presented in the other thread) so I was happy to see some similarity. The threading coding I'm not familar with but I think that I might get where I want if I can just declare and use the panel the same way as you do. You code doesn't show that unfortunately.
I'd hate to ask you for more help because I know I've been asking you too much...so if you just send me a notification when you post your article next week (or whenever it is shared) I'd be happy.
Added: Hey it looks like that the picturebox should be added to the panel itself ...I thought one could put in a bitmap directly on the panel. Hopefully doing that will solve my flickering problem!
Thanks for your help and patience
Tom
modified on Saturday, September 25, 2010 5:46 PM
|
|
|
|
|
There is nothing much special about the panel, except it should be a DoubleBufferedPanel.
I know of three ways to get it on the Form:
1.
make sure that class is already compiled (maybe in a separate DLL) and you can design it in with Visual Designer. This is the official approach.
2.
or add a Panel with Visual Designer, close the Form, and edit the form.designer.cs file changing new Panel() into new DoubleBufferedPanel()
(not recommended, one isn't supposed to edit a designer file)
3.
or add a Panel with Visual Designer, and have it replaced at run-time. That is what I did, like so, in the Form's constructor.
Panel oldPlot=plot;
plot=new DoubleBufferedPanel();
plot.Bounds=oldPlot.Bounds;
Controls.Remove(oldPlot);
Controls.Add(plot);
plot.Paint+=plotter;
The disadvantage is you have to copy all Designer properties/events from the old to the new Panel.
The alternative is to leave the Panel, and turn it into a double-buffered one by setting some ControlStyles (the DoubleBuffered property being protected cannot be modified).
|
|
|
|
|
Hello Experts,
Good day/Good Evening Experts I am here again to ask some few questions.
I would like to ask if why does my sqldataAdapter do not refresh the query it fill my datatable.
I am making a borrow transaction for borrowing a book where in a student or ... borrow. The quantity of book will decrease depending on the quantity of book borrowed.My problem is that my sqlDataAdapter to load the table of my bookRecords does not refresh what it need to fill.Confuse why does it refresh my sql query.
Below here is my code:
'save the borrow transaction
Public Sub SaveBorrowTransaction(ByVal txtBorrowerID As TextBox, ByVal txtLastName As TextBox, _
ByVal txtFirstName As TextBox, ByVal txtMI As TextBox, _
ByVal txtContactNo As TextBox, ByVal cboPosition As ComboBox, _
ByVal txtYear As TextBox, ByVal txtSection As TextBox, _
ByVal txtLibrarianLastName As TextBox, ByVal txtLibarianFirstName As TextBox, _
ByVal txtLibrarianMI As TextBox, ByVal cboNoOfDays As ComboBox, _
ByVal txtTransactionID As TextBox, _
ByVal lvItems As ListView, ByVal con As SqlConnection)
Try
Dim dt As New DataTable
Dim ds As New DataSet
ds.Tables.Add(dt)
Dim da As New SqlDataAdapter
con.Open()
da = New SqlDataAdapter("Select * from BorrowTransactionTable", con)
da.Fill(dt)
Dim dt2 As New DataTable
Dim ds2 As New DataSet
ds2.Tables.Add(dt2)
Dim da2 As New SqlDataAdapter
da2 = New SqlDataAdapter("Select * from transactionitemstable", con)
da2.Fill(dt2)
Dim dt3 As New DataTable
Dim ds3 As New DataSet
ds3.Tables.Add(dt3)
Dim da3 As New SqlDataAdapter
Dim j As Integer = 0
While j <> lvItems.Items.Count
'for borrow transaction Dim newRow As DataRow = dt.NewRow
With newRow
.Item("txtTransactionID") = txtTransactionID.Text
.Item("txtBorrowerID") = txtBorrowerID.Text
.Item("txtLibrarianLastName") = txtLibrarianLastName.Text
.Item("txtLibrarianFirstName") = txtLibarianFirstName.Text
.Item("txtLibrarianMI") = txtLibrarianMI.Text
.Item("txtLastName") = txtLastName.Text
.Item("txtFirstName") = txtFirstName.Text
.Item("txtMI") = txtMI.Text
.Item("txtSection") = txtSection.Text
.Item("txtYear") = txtYear.Text
.Item("txtContactNo") = txtContactNo.Text
.Item("txtPosition") = cboPosition.Text
.Item("txtItemNO") = lvItems.Items(j).SubItems(0).Text
.Item("txtRemarks") = "Borrowed"
.Item("intNoDay") = Val(cboNoOfDays.Text)
.Item("intQuantity") = 1
'update changes to quantity of book catalogue records when borrowing///////////////////
da3 = New SqlDataAdapter("Select * from BookCatalogueTable where txtAccessionNo='" & lvItems.Items(j).SubItems(0).Text & "'", con)
da3.Fill(dt3)
dt3.Rows(0).BeginEdit()
dt3.Rows(0).Item("intQuantity") = Val(dt3.Rows(0).Item("intQuantity")) - 1
dt3.Rows(0).EndEdit()
Dim cb3 As New SqlCommandBuilder(da3)
da3.Update(dt3)
.Item("dateDateBorrow") = Now.Date
.Item("dateDueDate") = Now.AddDays(Val(cboNoOfDays.Text))
.Item("dateDateTransaction") = Now.Date
End With
dt.Rows.Add(newRow)
Dim cb As New SqlCommandBuilder(da)
da.Update(dt)
'for transaction items Dim newrow2 As DataRow = dt2.NewRow
With newRow2
.Item("txtTransactionNo") = txtTransactionID.Text
.Item("txtItemNo") = lvItems.Items(j).SubItems(0).Text
.Item("txtItemTitle") = lvItems.Items(j).SubItems(1).Text
.Item("txtItemAuthor") = lvItems.Items(j).SubItems(2).Text
.Item("txtItemDecription") = lvItems.Items(j).SubItems(3).Text
.Item("txtTransactionStatus") = "Borrow"
End With
dt2.Rows.Add(newrow2)
Dim cb2 As New SqlCommandBuilder(da2)
da2.Update(dt2)
j = j + 1
End While
con.Close()
MsgBox("Transaction is Complete")
Catch ex As Exception
MsgBox(ex.ToString)
con.Close()
End Try
End Sub
Any Comments or Suggestion are so much appreciated.
Thanks,
Dan
|
|
|
|
|
Which of the three DataAdapters are you asking about? What are you expecting to happen? It doesn't even appear you are using the first one. You are creating DataSets then never using them. I would say this could also be handled better with a stored procedure. And lastly, you're using VB
I know the language. I've read a book. - _Madmatt
|
|
|
|
|
Hello,
Sorry bout not giving good info for my question, the problem came from my 3rd dataAdapter
at this part:
'update changes to quantity of book catalogue records when borrowing///////////////////
da3 = New SqlDataAdapter("Select * from BookCatalogueTable where txtAccessionNo='" lvItems.Items(j).SubItems(0).Text & "'", con)
da3.Fill(dt3)
dt3.Rows(0).BeginEdit()
dt3.Rows(0).Item("intQuantity") = Val(dt3.Rows(0).Item("intQuantity")) - 1
dt3.Rows(0).EndEdit()
Dim cb3 As New SqlCommandBuilder(da3)
da3.Update(dt3)
My da3 suppose to load again the new query while its on a loop block so that I can Subtract those quantity value,but my da3 doesn't refresh the query that will load new records.
For example my da3 select query at first is "Select * from BookCatalogueTable where txtAccessionNo='1'". After I update the table the new da3 select query for example become "Select * from BookCatalogueTable where txtAccessionNo='2'".My da3 does not execute the next query instead it still using the first query.
If my question is not yet clear I can still clarify to the best I can.
I'm really confuse why my da3 doesn't load the new query.
All comments and suggestion are soo much appreciated.
Thanks,
Dan
|
|
|
|
|
As I said, this is better handled with other methods, i.e. not using a DataAdapter for everything. You creating far too much overhead and I'm sure your code doesn't perform well. Use a stored proc to handle this, or use SqlCommands directly.
I know the language. I've read a book. - _Madmatt
|
|
|
|
|
Hello experts,
I would like to ask if how can I show some specific records only in my listview coming from my database
table.My Database is Sql Server 2005 and I am using vb 2008 as the programming language.
Scenerio:
BorrowTable
ID | Last Name |
1 | mrDan
1 | mrDan
2 | mrsDan
2 | mrsDan
I have a table in my database with name BorrowTable as shown above for example, my problem is I would
like to show the transaction records of people in table.To be more precise bec I cant so much
explain my problem I want to display it like this way in my listview
ID | Last Name |
1 | mrDan
2 | mrsDan
Below here is the code, its working but it just show all the records I have tried "group by" as select query to my sqlDataAdapter but it's not working.
Public Sub LoadListViewReturn(ByVal lvReturnList As ListView, ByVal con As SqlConnection)
Try
If con.State = ConnectionState.Open Then
con.Close()
End If
Dim dt As New DataTable
Dim ds As New DataSet
ds.Tables.Add(dt)
Dim da As New SqlDataAdapter
con.Open()
da = New SqlDataAdapter("Select * from BorrowTransactionTable ", con)
da.Fill(dt)
For Each r As DataRow In dt.Rows
lvReturnList.Items.Add(r("txtTransactionID"))
lvReturnList.Items(lvReturnList.Items.Count - 1).SubItems.Add(r("txtLastName"))
lvReturnList.Items(lvReturnList.Items.Count - 1).SubItems.Add(r("txtFirstName"))
lvReturnList.Items(lvReturnList.Items.Count - 1).SubItems.Add(r("txtMI"))
lvReturnList.Items(lvReturnList.Items.Count - 1).SubItems.Add(r("txtPosition"))
Next
con.Close()
Catch ex As Exception
MsgBox(ex.ToString)
con.Close()
End Try
End Sub
Any comments and suggestions are so much appreciated.
Thanks,
Dan
|
|
|
|
|
So you want the DISTINCT[^] keyword. You'd better replace the asterisk by the actual list of fields you want returned then, and specify which of those MUST be distinct.
|
|
|
|
|
Hello,
Thank you so much this is really the answer I was looking for
Thanks also the link it was super helpfull.Now I can finish the System I am doing,
I am stuck in that part due to filtering my listview.
Thank so much,
Dan
|
|
|
|
|
You're welcome.
|
|
|
|
|
So, my neighbor, who I didn't realize knew I was a 'computer person' came around last night and asked if I would be able to take a look at a friends USB drive to see if I could get anything off it. Wanting to make friends with my neighbors I said I could take a look. So, if the USB drive isn't being recognized (what I would think most users would term "not working"), then unless her USB controller fried, I'm going to have a hard time investigating something I can't communicate with.
That aside, it got me onto the thought of file recovery. So, I have a few questions;
Is .NET able to communicate with hardware at a low enough level that I could query the contents of a piece of hardware? (I know about the IO namespace and the ability to enumerate drives etc.). If not, am I picking back up C++?
How do I go about finding resources on teaching me how to investigate, verify and re-assemble(if necessary) files. I went 4-5 pages into google searches and was just seeing data-recovery software/companies.
|
|
|
|
|
hammerstein05 wrote: Is .NET able to communicate with hardware at a low enough level that I could query the contents of a piece of hardware?
If there's an API for it, you can p/invoke it. Bear in mind that C++ will use the API then .NET should be able to - I'm not saying that it's the right tool for the job, but you can do it.
|
|
|
|
|
|
You might want to post this against the OP so that they receive the email. They won't be notified of your reply to my answer.
|
|
|
|
|