I have a form which has a TableLayoutPanel with 6 rows like this:
Each label MouseClick event toggles the visible property of the panel below it (which resides in a separate row).
Which makes it look like this:
As you can see only the last row expands upwards to fill the now available space.
If I collapse the bottom or middle row, the row/s above do not expand downwards:
How can I make the following happen:
Visible rows will expand equally to fill the available space
Rows will move/expand downwards
I've tried all sorts of combinations with the row Absolute/Percent/AutoSize properties... But guessing this might need expansion of the TableLayoutPanel class?
I think I have a working example.
Put a panel on the form and anchored it all directions.
Put the tablelayoutpanel in the panel, set whatever size and anchor it all directions.
Put labels in rows 0, 2, 4.
Set label autosize = true.
Put panels in rows 1, 3, 5.
Anchor the panels in all directions. No docking or autosizing.
Set the tablelayoutpanel rows with labels to absolute and whatever height. I used 20 pixels.
Set the panel rows to Percent 33.33. The table automatically scales it to total 100% if you're under or over.
Label click events:
Public Class Form1
Private Sub Label1_Click(sender As System.Object, e As System.EventArgs) Handles Label1.Click
If TableLayoutPanel1.RowStyles(1).Height > 10 Then
TableLayoutPanel1.RowStyles(1).SizeType = SizeType.Absolute
TableLayoutPanel1.RowStyles(1).Height = 2
Else
TableLayoutPanel1.RowStyles(1).SizeType = SizeType.Percent
TableLayoutPanel1.RowStyles(1).Height = 33.33
End If
End Sub
Private Sub Label2_Click(sender As System.Object, e As System.EventArgs) Handles Label2.Click
If TableLayoutPanel1.RowStyles(3).Height > 10 Then
TableLayoutPanel1.RowStyles(3).SizeType = SizeType.Absolute
TableLayoutPanel1.RowStyles(3).Height = 2
Else
TableLayoutPanel1.RowStyles(3).SizeType = SizeType.Percent
TableLayoutPanel1.RowStyles(3).Height = 33.33
End If
End Sub
Private Sub Label3_Click(sender As System.Object, e As System.EventArgs) Handles Label3.Click
If TableLayoutPanel1.RowStyles(5).Height > 10 Then
TableLayoutPanel1.RowStyles(5).SizeType = SizeType.Absolute
TableLayoutPanel1.RowStyles(5).Height = 2
Else
TableLayoutPanel1.RowStyles(5).SizeType = SizeType.Percent
TableLayoutPanel1.RowStyles(5).Height = 33.33
End If
End Sub
End Class
I don't know if you need to put it in a panel to make it work. You could probably anchor it to the form and be fine.
You could also do this in one event handler by getting the row of the label clicked and adding 1.
Related
I am programmatically creating a GridViewColumn in WPF as follows:
Dim oGVCol As GridViewColumn = New GridViewColumn
Dim oHeaderTemplate As DataTemplate
oHeaderTemplate = New DataTemplate
Dim oGridFactory As FrameworkElementFactory = New FrameworkElementFactory(GetType(Grid))
oHeaderTemplate.VisualTree = oGridFactory
oGridFactory.SetValue(Grid.BackgroundProperty, Brushes.Transparent)
oGVCol.HeaderTemplate = oHeaderTemplate
(I have removed irrelevant code to set the content of the grid)
What I can't figure out is how to add a "click" event for the GridViewColumnHeader itself. I can add events to the Grid and any other Controls I added through the Factory objects, no problem. But I'm stuck on how to add an event to the header itself.
If you have a solution in VB.NET, great, but C# is fine too.
One (failed) attempt:
AddHandler TryCast(oGVCol.Header, GridViewColumnHeader).Click, AddressOf HeaderClick
Sadly it turns out that I cannot cast oGVCol.Header to a GridViewColumnHeader.
Ok, it may not be pretty, but I found a pretty decent solution to the problem.
Firstly, when I create the Grid Factory for the root element in the header's Visual Tree, I give it a name
oGridFactory.SetValue(Grid.NameProperty, <column name here>))
(Please note that the Names must have only letters, numbers and underscores so if your data contains column names that don't have those, you'll need to deal with that both here, to convert invalid names to valid ones, and below, to revert them back to their original names if necessary.... I won't detail that functionality here)
Also, I add an event handler to the Root "Grid" in the Template for the column header:
oGridFactory.AddHandler(Grid.SizeChangedEvent,
New SizeChangedEventHandler(AddressOf ColumnHeaderSizeChanged))
The "magic" happens in the procedure ColumnHeaderSizeChanged. This procedure is called both when the grid is Rendered the first time, but also when the user is manually resizing columns.
Signature:
Private Sub ColumnHeaderSizeChanged(sender As Object, e As SizeChangedEventArgs)
I keep a List(Of GridViewColumnHeaders) which is reset to an empty list when I need to replace the Grid with a different one. In the ColumnHeaderSizeChanged event I then do the following:
The first thing we need to do is get to the Root of the controls in the Column Header. For example, your column header may contain a TextBlock to show a column name, and icons to indicate it's been sorted up or down. That sort of thing. When the user clicks on the header they may be clicking on any of those controls, so:
Dim oParent As Object
Dim oColHeader As GridViewColumnHeader = Nothing
Dim sColHeaderName As String = String.Empty
Dim oGWH As Grid = Nothing
oParent = e.OriginalSource 'This may be any of the controls in the header.
If Not oParent Is Nothing Then
Try
While Not oParent.Parent Is Nothing
'So we keep going down the Tree until we hit the Root Parent
'which will be the main Grid created in the Grid Factory
oParent = oParent.Parent
End While
Catch
End Try
End If
'But at this point, if we still have a control, it will be the main Grid
If oParent Is Nothing Then
Exit Sub
End If
If TryCast(oParent, Grid) Is Nothing Then
'what the heck is this? This SHOULD be the Grid at the root of the Visual Tree,
'so if, for whatever reason, this is NOT a Grid, get outta here.
Exit Sub
End If
By this point we're on the main Grid, but now we need to get the GridViewColumnHeader into which this Grid has been created. So now we go to the TemplatedParent
While Not oParent.TemplatedParent Is Nothing
oParent = oParent.TemplatedParent
oColHeader = TryCast(oParent, GridViewColumnHeader)
If Not oColHeader Is Nothing Then
'This procedure is called both when the Grid View is first rendered,
'and when the user is dragging the column width.
If Mouse.LeftButton = MouseButtonState.Pressed Then
'Do something appropriate to when the user is resizing the column
Else
'Do something appropriate to when the grid
'is first Rendered or re-displayed
End If
Exit While
End If
End While
At this point we have the GridViewColumnHeader we need, so we can add it to the List and add a Handler for its Click event. moColHeaders is the List(Of GridViewColumnHeaders)
If Not oColHeader Is Nothing Then
If Not moColHeaders.Contains(oColHeader) Then
oColHeader.Name = <column name here>
moColHeaders.Add(oColHeader) 'Only need to add it once!
AddHandler oColHeader.Click, AddressOf HeaderClick
End If
End If
Now you can code your HeaderClick procedure to handle the GridViewColumnHeader.Click event.
Signature:
Private Sub HeaderClick(sender As Object, e As RoutedEventArgs)
I'm am trying to animate a box width to extend in both left and right direction.
Of course it's only extending in the right direction, how would I have it extend in both. Most WPF codes are in C# so I can't really find an answer from Google.
Imports System.Windows.Media.Animation
Class MainWindow
Private Sub EnlargeBtn_Click(sender As Object, e As RoutedEventArgs) Handles EnlargeBtn.Click
Dim NewWidth = 300
Dim widthAnimation As New DoubleAnimation(NewWidth, TimeSpan.FromMilliseconds(1000))
Box.BeginAnimation(WidthProperty, widthAnimation)
End Sub
End Class
You need to animate the left position as well as the width.
If you can change the left position you'll need to change the width by twice the amount you're changing the left property by to get the box to grow uniformly in both directions.
If you can't change the left or X property of a Rectangle (because it doesn't appear to have one you can access - MSDN) then the only solution might be to put it inside another container (a Grid or Border) and set it's HorizontalAlignment to Center.
Figured it out, had to set the HorizontalAlignment to Center and move the left and right margins by half the width
Imports System.Windows.Media.Animation
Class MainWindow
Private Sub EnlargeBtn_Click(sender As Object, e As RoutedEventArgs) Handles EnlargeBtn.Click
Dim NewWidth = 300
Dim widthAnimation As New DoubleAnimation(NewWidth, TimeSpan.FromSeconds(1))
Dim marginAnimation As New ThicknessAnimation(New Thickness(Box.Margin.Left - NewWidth / 2, Box.Margin.Top, Box.Margin.Right - NewWidth / 2, 0), TimeSpan.FromSeconds(1))
Box.BeginAnimation(WidthProperty, widthAnimation)
Box.BeginAnimation(MarginProperty, marginAnimation)
End Sub
End Class
here is my problem: i have created a main window called mainWindow in XAML (vb.net) and inside i have 2 buttons (valid and stop) and a grid in the center.
I have two others little windows (valid window and stop window) written in XAML (vb.net) which have buttons, textbox...
I want, when i click on valid button or stop button, display the valid window or the stop window inside the grid of my mainWindow, so i have this code in my mainWindow.vb:
enter code here
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
valid_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
Private Sub stop_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles stop.Click
Dim content As Object = stop_.Content
stop_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
So when i click on button valid in my mainWindow, it's ok it displays the valid window in my grid.
First problem: then when i click on button stop in my mainWindow, the stop window is placed just above the valid window, it is not nice, is there a way to clear the grid before display this second window?
And finally, the biggest problem: i need to click many times on valid button or stop button but when i click the second time i have a null reference exception: Me.Grid.Children.Add(content) content is null after the first call so i am only able to click one time on my button.
How can i fix it in order to click many times on my buttons please?
I give you thanks.
Yet again, someone writes some invalid code and then says why isn't this code working? If you look at your code, you should see something:
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
Me.Grid.Children.Add(content)
End Sub
I'm guessing that you have a private variable for your Valid Window as you call it: valid. On this line, what are you doing to your variable?:
valid_.Content = Nothing
That's right! You're setting its Content property to null. Therefore, I'm not really sure why you were surprised that it was null after the first attempt.
How can i fix it in order to click many times on my buttons please?
Try removing the line that sets the Content property to null.
UPDATE >>>
Your problem is really caused by the fact that you cannot display any UI element in two places at once. Your whole idea of copying the Window.Content to your DataGrid is entirely wrong, but in the name of brevity, your fix is simply to move your content back to the Window, rather than setting it to null each time. Try something like this:
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
If Me.Grid.Children.Count > 0 Then 'Put content back where it belongs
Dim oldContent = Me.Grid.Children(0)
stop.Content = oldContent
Me.Grid.Children.Remove(oldContent)
EndIf
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
valid_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
Please forgive any code mistakes, as I don't write VB.NET. Also, you'll need to update your other method likewise.
I have a textbox and DataGrid
While textbox loses the focus and if DataGrid is not Focused then I want to Hide the DataGrid.
I use the below code.
Private Sub txt_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles txt.LostFocus
If DataGrid1.IsFocused = False Then
DataGrid1.Visibility = Windows.Visibility.Hidden
End If
End Sub
using this code even if I click on any Item on DataGrid the DataGrid hides.
Is there any problem in my code?
I'm not sure what the problem is... the behaviour you describe is consistent with your code.
The behaviour might be different from what you would expect...
I think when the TextBox loses focus the DataGrid will never have focus because the TextBox didn't finish losing focus. Is that the problem?
If that's the problem, you may add some kind of delay before hiding DataGrid (in a non blocking way of course). You can create a new Thread, do a Sleep(500) on that thread before hiding the control, and see what happens.
You also need to take care because only the UI thread may change visible controls, but you may ask further help if you choose to do that.
I hope it helps.
When the textbox lostfocus even fired .. the gridview not focused yet ..
So, add something like this
Dim lDGVFocused as Boolean
Private Sub Datagrid1_Enter( ... ) ...
lDGVFocused = True
End Sub
Private Sub Datagrid1_LostFocus( ... ) ...
lDGVFocused = False
End Sub
Private Sub txt_LostFocus( ... ) ...
If not lDGVFocused then DataGrid1.Visible = False
End Sub
Private Sub txt_GotFocus( ... ) ...
DataGrid1.Visible = True
End Sub
I have a ComboBox that is databound to an ObservableCollection of strings. The ComboBox is also editable, so you can either enter in your own value or select one from the list. The issue I'm running into is the index of SelectedItem seems to be the index of the last item you selected when you've entered in your own value in the ComboBox, though it's -1 when you have IsTextSearchEnabled set to true.
The problem is, if someone entered their own value and then decide to instead select the item on the ComboBox that had been selected before, the index doesn't change so the SelectionChange event doesn't fire. How could I get an event to fire in this situation?
Test this...
I hope this helps:
Dim oldSEL As String = ""
'always checking while you move your mouse over the combobox (when altering selection) and using the keyboard to (alter selection)
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.MouseMove, ComboBox1.KeyPress
Dim currentSEL As String = ComboBox1.SelectedText
If Not (oldSEL = "" And currentSEL = oldSEL) Then
fire()
oldSEL = currentSEL
End If
End Sub
Private Sub fire()
Trace.Write("text selected changed")
End Sub
You should change all the Combobox1 to your liking.