Can I stack and toggle between Panels with transparent PNG's on top of each other? - vb.net

I am trying to make a form that allows multiple images to appear/disappear based on what buttons the user chooses. This example is just a test that I'm using to try and figure this out. My real project is more complicated, but the same principles apply, and I'm having the same issue.
I have 5 Transparent PNG's that I need to lay on top of one another to appear as one image. I put each one as the background image of a panel, with the backColor of the panel set to transparent. All looks good while in the form editor, but the images don't appear properly, and I don't know why.
I would just like for the buttons to either turn on or turn off the visibility of the image depending on it's state.
the odd part is, everything works as it should when the panels are not stacked on top of each other. But when I stack them, it acts up.
Here is what I have in place for everything:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PicA.Visible = False
PicB.Visible = False
PicC.Visible = False
PicD.Visible = False
End Sub
'Button All
Private Sub BtnAll_Click(sender As Object, e As EventArgs) Handles BtnAll.Click
If PicA.Visible = False Then
PicA.Visible = True
Else
PicA.Visible = False
End If
If PicB.Visible = False Then
PicB.Visible = True
Else
PicB.Visible = False
End If
If PicC.Visible = False Then
PicC.Visible = True
Else
PicC.Visible = False
End If
If PicD.Visible = False Then
PicD.Visible = True
Else
PicD.Visible = False
End If
End Sub
'Button A
Private Sub BtnA_Click(sender As Object, e As EventArgs) Handles BtnA.Click
If PicA.Visible = True Then
PicA.Visible = False
Else
PicA.Visible = True
End If
End Sub
'Button B
Private Sub BtnB_Click(sender As Object, e As EventArgs) Handles BtnB.Click
If PicB.Visible = True Then
PicB.Visible = False
Else
PicB.Visible = True
End If
End Sub
'Button C
Private Sub BtnC_Click(sender As Object, e As EventArgs) Handles BtnC.Click
If PicC.Visible = True Then
PicC.Visible = False
Else
PicC.Visible = True
End If
End Sub
'Button D
Private Sub BtnD_Click(sender As Object, e As EventArgs) Handles BtnD.Click
If PicD.Visible = True Then
PicD.Visible = False
Else
PicD.Visible = True
End If
End Sub
Here is a short clip of what happens when I run the form:
https://youtu.be/oG19RNJKjTU
I'm just getting more confused the more I look into it. Does the order of my code make a difference? does the order in which the panels are stacked make a difference?
If anyone knows what I'm doing wrong or if this isn't possible, I would really appreciate it.
Thanks

Just create one custom control, having 5 layers, and turn on/off the layers, and draw the visible ones on top of each other.
The following code creates a very simple image layers control. Each layer has a Visible and an Image property, and when the Visible of each is True it will be displayed otherwise it wont be displayed. The layers will be painted in the revers order of adding, so the last added layer is on top. After adding or removing layers or changing the visibility, Invalidate the control.
Here is the code which is a good start point. You may want to extend it later and add support for automatic refresh after modifying the layers, or add opacity to the layers, or add size mode (like PictureBox), and many other properties based on your requirement.
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing.Design
Public Class Layer
Public Property Image As Image
Public Property Visible As Boolean = True
End Class
Public Class ImageLayersControl
Inherits Control
Sub New()
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
DoubleBuffered = True
ResizeRedraw = True
End Sub
<Editor(GetType(CollectionEditor), GetType(UITypeEditor))>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public Property Layers As Collection(Of Layer) = New Collection(Of Layer)
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
For Each l As Layer In Layers
If (l.Visible And l.Image IsNot Nothing) Then
e.Graphics.DrawImage(l.Image, 0, 0)
End If
Next
End Sub
End Class
Then add layers in design-time or run-time and make the layers visible or invisible in design time or using code when you need:
Me.ImageLayersControl1.Layers(1).Visible = False
Me.ImageLayersControl1.Invalidate()
Note: The code requires you add reference to System.Design, or replace the editor attribute with <Editor("System.ComponentModel.Design.CollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", GetType(UITypeEditor))>.

Related

How to degrade similar tool strip button procedures to a single in VB.NET

I'm using ToolStrip and it's buttons to change GUI structure with an example code how i change GUIs with tool strip buttons. Are there any methods to use them more easy way like when clicked event of ToolStripButton handled it can call a single procedure etc.? In current case it seems i'm coding in a bad way.
For example if user click the Home button it highlights the button as selected and hides other panel elements and make visible Home's panel.
Private Sub tsbHome_Click(sender As Object, e As EventArgs) Handles tsbHome.Click
tsbHome.Checked = True
tsbTools.Checked = False
tsbReport.Checked = False
tsbAnalyze.Checked = False
'... Tool Strip Button lists continues...
pnlHome.Visible = True
pnlTools.Visible = False
pnlReport.Visible = False
pnlAnalyze.Visible = False
' ... Panel lists continues...
End Sub
if user click the Tools button it highlights the button as selected and hides other panel elements and make visible Tool's panel.
Private Sub tsbTools_Click(sender As Object, e As EventArgs) Handles tsbTools.Click
tsbHome.Checked = False
tsbTools.Checked = True
tsbReport.Checked = False
tsbAnalyze.Checked = False
'... Tool Strip Button lists continues...
pnlHome.Visible = False
pnlTools.Visible = True
pnlReport.Visible = False
pnlAnalyze.Visible = False
' ... Panel lists continues...
End Sub
There are two tricks to making this code much simpler. The first is knowing you can have more than one item in the Handles clause of an event method declaration. (You can also omit that clause, and use AddHandler to set up event handlers for a lot of controls to one method.) The other trick is knowing how to use the sender argument to determine which of the several controls connected to this method was used.
Put those together, and you get one method that will work to change to any of your views.
Private Sub NavigationMenuItem_Click(sender As Object, e As EventArgs) Handles tsbHome.Click, tsbTools.Click, tsbReport.Click, tsbAnalyzer.Click
'First Suspend Layout, to avoid extra screen re-draws
Me.SuspendLayout()
'Set your checkboxes
tsbHome.Checked = sender Is tsbHome
tsbTools.Checked = sender Is tsbTools
tsbReport.Checked = sender Is tsbReport
tsbAnalyze.Checked = sender Is tsbAnalyze
'Then De-select EVERYTHING
pnlHome.Visible = sender Is tsbHome
pnlTools.Visible = sender Is tsbTools
pnlReport.Visible = sender Is tsbReport
pnlAnalyze.Visible = sender Is tsbAnalyze
' ... lists continues...
'Finally, resume layout so all changes draw to the screen at once
Me.ResumeLayout()
End Sub
We can make this simpler if you add code to the Form Load or InitializeComponent() method to put the panels and toolstrip buttons into lists:
Private ViewButtons As List(Of ToolStripButton)
Private ViewPanels As List(Of Panel)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ViewButtons = New List(Of ToolStripButton) From {tsbHome, tsbTools, tsbReport, tsbAnalyze}
ViewPanels = New List(Of Panel) From {pnlHome, pnlTools, pnlReport, pnlAnalyze}
For Each b In ViewButtons
AddHandler b.Click, AddressOf NavigationMenuItem_Click
Next
End Sub
Private Sub NavigationMenuItem_Click(sender As Object, e As EventArgs)
Me.SuspendLayout()
For i As Integer = 0 To ViewButtons.Length - 1
Dim selected As Boolean = ViewButtons(i) Is sender
ViewButtons(i).Checked = selected
ViewPanels(i).Visible = selected
Next
Me.ResumeLayout()
End Sub

Add control in TablelayoutPanel row only

I want to add some control in Tablelayout row 2, but control is just added in Split Container instead and not Tablelayout Itself. TableLayout is inside Split Container. How can I add control only to TableLayoutPanel row 2 ? I use row 1 for different purposes, and I need It visible all the time. Here is my code for adding control to TableLayout :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim frm = Me 'Form2 that is currently opened too
If Form1.DataGridView1.Visible = True Then
Form1.DataGridView1.Visible = False
frm.TopLevel = False
Form1.TableLayoutPanel1.Controls.Add(frm, 0, 2)
frm.WindowState = FormWindowState.Maximized
frm.FormBorderStyle = FormBorderStyle.None
frm.Show()
Else 'Do nothing
End If
End Sub
Sorry, too fast on posting. Everything is ok, only problem was "frm.WindowState = FormWindowState.Maximized". I changed It to "frm.Dock = DockStyle.Fill". That solved my issue.

IndexOutOfRangeException error when DataGridView header is clicked

Iam trying to get DataGridView rowIndex and set it to textbox and all is well with this code
Private Sub dgv_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellContentClick
isitxt(e.RowIndex)
btnInsert.Enabled = False
btnUpdate.Enabled = True
btnDelete.Enabled = True
End Sub
and
Sub isitxt(ByVal x As Integer)
txtIDBarang.Text = dgv.Rows(x).Cells(0).Value
txtNamaBarang.Text = dgv.Rows(x).Cells(1).Value
cbJenisBarang.Text = dgv.Rows(x).Cells(2).Value
numHargaBeli.Value = dgv.Rows(x).Cells(3).Value
numHargaJual.Value = dgv.Rows(x).Cells(4).Value
End Sub
But i got IndexOutOfRangeException when i clicked on Column Header. how can i handle it ?
Note, that if you use CellContentClick, the code will be executed only if the user actually aims at text content of a cell. Usually a CellClick is makes more sense.
As for your code, you can debug and see, what's in "x", when you get an error - I guess "-1"... You can handle it then. However, the reason for this should not be in your code above.
You can also set SelectionMode = FullRowSelect and do it following way:
Private Sub dgv_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellClick
isitxt(Me.dgv.selectedRows(0).index)
btnInsert.Enabled = False
btnUpdate.Enabled = True
btnDelete.Enabled = True
End Sub
Unless you want to handle the cells separately, users usually prefer the FullRowSelect mode.

calling button class in a form in vb.net

I am herewith develop an application but I have created a class which calls button events(i.e. enable or visible)
Public Class ClassfrmLoad
Dim btnAdd As New Button
Dim btnEdit As New Button
Private Sub FormLoad()
Me.btnAdd.Visible = True
Me.btnAdd.Enabled = True
Me.btnEdit.Visible = True
Me.btnEdit.Enabled = False
End Sub
End Class
I am actually created the button events class(classfrmLoad) for the way which buttons should be enabled and visible when each & every form loading.
There are 6 buttons in the forms (frm1, frm2 etc.,) like btnAdd, btnEdit, btnCancel etc., i don't want to display the buttons(visible/enable) while loading the form.
Here is the question:
How can i call this class(classfrmLoad) events to alter (enable/visible) the buttons positioned in the forms(frm1, frm2, etc.,) and how to get the buttons events in those forms?
Dear Sergio,
Thanks for your immediate response. I am missing something and it is not as expected, here is my complete code for the form including yours.
This is the code you suggested for the classfrmLoad:
Public Class ClassfrmLoad
Public Shared Sub FormLoad(ByRef Target As Form)
For Each ctl As Control In Target.Controls
If Not TypeOf ctl Is Button Then Continue For
Dim btn As Button = DirectCast(ctl, Button)
Select Case btn.Name.ToLower()
Case "btnadd"
btn.Visible = True
btn.Enabled = True
Case "btnsave"
btn.Visible = False
btn.Enabled = False
Case "btnedit"
btn.Visible = True
btn.Enabled = True
Case "btncancel"
btn.Visible = True
btn.Enabled = False
Case "btnclose"
btn.Visible = True
btn.Enabled = True
Case "btnupdate"
btn.Visible = False
btn.Enabled = False
Case "btnfind"
btn.Visible = False
btn.Enabled = False
'and so on
End Select
Next
End Sub
End Class
This is the code for the formload event:
Private Sub frmCreate_Unit_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ClassfrmLoad.FormLoad(Me)
'Display software and title of the page
Me.Text = msgCap & " | " & " CREATE UNIT"
Me.lblComp.Text = CompName
Me.stBar_User.Text = frmMain.stBar_User.Text
Me.stBar_UserID.Text = frmMain.stBar_UserID.Text
Me.stBar_G_ID.Text = frmMain.stBar_G_ID.Text
Me.stBar_G_No.Text = frmMain.stBar_G_No.Text
Me.cboUnit.Visible = False
Me.txtUnit_Long.Visible = True
Me.btnfind.Visible = False
Me.txtUnit_ID.Enabled = False
Me.btnadd.Focus()
End Sub
Please help me to sort out this one. Thanks...
Every form in WinForms is a class instance, inherited from Form class.
Because of this, you cannot make static references to it's members from another class, given any Form instance.
Generic class won't cut it either because you're after specific class members.
I would take advantage of the Controls collection and would check it's name and type. Once caught, we can cast the control to button and set it's visibility and access.
For this, you should make small adjustments to your code:
Public Class ClassfrmLoad
Public Shared Sub FormLoad(ByRef Target As Form)
For Each ctl As Control In Target.Controls
If Not TypeOf ctl Is Button Then Continue For
Dim btn As Button = DirectCast(ctl, Button)
Select Case btn.Name.ToLower()
Case "btnadd", "btnedit"
btn.Visible = True
btn.Enabled = True
'and so on
End Select
Next
End Sub
End Class
Changed the FormLoad visibility and access. It should be shared so we don't need to create unnecessary instances of this class
Added a parameter, passe by reference that referrs the target form. This will assure that you can use it on any class that inherits Form class
Iterated the Controls collection and find a candidate based on it's type and name
When found, we cast it to Button and access it's properties normally.
It's just an example, based on your code.
You should now call it on every Form's formload handler, like so:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ClassfrmLoad.FormLoad(Me)
End Sub
Finally I got the solution. It is very simple and any way thanks to sergio.
Here is the complete code.
Public Class ClassfrmLoad
Public Shared Sub formLoad(ByVal btnAdd As Button, ByVal btnEdit As Button, ByVal btnSave As Button, ByVal btnCancel As Button, ByVal btnClose As Button, ByVal btnUpdate As Button, ByVal btnFind As Button)
btnAdd.Visible = True
btnAdd.Enabled = True
btnSave.Visible = False
btnSave.Enabled = False
btnCancel.Visible = True
btnCancel.Enabled = False
btnClose.Visible = True
btnClose.Enabled = True
btnEdit.Visible = True
btnEdit.Enabled = True
btnUpdate.Visible = False
btnUpdate.Enabled = False
btnFind.Visible = False
btnFind.Enabled = False
End Sub
End Class
And calling proceedure in a form like this:
Dim x As New ClassfrmLoad
ClassfrmLoad.formLoad(Me.btnAdd, Me.btnEdit, Me.btnSave, Me.btnCancel, Me.btnClose, Me.btnUpdate, Me.btnFind)

Mimic Disabled Focus Behaviour in WinForms

In accordance to this Post I'm trying to mimic the behavior of
Enabled = False
without actually disable the Control. (In my case a multiline TextBox)
The next I'm trying to accomplish is to mimic the focus behavior by mouse of a disabled control. If I click on a disabled control it won't get the focus and the control that previously had focus won't loose the focus.
What I came up with so far: I can intercept the WM_SETFOCUS message in WndProc so my control won't recieve focus.
Private Const WM_SETFOCUS = &H7
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If Me.ReadOnly AndAlso (m.Msg = WM_SETFOCUS) Then Exit Sub
MyBase.WndProc(m)
End Sub
The problem with that is, that the previous contol lost the focus, which isn't intended. How do I prevent that even the click by mouse will do anything in the focus behaviour? Is there any way to do this?
Update: 06.08.12
As suggested by Justin I solved the problem by changing it to a label in an autoscroll panel. A minimal code example is as followed:
Imports System.Windows.Forms
Public Class ScrollableDisabledTextBox
Inherits TextBox
Private xLabel As Label
Private xPanel As Panel
Public Sub New()
InizializeComponent()
End Sub
Private Sub InizializeComponent()
xPanel = New Panel
xPanel.AutoScroll = True
xPanel.BorderStyle = BorderStyle.FixedSingle
xLabel = New Label
xLabel.Enabled = False
xLabel.AutoSize = True
xPanel.Controls.Add(xLabel)
Me.Me_SizeChanged()
End Sub
Private Sub Me_EnabledChanged() Handles Me.EnabledChanged
If Me.Enabled Then
Me.Show()
xPanel.Hide()
Else
xPanel.Show()
Me.SendToBack()
Me.Hide()
End If
End Sub
Private Sub Me_TextChanged() Handles Me.TextChanged
xLabel.Text = Me.Text
End Sub
Private Sub Me_SizeChanged() Handles Me.SizeChanged
xPanel.Size = Me.Size
xLabel.MaximumSize = New System.Drawing.Size(xPanel.Size.Width, 0)
End Sub
Private Sub Me_ParentChanged() Handles Me.ParentChanged
xPanel.Location = Me.Location
'If parent changed multiple times, remember to remove panel from old parent!
If Not Me.Parent.Controls.Contains(xPanel) Then
Me.Parent.Controls.Add(xPanel)
End If
End Sub
End Class
I do not believe what you want to do is possible. If you do not have focus, then the scrolling will not work.
However, I posit that you should rethink your original problem. Why not use an AutoSize = true, MaximumSize.Width = ParentWidth label (which could be disabled) inside of a panel that will autoscroll. This sounds like what you are really looking for.