WPF Scrollviewer scroll to child element not working - wpf

Here is what I am trying to do. I have a Scrollviewer and nested inside it are UserControls, nested in other UserControls, inside grids, stackpanels and other containers. When I click a button that adds yet another child somewhere within this hierarchy I would like to scroll to see said child. Code of function below:
public static void ScrollParentNamedScrollViewerDown(DependencyObject child, string strTargetParent, DependencyObject newStartPoint = null)
{
if(child == null) return;
if(newStartPoint == null) newStartPoint = child;
ScrollViewer scvPotentialTarget = GetParentOfType<ScrollViewer>(newStartPoint);
if (scvPotentialTarget == null) return;
if (scvPotentialTarget.Name != strTargetParent)
{
ScrollParentNamedScrollViewerDown(child, strTargetParent, scvPotentialTarget);
}
else
{
UIElement scrollTarget = child as UIElement;
if (scrollTarget == null)
scvPotentialTarget.ScrollToBottom();
else{
Point pTarget = scrollTarget.TranslatePoint(new Point(0, 0), scvPotentialTarget);
if (pTarget == null)
scvPotentialTarget.ScrollToBottom();
else
scvPotentialTarget.ScrollToVerticalOffset(pTarget.Y);
}
}
This function is used like this:
gbSubWindow.Visibility = System.Windows.Visibility.Visible;
gbSubWindow.Content = uc;
ScrollParentNamedScrollViewerDown(gbSubWindow, "OmsWindowScrollViewer");
where gbSubWindow was a previously empty, hidden expander at the bottom of a nested UserControl
For some reason I am getting ridiculously low values for the vertical offset when I execute the TranslatePoint function - my scrollviewer scrollheight is near 800, and I am adding a child that will show up at the very bottom beneath 2 large child control and I am getting values in 160ish range.
Anyone have any ideas, what is happening here?

In case that is the answer
BringIntoView on the control
Since UserControl derives from FrameWorkElelement it should work.
I have only used it for ListItem
FrameworkElement.BringIntoView Method

Related

Visual Tree Finder is returning null while searching for Data Grid

I have Tab Control which has many tab items, I am checking Data Grid Items Count while closing tab items. For the first time it works fine(I mean in first iteration). After closing one tab item, in second iteration sellDtg is null. Does anyone know why it is happening? I am concerning that this is UI problem, layout is not being refreshed. Please help me or show direction.
while (tc.HasItems)
{
TabItem ti = tc.SelectedItem as TabItem;
if (ti.Header == "Продажа")
{
Microsoft.Windows.Controls.DataGrid sellDtg = FindChild<Microsoft.Windows.Controls.DataGrid>(tc, "SellDataGrid");
if (sellDtg.Items.Count > 0)
{
Sell sl = new Sell();
if (Sell.basketfromSellDateListBox == false)
{
sl.ClearBasket(sellDtg);
Sell.ClearFromSellBasket((int)sellDtg.Tag);
}
}
}
if (ti != null)
tc.Items.Remove(ti);
}
Thanks in advance!!!
I've written a simple FindChildLogical function in analogy for LogicalTreeHelper below:
public static T FindChildLogical<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
if (parent == null) return null;
var child = LogicalTreeHelper.FindLogicalNode(parent, childName);
return (T)child;
}
and you call it as:
Microsoft.Windows.Controls.DataGrid sellDtg = FindChildLogical<Microsoft.Windows.Controls.DataGrid>(ti, "SellDataGrid");
I hope it gets you where you intend to.
I am going to assume your FindChild method uses the VisualTreeHelper to find its children.
In the first iteration, the TabItem's Content has been through a layout pass, and is visible. This means that the TabItem's Content will be in the visual tree.
However, for the other tab items, their Content hasn't been through a layout pass (it is only added to the visual tree when it's parent gets selected, and this has to then go through a layout/render pass), and won't be in the visual tree.
There are a couple of ways to get the child content of a TabItem that hasn't been through a layout pass as the selected tab:
1) You can try using the LogicalTreeHelper to find the Grid you're looking for (and you will likely have to search the Content of the TabItem, not the TabControl).
2) You can take your code out of the while loop, and do a callback on the dispatcher at the Loaded priority:
void RemoveAllItems()
{
if (!tc.HasItems) return;
TabItem ti = tc.SelectedItem as TabItem;
if (ti.Header == "Продажа")
{
var sellDtg = FindChild<Microsoft.Windows.Controls.DataGrid>(tc, "SellDataGrid");
if (sellDtg.Items.Count > 0)
{
Sell sl = new Sell();
if (Sell.basketfromSellDateListBox == false)
{
sl.ClearBasket(sellDtg);
Sell.ClearFromSellBasket((int)sellDtg.Tag);
}
if (ti != null)
tc.Items.Remove(ti);
}
}
Dispatcher.BeginInvoke(new Action(RemoveAllItems), DispatcherPriority.Loaded);
}
If you use the second method, you will likely be able to see the tab items removed one at a time, which may be something you don't want to see.

Dynamic spacing between controls in the Toolbar in WPF

I have a toolbar which contains several controls, say 10.
I want to place last three controls in the right corner of the tool bar with some space between these three controls and remaining controls.
When I resize the window, it should reduce the space, once there is no space it should be cliped to overflow drop down one by one.
Note: I need to use only one toolbar.
I did this once before
The items existed in a customized ScrollViewer so they could go off-screen, and the SizeChanged event of the ScrollViewer would check each item to see if it was off-screen or not. If it wasn't visibile, or was partially visible, then the item would get hidden and the data object would get added to a Menu. If the item was visible, it would remove it from the Menu if it existed there, and set it visible. The Menu was only visible if it had items.
This was done a while back when I was still new to WPF, so I'm sure the process could be improved on.
The helper class I used to determine item visibility is listed below
public static ControlVisibility IsObjectVisibleInContainer(FrameworkElement child, UIElement parent)
{
GeneralTransform childTransform = child.TransformToAncestor(parent);
Rect childSize = childTransform.TransformBounds(new Rect(new Point(0, 0), new Point(child.Width, child.Height)));
Rect result = Rect.Intersect(new Rect(new Point(0, 0), parent.RenderSize), childSize);
if (result == Rect.Empty)
{
return ControlVisibility.Hidden;
}
else if (result.Height == childSize.Height && result.Width == childSize.Width)
{
return ControlVisibility.Full;
}
else if (result.Height == childSize.Height)
{
return ControlVisibility.FullHeightPartialWidth;
}
else if (result.Width == childSize.Width)
{
return ControlVisibility.FullWidthPartialHeight;
}
else
{
return ControlVisibility.Partial;
}
}
public enum ControlVisibility
{
Hidden,
Partial,
Full,
FullHeightPartialWidth,
FullWidthPartialHeight
}
It could be used like this:
ControlVisibility itemVisibility =
MyHelpers.IsObjectVisibleInContainer(someItem, parentContainer);

WPF TextBox Scrollbars actually visible or not?

I have a TextBox in a WPF Project whose verticalscrollbar visibility is set to Auto. When I set a text in it at Runtime the vertical scroll bar becomes visible when text is more and vertical scroll bar hides when text is less.
What i want is if when i assign a large text to it (and verticalscrollbar becomes visible) then i will run a recursive loop which will reduce font size to a level till the scroll bar become hidden. I want to get the actual visibility value of verticalscrollbar.
In code behind the verticalscrollbarvisibility property always gives auto. (i think boz its set to auto in XAML).
// If my approach is wrong for this problem please let me know that also.
I found it. I passed my textbox in the function
ScrollViewer sv = FindVisualChild<ScrollViewer>(mytextbox);
if (sv != null)
{
// do something with ScrollViewer
}
public static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
It returns me ScrollViewer whose visibility property is what i wanted.

How can I know if the mouse is over the header row in a WPF ListView?

I'm implementing simple drag-and-drop functionality in my app, and I would like to know if the user has dropped the item above the first item in the list (in the header row) so i can just insert it as the first item.
I'm using VisualTreeHelper.HitTest to get the item at the drop position, but this only works if there actually is an item there.
HitTestResult hitTestResults = VisualTreeHelper.HitTest(myListView, location);
When the mouse is on the headers i get one of many several items in hitTestResults.VisualHit. In just a few tests, I've gotten ListBoxChrome, TextBlock, and Border How can i know if any of these are part of the header row? I can't just test for them specifically since there could be other UI elements returned.
Can i get the coordinates of the header row of the listview to see if my point is inside it? Or is there a way that i can know if my Point is inside that header row?
I don't know how your current implementation look but you can walk up the Visual Tree until you either find a ListViewItem or a GridViewColumnHeader. If you find a GridViewColumnHeader you know that the item was dropped in this specific Header.
Uploaded a small sample project here demonstrating the effect with MessageBox's on drop: http://www.mediafire.com/?v3l8nl4rnewhz5s
It will look something like this
private void ListView_Drop(object sender, DragEventArgs e)
{
ListView parent = sender as ListView;
YourDataClass data = e.Data.GetData(typeof(YourDataClass)) as YourDataClass;
if (data != null)
{
HitTestResult hitTestResult = VisualTreeHelper.HitTest(parent, e.GetPosition(parent));
ListViewItem hitItem = VisualTreeHelpers.GetVisualParent<ListViewItem>(hitTestResult.VisualHit);
GridViewColumnHeader columnHeader = VisualTreeHelpers.GetVisualParent<GridViewColumnHeader>(hitTestResult.VisualHit);
if (hitItem != null) // ListViewItem Drop
{
//..
}
else if (columnHeader != null) // Header Drop
{
//..
}
}
}
public static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}

AssociatedObject.FindName in Silverlight behavior OnAttached method returns null

I'm making a Silverlight behavior to enable dragging an element by a contained "drag handle" element (rather than the whole element being draggable). Think of it like a window title bar.
In the OnAttached method I am calling: AssociatedObject.FindName(DragHandle)
but this is returning null.
I then tried handling the AssociatedObject's Loaded event and running my code there, but I still get a null returned.
Am I misunderstanding what FindName is able to do? The AssociatedObject is in an ItemsControl (I want a collection of draggable elements). So is there some kind of namescope problem?
Yes, it sounds like a namescope problem. The MSDN documentation on XAML namescopes goes over how namesopes are defined for templates and item controls. Are you using a template for the items in your ItemsControl?
You may just have to walk the visual tree recursively with something like this to find the correct element by name:
private static FrameworkElement FindChildByName(FrameworkElement parent, string name)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null && child.Name == name)
{
return child;
}
else
{
FrameworkElement grandChild = FindChildByName(child, name);
if (grandChild != null)
{
return grandChild;
}
}
}
return null;
}

Resources