I have two list view on WPF. The first listview is loaded with a Datatable. When double clicking on one item from the first listview, the selectedItem is moved to the second listview.
The problem arises when appears an scroll bar in the first list view due to a lot of elements loaded from the DataTable. If a select one item and double click on the scroll bar down arrow, MouseDoubleClick event is launched and the selected item is moved to the second listview.
How I can detect the double click on the scroll bar to prevent this?
Thanks a lot!
I tested the above code which was very helpful, but found the following to be more stable, as sometimes the source gets reported as GridViewRowPresenter when in fact you are double clicking an item.
var src = VisualTreeHelper.GetParent((DependencyObject)e.OriginalSource);
var srcType = src.GetType();
if (srcType == typeof(ListViewItem) || srcType == typeof(GridViewRowPresenter))
{
// Your logic here
}
Try this in you MouseDoubleClick event on the first Listview:
DependencyObject src = VisualTreeHelper.GetParent((DependencyObject)e.OriginalSource);
if(src is Control && src.GetType() == typeof(ListViewItem))
{
// Your logic here
}
Based on this.
I am using this in various projects and it solves the problem you are facing.
private void ListBox_OnMouseDoubleClick(object pSender, MouseButtonEventArgs pE)
{
FrameworkElement originalSource = pE.OriginalSource as FrameworkElement;
FrameworkElement source = pE.Source as FrameworkElement;
if (originalSource.DataContext != source.DataContext)
{
logic here
}
}
When you have the DataContext you can easy see if the sender is an item or the main listbox
I've got the final solution:
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var originalSource = (DependencyObject)e.OriginalSource;
while ((originalSource != null) && !(originalSource is ListViewItem)) originalSource = VisualTreeHelper.GetParent(originalSource);
if (originalSource == null) return;
}
it works for me.
Related
I'm trying to use a WPF listbox on a tablet.
In a dummy project I just made a listbox with a lot of items and when i select one this item will be showed in a textblock.
I have a selectionchanged event on the listBox
On my laptop everything works the way it should but when i run it on a tablet the selectionchanged event isn't triggered sporadically. On the screen the old selected item stays selected and the new selected one is highlighted but the item isn't shown in the textblock.
With remote debugging I have seen that the TouchDown, TouchMove and TouchUp event are all triggered, but some times the selectionChanged isn't triggered.
these things I've tried as well:
setting in Xaml inside the listbox:
ScrollViewer.PanningMode="None"
When I do this the selectionchanged event is always triggered but the user can't scroll down anymore with swiping (Which must be possible.
I think here lies the problem somewhere, but I don't have any solution yet.
Help Needed.
After a long time a solution for this problem was found.
first of we need some variables
private TouchPoint _movePoint;
private double _minimum = 0;
private double _maximum;
Me need to catch the TouchMove event of the listBox. This event triggers many times. We need get maximum and minimum Y-values of were the touch has been.
private void myListBox_TouchMove(object sender, TouchEventArgs e)
{
_movePoint := e.GetTouchPoint(myListBox);
if (_minimum.Equals(0))
{
_minimum := _movePoint.Position.Y;
_maximum := _movePoint.Position.Y;
return;
}
if (_movePoint.Position.Y < _minimum)
_minimum := _movePoint.Position.Y;
if (_movePoint.Position.Y > _maximum)
_maximum := _movePoint.Position.Y;
}
Now in the TouchUp event we look at the how far have been slided in the vertical direction. If this is not to big (in this example lower then 20), we gonna look at where the touchup event took place and look for the ListBoxItem that is on that place and set IsSelected=ture on this item.
private void myListBox_TouchUp(object sender, TouchEventArgs e)
{
var difference = _maximum - _minimum;
_maximum = 0;
_minimum=0;
if(difference < 20)
{
var touchPosition = e.GetTouchPoint(myListBox)
UIElement elem = myListBox.InputHitTest(touchPosition.Position) as UIElement;
while (elem != null)
{
if (elem == myListBox)
return;
ListBoxItem item = elem as ListBoxItem;
if (item != null)
{
item.IsSelected = true;
return;
}
elem = VisualTreeHelper.GetParent(elem) as UIElement;
}
}
}
This should work.
I have 4 List Boxes in my WPF App. Each of them, at any given point of time contains equal no. of String ListBoxItems in them. If selected index of any one of them changes the other three also reflect the same behaviour. What i want is that when a user moves scrollbar of one of them the other three should also move simultaneoulsly.
I tried Scrollintoview but it does not work bcoz if i select an item of a listBox and apply scrollintoview for other three Listboxes the selected item in them come on the top.
That's why i think scrollbar movement should be the best option for this. How to do that?
In XAML hook the ScrollChanged event
ScrollViewer.ScrollChanged="ListBox_ScrollChanged"
In CodeBehind find the Scrollviewers inside the ListBoxes and apply the Vertical offset:
private void ListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var sourceScrollViewer = FindVisualChild<ScrollViewer>(sender as DependencyObject) as ScrollViewer;
var targetScrollViewer = FindVisualChild<ScrollViewer>(listBox2) as ScrollViewer;
targetScrollViewer.ScrollToVerticalOffset(sourceScrollViewer.VerticalOffset);
}
// helper method
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Well in code it is something like that :
1) get the four scrollviewers of the four ListViews
( by finding them within the child (VisualTreeHelper.getchild)
inside a method like FindDescendant(...))
2) hook their scrolls events (ScrollChanged) to a common sub that
will get the VerticalOffset of the one that triggered the event
and ScrollToVerticalOffset(.) the others.
must be possible in xaml also, but seems more complicated to me.
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;
}
I have a ListBox and a DockPanel. List box contains items that are supposed to be dragged onto the dock panel. I've implemented that by following this link.
There are a couple of things I do not understand though:
While dragging, all I see is a cursor. I'd like to literary see the list item I am
dragging to move around with my cursor. How do I do that?
Is the DragDropEffect property only for the different cursor design or it has a
higher purpose? :)
How do I make list item disappear from the ListBox once it is dropped onto the
DockPanel?
I'd like to enforce some animation on the items that I drag, like glow once it is
dropped. Which trigger/setter should I use for that?
Here's my code for basic dragging and dropping:
Code-behind for the ListBox part
private Point startPosition;
private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPosition = e.GetPosition(null);
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point currentPosition;
Vector offset;
ListBox listBox;
ListBoxItem item;
Match match;
DataObject dragData;
currentPosition = e.GetPosition(null);
offset = startPosition - currentPosition;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(offset.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(offset.Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the data binded to ListBoxItem object, which is "match"
listBox = sender as ListBox;
item = FindAnchestor<ListBoxItem>((DependencyObject)e.OriginalSource);
match = (Match)listBox.ItemContainerGenerator.ItemFromContainer(item);
dragData = new DataObject("match", match);
DragDrop.DoDragDrop(item, dragData, DragDropEffects.Move);
}
}
Code-behind for the DockPanel part
private void DockPanel_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("match") ||
sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
}
private void DockPanel_Drop(object sender, DragEventArgs e)
{
Match match;
DockPanel matchSlot;
ContentPresenter contentPresenter;
Binding binding;
if (e.Data.GetDataPresent("match"))
{
match = e.Data.GetData("match") as Match;
matchSlot = sender as DockPanel;
contentPresenter = new ContentPresenter();
contentPresenter.ContentTemplate = this.FindResource("MatchTemplate") as DataTemplate;
binding = new Binding();
binding.Source = match;
contentPresenter.SetBinding(ContentPresenter.ContentProperty, binding);
matchSlot.Children.Clear();
matchSlot.Children.Add(contentPresenter);
}
}
Thanks for all the help.
Ok, after a while I found some answers and discovered a few things on my own.
As for the DragDropEffect enum, it should be used for two reasons:
To distinguish if the item is moved or copied in the code. It serves like a flag and should be used most commonly like this:
if (e.DragDropEffect == DragDropEffect.Move)
{
...
}
else ...
To decorate the mouse cursor based on the enum value. This way it tells the user if he or she is moving or copying the item.
As for the drag and drop visualization here's a link to the post containing the reference which is an excellent starting point for drag and drop to build on: WPF Drag & Drop: How to literally drag an element?
How can I programmatically force a silverlight list box to scroll to the bottom so that the last item added is always visible.
I've tried simply selecting the item. It ends up as selected but still not visible unless you manually scroll to it.
Use the ListBox's ScrollIntoView method passing in the last item. You may need to call UpdateLayout immediately before it for it to work.
The ScrollIntoView() method will scroll the last item into view, however listBox.UpdateLayout() must be called just before ScrollIntoView(). Here is a complete method with code:
// note that I am programming Silverlight on Windows Phone 7
public void AddItemAndScrollToBottom(string message)
{
string timestamp = DateTime.Now.ToString("mm:ss");
var item = new ListBoxItem();
item.Content = string.Format("{0} {1}", timestamp, message);
// note that when I added a string directly to the listbox, and tried to call ScrollIntoView() it did not work, but when I add the string to a ListBoxItem first, that worked great
listBoxEvents.Items.Add(item);
if (listBoxEvents.Items.Count > 0)
{
listBoxEvents.UpdateLayout();
var itemLast = (ListBoxItem)listBoxEvents.Items[listBoxEvents.Items.Count - 1];
listBoxEvents.UpdateLayout();
listBoxEvents.ScrollIntoView(itemLast);
}
}
Slightly refactored to reduce the lines of code:
listBoxEvents.Add(item)
listBoxEvents.UpdateLayout()
listBoxEvents.ScrollIntoView(listBoxEvents.Items(listBoxEvents.Items.Count - 1))
Just went through this and none of the solutions above worked in a Silverlight 5 app. The solution turned out to be this:
public void ScrollSelectedItemIntoView(object item)
{
if (item != null)
{
FrameworkElement frameworkElement = listbox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
if (frameworkElement != null)
{
var scrollHost = listbox.GetScrollHost();
scrollHost.ScrollIntoView(frameworkElement);
}
}
}