Extend Width In Both Directions - wpf

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

Related

Resizing TableLayoutPanels when row collapsed/hidden

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.

Helixtoolkit Rotate GeometryModel3D using mouse in all 3 axis

The goal is to measure a GeometryModel3D boundary (width, depth, height) but in some situations the model orientation is not correct so I want to allow the user to correct the orientation using the mouse with an interface similar to the camera controller (In inspect mode) in order for the user to correctly align the model.
I was able to achieve this to some extent using the RotateManipulator class but this limits to only correct the orientation in one axis. Furthermore I derived from the class and implemented my own mouse handler to change the RotateTransform3D axis but the result is not what I expect.
Imports System.Windows.Data
Imports System.Windows.Media.Media3D
Imports System
Imports System.Windows
Imports System.Windows.Input
Imports HelixToolkit
Imports HelixToolkit.Wpf
Public Class RenderRotateManipulator
Inherits HelixToolkit.Wpf.RotateManipulator
Private m_model As GeometryModel3D
Private m_transform As New RotateTransform3D
Private m_clickedPoint As Point
Public Sub New(ByRef g As GeometryModel3D)
MyBase.New
m_model = g
Me.TargetTransform = m_transform
UpdateGeometry()
End Sub
Protected Overrides Sub UpdateGeometry()
Me.Model.Geometry = m_model.Geometry
Me.Model.Material = m_model.Material
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
Dim currentPoint As Point = e.GetPosition(Me.ParentViewport)
MyBase.OnMouseMove(e)
If (Me.IsMouseCaptured) Then
Me.Model.Transform = Me.TargetTransform
If Not IsNothing(m_clickedPoint) Then
Dim delta As Point = currentPoint - m_clickedPoint
Dim angle As Double = Math.Atan2(delta.Y, delta.X)
'Console.WriteLine("Angle=" & angle * Math.PI / 180 & " Clicked point=" & m_clickedPoint.ToString & " CurrentPoint=" & currentPoint.ToString)
Me.Axis = New Vector3D(0, Math.Sin(angle), Math.Cos(angle))
End If
End If
End Sub
Protected Overrides Sub OnMouseDown(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseDown(e)
m_clickedPoint = e.GetPosition(Me.ParentViewport)
End Sub
End Class
Another approach I was thinking is to use the perpective camera controller and use the difference of two camera locations as you move the camera (which looks as if the Model is rotating) and calculate the transformation from these two deltas which I can apply to the Model. But I'm not sure the Math required. I had a look at the RotateHandler.cs (Rotate camera using 'Turntable' rotation.) but a bit of direction would be greatly appreciated.

WPF - Image Control Actual Size

I have a problem in getting ActualHeight and ActualWidth of image control in WPF. When user selects the image file, I want to resize the selected image based on the dimensions of the image control.
I tried to get the Image.ActualHeight and Image.ActualWidth when window initializes, but I found that both properties of Image control are '0'.
So how to get the dimensions of the image control.
the remarks for FrameworkElement.ActualHeight say that there might be some lag before the property has its real value.
This property is a calculated value
based on other height inputs, and the
layout system. The value is set by the
layout system itself, based on an
actual rendering pass, and may
therefore lag slightly behind the set
value of properties such as Height
that are the basis of the input
change.
The final size of your control is set by FrameworkElement.Arrange(-Override). You could override the method and just call the base class implementation. Its return value will be the actual size of your Image.
Off the top of my head, I think you should subscribe to the Load event on the image control, the ActualHeight/Width are not updated until that event fires.
The control's ActualSize is set after the "Measure" layout pass (the "Arrange" layout pass sets its location). The other two answers are helpful; the "Arrange" layout pass of the container only happens after its children have been measured, and the load handler of your image control should be called after its first layout pass has completed.
The Best solution I have found is to wait until after the Image has loaded.
Private Sub Update_imgImage(tURI As Uri)
imgImage.LayoutTransform = New ScaleTransform(scaleX:=1, scaleY:=1)
Dim src As BitmapImage = New BitmapImage()
src.BeginInit()
src.UriSource = tURI
src.CacheOption = BitmapCacheOption.OnLoad
src.EndInit()
imgImage.SetCurrentValue(Image.SourceProperty, src)
AddHandler src.DownloadCompleted, AddressOf ImageDownloadCompleted
End Sub
Then for ImageDownloadCompleted i have the following:
Sub ImageDownloadCompleted(sender As Object, e As System.EventArgs)
Dim src As BitmapImage
src = DirectCast(sender, BitmapImage)
Dim scaleXY As Double
If sender.Width = 0 Then Exit Sub
'default width is 600 for my item, if changed, then resize
If sender.Width <> 600 Then
scaleXY = 500 / sender.Width
imgImage.LayoutTransform = New ScaleTransform(scaleX:=scaleXY, scaleY:=scaleXY)
Else
imgImage.LayoutTransform = New ScaleTransform(scaleX:=1, scaleY:=1)
End If
RemoveHandler src.DownloadCompleted, AddressOf ImageDownloadCompleted
End Sub
I hope this works for you.

WPF refresh TreeView when it loses the focus

I have a problem with my TreeView in a WPF application (Framework 3.5 SP1).
It's a TreeVIew with 2 Levels of Data. I expand / collapse the items of the first level in a particular way (with a single mouse-click on the TreeViewItem). Again when I expand a first-level TreeViewItem, I add some second-level TreeViewItems to the group (it's an important detail, infact if I don't add the items the problem doesn't occur). All works good until the TreeView loses focus.
If, for example, I expand the TreeViewItem at the first position, adding at the same time one element to the second-level, then I click on a button (to let the TreeView lose the focus), and then I click again on the TreeViewItem at the third position to expand it, the TreeViewItem that results from the hit-test with the mouse position is not the "real" TreeViewItem (in this case the third), but a TreeViewItem which is in an higher position than the one clicked (in this case the second).
I have tried to use the UpdateLayout method on the TreeView-LostFocus event, but without results. Probably I need a method that does the opposite: starting from the UI, refresh the object that contains the position of the TreeViewItems.
Can you, please, help me?
Thank you!
Pileggi
This is the code:
' in this way I tried to put remedy at the problem, but it doesn't work.
Private Sub tvArt_LostFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles tvArt.LostFocus
Me.tvArt.UpdateLayout()
e.Handled = True
End Sub
' here I expand / collapse the items of the first level of my TreeView
Private Sub tvArt_PreviewMouseUp(ByVal sender As System.Object, ByVal e As MouseButtonEventArgs) Handles tvArt.PreviewMouseUp
Dim p As Point = Nothing
Dim tvi As TreeViewItem = getItemFromMousePosition(Of TreeViewItem)(p, e.OriginalSource, Me.tvArt)
If tvi Is Nothing = False Then
If tvi.HasItems Then
Dim be As BindingExpression = BindingOperations.GetBindingExpression(tvi, TreeViewItem.ItemsSourceProperty)
Dim ri As P_RicambiItem = DirectCast(be.DataItem, P_RicambiItem)
If ri.isExpanded = False then
' here I add items to the second level collection
End If
ri.isExpanded = Not ri.isExpanded
End If
End If
e.Handled = True
End Sub
Private Function getItemFromMousePosition(Of childItem As DependencyObject)(ByRef p As Point, ByVal sender As UIElement, _
ByVal _item As UIElement) As childItem
p = sender.TranslatePoint(New Point(0, 0), _item)
Dim obj As DependencyObject = DirectCast(_item.InputHitTest(p), DependencyObject)
While obj Is Nothing = False AndAlso TypeOf obj Is childItem = False
obj = VisualTreeHelper.GetParent(obj)
End While
Return DirectCast(obj, childItem)
End Function
I have find this solution (but I don't like it very much). The problem is depending from the added items that wpf, for some reasons, doesn't remember that exist. Then I do a "manual" refresh with a method that clear and re-add all the items in the source-collection:
Public Sub RefreshData(ByVal RicambiListPass As ObservableCollection(Of P_RicambiItem))
Dim l As New List(Of P_RicambiItem)
l.AddRange(RicambiListPass)
_RicambiList.Clear()
For Each i As P_RicambiItem In l
_RicambiList.Add(i)
Next
End Sub

WPF ListView Databound Drag/Drop Auto Scroll

I've been working with Bea's solution here for a while and finding it very helpful. Problem now I'm having is when I drag-n-drop items within or to another ListView control and I want to scroll up/down "during" the drag (moving an item from index 30 to index 1), it's not happening. I would have to drag to the top of the visual items in the ListView, manually scroll up, then drag again, eventually ending at the position I want. This isn't very user friendly.
Now I found the function (DragDropHelper.DropTarget_PreviewDragOver) that I would want to do the testing of which item is being dragged over, and I'm getting that.
Dim pt As Point = e.GetPosition(DirectCast(Me.targetItemsControl, UIElement))
' Perform the hit test against a given portion of the visual object tree.
Dim result As HitTestResult = VisualTreeHelper.HitTest(Me.targetItemsControl, pt)
Now from there I can get the DependencyProperty of this visual hit
Dim lvi As ListViewItem = TryCast(GetDependencyObjectFromVisualTree(TryCast(result.VisualHit, DependencyObject), GetType(ListViewItem)), ListViewItem)
Which is of a ListViewItem. Now in the function DropTarget_PreviewDragOver I have the "DraggedItem" which is of type Picture in Bea's example, but that can change depending on the ObservableCollection you have bound to the ListView. Now, I want to drag the ListView up or down depending on where the mouse is on the control. I've attempted with the below un-finished non-working code
If lvi IsNot Nothing Then
If pt.Y <= 25 Then
Dim lv As ListView = TryCast(targetItemsControl, ListView)
If lv IsNot Nothing Then
Dim index As Integer = lv.Items.IndexOf(lvi)
If index > 1 Then
lv.ScrollIntoView(lv.Items(index - 1))
End If
End If
Else
If pt.Y >= Me.targetItemsControl.ActualHeight - 25 Then
Debug.Print("Scroll Down")
End If
End If
End If
Can someone point me in the right direction to get this ItemsControl or ListView to scroll when dragging over the items??
Thanks!
I'm still messing around with this exact same issue too. I'm using a slightly modified version of Bea's Drag and Drop found here, which is in VB instead of C#. When I used ScrollIntoView as described above, I could scroll down but not up. So I messed around and came up with this as my DropTarget_PreviewDragOver:
Private Sub DropTarget_PreviewDragOver(ByVal sender As Object, ByVal e As DragEventArgs)
Dim draggedItem As Object = e.Data.GetData(Me.m_format.Name)
Me.DecideDropTarget(e)
If (Not draggedItem Is Nothing) Then
If (TypeOf m_targetItemsControl Is ListBox) Then
Dim lb As ListBox = CType(m_targetItemsControl, ListBox)
Dim temp As Integer = m_insertionIndex
Dim scroll As ScrollViewer = Utilities.GetScrollViewer(lb)
If scroll.VerticalOffset = temp Then
temp -= 1
End If
If temp >= 0 And temp <= (lb.Items.Count - 1) Then
lb.ScrollIntoView(lb.Items(temp))
End If
End If
Me.ShowDraggedAdorner(e.GetPosition(Me.m_topWindow))
Me.UpdateInsertionAdornerPosition()
End If
e.Handled = True
End Sub
and I had to include this utility function, taken from here
Public Shared Function GetScrollViewer(ByVal listBox As ListBox)
Dim scroll_border As Decorator = CType(VisualTreeHelper.GetChild(listBox, 0), Decorator)
If (TypeOf scroll_border Is Decorator) Then
Dim scroll As ScrollViewer = CType(scroll_border.Child, ScrollViewer)
If (TypeOf scroll Is ScrollViewer) Then
Return scroll
Else
Return Nothing
End If
Else
Return Nothing
End If
End Function
which is great and all. Then running out what theuberk mentioned above with the adorner moving, and in the spirit of making this easy for someone else later, I added a variable to the DragDropAdorner class:
Private m_mouseDelta As Point
Added this to the last line of DragSource_PreviewMouseLeftButtonDown:
Me.m_mouseDelta = e.GetPosition(m_sourceItemContainer)
And turned ShowDraggedAdorner into:
Private Sub ShowDraggedAdorner(ByVal currentPosition As Point)
If (Me.m_draggedAdorner Is Nothing) Then
Dim adornerLayer As AdornerLayer = adornerLayer.GetAdornerLayer(Me.m_topWindow.Content)
Me.m_draggedAdorner = New DraggedAdorner(Me.m_draggedData, DragDropBehavior.GetDragTemplate(Me.m_sourceItemsControl), m_topWindow.Content, adornerLayer)
End If
Me.m_draggedAdorner.SetPosition((currentPosition.X - m_mouseDelta.X), (currentPosition.Y - m_mouseDelta.Y))
End Sub
What I did was took advantage of the ListBox.ScrollIntoView method. Basically, when you update your drop target, you can just call this method on it and wpf will do all the work. All you need to know is the index of the drop target item. This handles both vertical and horizontal scrolling.
this.listView.ScrollIntoView(this.listView.Items[index]);
When you use this method, your adorner might move with the scrolling ListBox. To fix this, I just set my adorner parent and adorner layer parent to the content of the window at the top of the visual tree (i.e. topWindow.Content).
Another possibility to scroll is to use the ScrollBar-Commands. You can do this without climbing down the VisualTree. If you have a styles ListBox with no border the GetScrollViewer()-Method would not work anymore.
I use the first Element of the ItemContainerGenerator as CommandTarget for the ScrollBar.LineXXXCommand:
Point p = e.GetPosition(itemsControl);
IInputElement commandTarget = itemsControl.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement;
if (commandTarget != null)
{
if (p.Y < OFFSET_TO_SCROLL)
ScrollBar.LineUpCommand.Execute(null, commandTarget);
else if (p.Y > itemsControl.ActualHeight - OFFSET_TO_SCROLL)
ScrollBar.LineDownCommand.Execute(null, commandTarget);
if (p.X < OFFSET_TO_SCROLL)
ScrollBar.LineLeftCommand.Execute(null, commandTarget);
else if (p.X > itemsControl.ActualWidth - OFFSET_TO_SCROLL)
ScrollBar.LineRightCommand.Execute(null, commandTarget);
}
Calling the LineXXXCommands is similiar to clicking the Arrow-Buttons of a ScrollBar: The ScrollViewer scrolles by a certain ammount which you can configure by setting the 'SmallAmount' Property of the ScrollBar.

Resources