IsMouseOver and DragLeave not working properly - c#

I have a grid/panel on my UI that can take a drop action. The control is a Grid with some dynamically created content in it (a viewbox containing a stack panel of borders if that matters).
When I drag relevant information into the grid I want it to change (regenerate) the internal content and when it drops or leaves it should go back to normal. I have the 'change it' and 'put it back' methods working just fine, but I'm having trouble determining when to call them.
I have drag enter and drag leave events on the grid. The enter works great, but the leave is not. As soon as the mouse gets over the top of the content inside of the grid it fires a drag leave event, so it changes the content (and if that puts it back out of the child content it fires the drag enter again and just flashes).
My first thought was to just determine if the mouse is actually still over the grid and just ditch out without putting the content back. However this doesn't seem to be working. Here is my code for drag leave:
private void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null || gd.IsMouseOver ||
gd.Children.Cast<FrameworkElement>().Any(x => x.IsMouseOver)) return;
wizard.Assembly.CollapsePreview();
}
As you can see, I even tried to iterate the children of the grid and see if mouse over is true on any of those and ditch out, and still it just keeps returning false for all of that and collapsing. I thought IsMouseOver was supposed to tell me if the mouse is over at all even if it's children of the control...

Ok well after some more research I found something that seems to be working. I ended up having to use VisualTreeHelper.HitTest and use the overload that has a callback and gets all hit items even if it's obscured. Here is my final code:
void Grid_DragLeave(object sender, DragEventArgs e)
{
var wizard = DataContext as WizardVM;
var gd = sender as Grid;
if (wizard == null || gd == null) return;
Point pt = e.GetPosition(this);
hitResults.Clear();
VisualTreeHelper.HitTest(gd, null, GridHitTestResultCallback,
new PointHitTestParameters(pt));
if (!hitResults.Contains(gd))
{
wizard.Assembly.IsExpanded = false;
}
}
HitTestResultBehavior GridHitTestResultCallback(HitTestResult result)
{
hitResults.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
Basically it gets the point in relation to the full window, then calls HitTest with that point and the overall grid I'm trying to work with. The callback populates a list of visuals that are hit by the hit test. Last the original method checks if the grid is in the list of hits and if not it collapses it.
As a side note I also changed from .ExpandPreview() and .CollapsePreview() methods to a .IsExpanded property for unrelated reasons... didn't want anyone to be confused by that change...

Related

RadTreeView horizontal auto scroll when expanding tree WPF

I am using a RadTreeView control from telerik.
I am binding data to my tree using hierarchical data templates.
The data is displaying fine but when I programmatically expand all my items tree is scrolling horizontally right to the furthest point.
I have tried hooking into the loaded event of the tree view and only enabling horizontal scrollbar after the expand all has been called but this doesn't work.
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var rtv = sender as RadTreeView;
if (rtv.ScrollViewer != null)
{
rtv.ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
rtv.ExpandAll();
System.Windows.MessageBox.Show("test"); //this causes desired behaviour.
rtv.ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
}
}
One thing I have noticed is if I add a call to show a messagebox after the expandall call the horizontal scroll bar is displayed scrolled to the left as required.
Is the messagebox causing the UI to fully refresh and expand before enabling the scrollbar?
Please let me know if you need anymore information.

Strange behavior by combo box selection in wpf

I am working on an application, wherein we have a main window which is having so many child windows in different dock option. So, one dock is having a property panel window which allows a user to modify property of selected entity and after changing the value user has to click on apply button which is available in the bottom of the control. So, I was willing to have some sort of functionality that if user has modified some value and instead of clicking on apply, if user click somewhere else apart from property panel view's sub control's, then user should be given a message that "Please first click apply to save your changes". for this what I did, I wrote the following piece of code on the mouse down event of MainWindow.
private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
{
var hitObject = this.InputHitTest(e.GetPosition(this)) as DependencyObject;
if (hitObject.FindVisualAncestor<PropertyPanelUserControl>() == null)
{
MessageBox.Show("Please save your changes");
}
}
So, the logic is this, on mouse down of main window, get the hit object and check that if it is a child control of property panel control, then it will have PropertyPanelUserControl as its parent and other control which are not part or child control of PropertyPanelUserControl, then user will be prompted to click on the apply.
The above piece of code was working superb...but I figured out a strange issue, I had a combo box in the property panel which had entries from 1 to 10. So, when user tries to change value to other value, then user will not be given that message as, so far user is clicking on the property panel control and when i check the hit object in the mouse down event after selecting an item in the combo box, then hit object was the chromeButton or combo box. but When I selected the last item 10, then hit object comes as border which has the property panel control.
<Border><View:PropertyPanelControl/></Border> and above check fails, as border doesn't have ancestor as property panel control, rather border is the ancestor of the control. So, user gets a message even while changing only combo box value,
Moreover, I have made sure that I was clicking on the combo box item not outside, So, now question is this why wpf is behaving in this weird way and how to address this issue.
Your first question is strange:
why wpf is behaving in this weird way
You described what happens and it all seems totally normal to me. The user clicks on a ComboBoxItem and your HitTest tells you that you've clicked on a ComboBoxItem... I don't see any problem there.
how to address this issue
Now I imagine that if you had taken that ComboBoxItem and worked your way up the visual tree, then you would have found your PropertyPanelUserControl control. Try something like this instead:
HitTestResult result = VisualTreeHelper.HitTest(this, e.GetPosition(this));
UIElement uIElement = result.VisualHit.GetParentOfType<PropertyPanelUserControl>();
if (uIElement != null)
{
// the user clicked inside the PropertyPanelUserControl control
}
The GetParentOfType method is an Extension method that I created that walks up the visual tree looking for the first element of a particular type... you can easily refactor it into a normal method if you prefer:
public static T GetParentOfType<T>(this DependencyObject element) where T : DependencyObject
{
Type type = typeof(T);
if (element == null) return null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
if (parent == null) return null;
else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
return GetParentOfType<T>(parent);
}

Handle multiple hover to click events in C# for kinect

I am developing a Kinect game in C# where the user needs to click 2 buttons at the same time by hovering over one button with each hand.
However, with my current code, when the user hovers with one hand over a button, the other buttons get disabled and the other hand can only click when the first hand stops hovering over a button.
To solve this, I'm thinking of queuing the second click while the first click is being processed. To do this, I have used the following code based on this link
private Queue<System.Windows.Controls.Button> Button_Queue = new Queue<System.Windows.Controls.Button>();
private bool isProcessing = false;
private void Button_Click((object sender, RoutedEventArgs e){
if(isProcessing){
Button_Queue.Enqueue(this);
}
else
{
isProcessing = true;
// code here
isProcessing = false;
while(Button_Queue.Count > 0){
Button_Queue.Dequeue().PerformClick();
}
}
However, the Button_Queue.Enqueue(this) line shows the error
"The best overloaded method match for Queue.Enqueue has invalid
arguments."
I'm guessing this is because the button click event cannot be queued in a queue declared with the type Button.
Do you have any suggestions for how to create this queue of button click events or another way to handle multiple clicks from the user?
You don't need to queue the event. If isProcessing is true, then the other button was already clicked, so you can handle the event for both button clicks from that point on.
You could measure the time between the two clicks, to work out if it validates as a "two buttons clicked at the same time" event.
Have you considered a more low-level approach? The first thing that came to mind was to create two hot areas instead of buttons and monitor whether the user's hands are inside those areas at the same time.
It is unclear to me why another button is disabled when you hand is hovering over another object. Without seeing the code, I would say that you are doing something that would cause that -- and there is no reason to.
Additionally, you should be using interaction concepts centered around a gesture system and not something that is written for a mouse/keyboard input. Using regular UI objects and interacting with them in ways that parallel traditional inputs will only serve to confuse the users.
Have a look at the following two examples, which use a "hover-to-click" and a "press-to-click" interaction
Basic Interaction, for SDK 1.6
Control Basics, for SDK 1.7 (in Kinect for Windows Developer Toolkit)
In both cases, you are using a hit test on custom controls to handle events. Here is an example of a hit test function I use in one of my apps:
private void HitTestHand(HandPosition hand)
{
// quick fix to null pointer exception on exit.
if (Application.Current.MainWindow == null)
return;
Point pt = new Point(hand.X, hand.Y);
IInputElement input = Application.Current.MainWindow.InputHitTest(pt);
if (hand.CurrentElement != input)
{
var inputObject = input as DependencyObject;
var currentObject = hand.CurrentElement as DependencyObject;
// If the new input is a child of the current element then don't fire the leave event.
// It will be fired later when the current input moves to the parent of the current element.
if (hand.CurrentElement != null && Utility.IsElementChild(currentObject, inputObject) == false)
{
// Raise the HandLeaveEvent on the CurrentElement, which at this point is the previous element the hand was over.
hand.CurrentElement.RaiseEvent(new HandInputEventArgs(HoverDwellButton.HandLeaveEvent, hand.CurrentElement, hand));
}
// If the current element is the parent of the new input element then don't
// raise the entered event as it has already been fired.
if (input != null && Utility.IsElementChild(inputObject, currentObject) == false)
{
input.RaiseEvent(new HandInputEventArgs(HoverDwellButton.HandEnterEvent, input, hand));
}
hand.CurrentElement = input;
}
else if (hand.CurrentElement != null)
{
hand.CurrentElement.RaiseEvent(new HandInputEventArgs(HoverDwellButton.HandMoveEvent, hand.CurrentElement, hand));
}
}
Notice that an event is being fired on the element below the hand cursor. Examples of these elements can be found in the two links above (the HoverDwellButton is what I use with the above code sample).
Two events on two different elements, or the same element, can fire at any time with this. You can easily keep track of which user is over which button, if that button is in the process of being pressed, or if it has been pressed.
The key to all this is not using a UI paradigm that isn't designed for gesture systems! Don't try to shoehorn the keyboard/mouse event structure into a gesture based system -- it will only cause you more pain in the long run and cause your users confusion.

Set focus on a control without bringing the application to the front

I want to set the focus to a control within an application without giving focus to the whole application.
For example: Click a button which takes a while to load a screen, when the screen is loaded set the focus on one of the controls. In the meantime I have gone to a different application to do something and the focus returns to the previous application.
This happens when I use Keyboard focus or Logical focus.
Is there any way to stop this happening?
Here is the code:
private void SetFocusInternal()
{
// Focus the first control we find with the 'PositionCursor' indicator expression
FrameworkElement controlToFocus = GetFirstRequiresFocusControl();
// Give focus back to the control which last had it (if any)
if (controlToFocus == null)
controlToFocus = GetLastFocusedControl();
// Just focus the first thing we can find
if (controlToFocus == null)
controlToFocus = GetFirstFocusableControl();
// Using any of the following goes wrong!!
controlToFocus.Focus();
Keyboard.Focus(controlToFocus);
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), controlToFocus);
}

ASP.NET - Accessing All ImageButtons on a Page and Putting Two Images on an ImageButton

I actually have two questions:
(1) Is it possible to put another image on top of an ImageButton that already has an ImageUrl set (without changing the ImageUrl - literally just add the second image 'on top')? Even by using CSS?
(2) I have a dynamically set number of ImageButtons contained within a ListView. When a user clicks on an ImageButton, I change the .CssClass property of the one clicked in order to 'highlight' it. My question is this: whenever an ImageButton is click, I need to not only highlight it, but make sure I unhighlight all the others. However, I'm having trouble getting the others. I get the clicked ImageButton using
((ImageButton)sender).CssClass = "SelectedImageButton";
in the event handler. However, how do I get all the others so I can set their style 'back' to the unhighlighted style?
Thanks in advance for any help!
UPDATE: ANSWERED!
I've solved the issue mentioned in (2) using the following algorithm. Note, I've marked #OFConsulting's answer below as the correct answer because without his algorithm, I would have never gotten the following algorithm (which came from tweaking his algorithm slightly). Thanks #OFConsulting!
// Cast the sender to an ImageButton to have the clicked ImageButton
ImageButton clickedImageButton = sender as ImageButton;
// The ListView has ListViewDataItems and the ImageButtons are in
// THOSE children controls, thus match on the ImageButtons' Parents' IDs
Control parentControl = clickedImageButton.Parent;
List<ListViewDataItem> allOtherImageButtons = MyListView.Controls.OfType<ListViewDataItem().AsQueryable().Where(i => i.ID != clickedImageButton.Parent.ID).ToList();
// Highlight
clickedImageButton.CssClass = "HighlightedStyle";
// Unhighlight
foreach (ListViewDataItem button in allOtherImageButtons)
{
// The ImageButton is always the 2nd child control of the ListViewDataItem
ImageButton childImageButton = (ImageButton)button.Controls[1];
childImageButton.CssClass = "NoHighlightedStyle";
}
For Part (1) of that question, setting the background image within your css class might do the trick, but you never really explained why you just couldn't change the ImageUrl. You can always throw everything on an update panel if you need it to be dynamic without the hassle of a bunch of script.
Part (2) seems pretty straight forward. Just use a little bit of linq against the relevant control collection within your page.
protected void ImageButton5_Click(object sender, ImageClickEventArgs e)
{
ImageButton clickImageButton = sender as ImageButton;
// This example assumes all the image buttons have the same parent.
// Tweak as needed depending on the layout of your page
Control parentControl = clickImageButton.Parent;
List<ImageButton> allOtherImageButtons = parentControl.Controls.OfType<ImageButton>().AsQueryable().Where(i => i.ID != clickImageButton.ID).ToList();
// Highlight
clickImageButton.CssClass = "WhateverHighlights";
// Unhighlight
foreach (ImageButton button in allOtherImageButtons)
{
button.CssClass = "WhateverClears";
}
}
Edit: One more thing. Make sure any controls you are adding dynamically get added before Page_Load (I.E. during Init). There are some viewstate issues associated with adding control too late.

Categories

Resources