How to Raise DragDelta event on Thumb - wpf

As you can see below, I want to start moving when the component visibility changes.
because otherwise I need the user to click again to start the movement, and that is bad in terms of usability for my application.
public MoveILayoutControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(MoveILayoutControl_IsVisibleChanged);
this.moveThumb.DragDelta += new DragDeltaEventHandler(MoveThumb_DragDelta);
}
void MoveILayoutControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.IsVisible)
{
// Raise Drag Event !?
}
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var myData = DataContext as ILayoutVisual;
if (myData != null)
{
Point dragDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (myData.Rotation != 0)
{
Matrix toCalculate = ((this.Parent as FrameworkElement).RenderTransform).Value;
if (toCalculate != null)
{
dragDelta = toCalculate.Transform(dragDelta);
}
}
myData.X += dragDelta.X;
myData.Y += dragDelta.Y;
}
}

I believe the only way is using reflection to change the internal values ​​of the thumb. Changing the property "IsDragging" (not tested).

I looked up the source code of Thumb and I think a better way is to simulate a MouseLeftButtonEvent on the thumb:
var evt = new MouseButtonEventArgs(mouseDevice, timestamp, MouseButton.Left)
{
RoutedEvent = UIElement.MouseLeftButtonDownEvent
};
thumb.RaiseEvent(evt);

Related

Prevent RepositoryItemSearchLookUpEdit when Popup is Open When CloseUpKey.Key is pressed

I use a RepositoryItemSearchLookUpEdit. its CloseUpKey property is set to space
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
I want to use this shortcut only for open popup and not for closing popup.
How can I achieve this?
UPDATE------------------------
First I create an RepositoryItemSearchLookUpEdit object
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.KeyDown += repositoryItemLookUpEdit_KeyDown;
result.CloseUp += repositoryItemLookUpEdit_CloseUp;
result.QueryCloseUp += repositoryItemLookUpEdit_QueryCloseUp;
private void repositoryItemLookUpEdit_QueryCloseUp(object sender, System.ComponentModel.CancelEventArgs e)
{
var edit = sender as SearchLookUpEdit;
KeyEventArgs k = new KeyEventArgs(edit.Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == edit.Properties.CloseUpKey.Key)
e.Cancel = true;
}
And pass it to a grid column:
grdListView.Columns["yyy"].ColumnEdit = result
How can I achieve that with these events, without creating a descendant SearchLookUpEdit
UPDATED:
The problem is that CloseUp event (where you could get the necessary info about the closeup key) occurs after the QueryCloseUp event (where you could precent the closing up event). Also the KeyPress, KeyDown and KeyUp events seem also NOT to occur when the QueryCloseUp occurs, as a result they couldn't be overridden. So I tried this, I created a custom KeyEventHandler and triggered him during QueryCloseUp event in order to get the necessary info of what key was pressed and cancel the event if the close key event was the one. Here is my codeTry it to see if it suits you
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Here you can add your grid control as you have created
DataTable dt = new DataTable();
dt.Columns.Add("ID"); //use your own names and types
gridControl1.DataSource = dt;
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.QueryCloseUp += new CancelEventHandler(repositoryItemLookUpEdit_QueryCloseUp);
((gridControl1.MainView as GridView).Columns["ID"] as GridColumn).ColumnEdit = result;
}
private static readonly object myQueryCloseUp = new object();
public event KeyEventHandler MyQueryCloseUp
{
add { Events.AddHandler(myQueryCloseUp, value); }
remove { Events.RemoveHandler(myQueryCloseUp, value); }
}
protected virtual void AttachKeyPressEvent(KeyEventArgs e)
{
KeyEventHandler handler = (KeyEventHandler)Events[myQueryCloseUp];
if (handler != null)
handler(this, e);
}
//Here you add your own Handler implementation
public void repositoryItemLookUpEdit_QueryCloseUp(object sender, CancelEventArgs e)
{
KeyEventArgs k = new KeyEventArgs((sender as SearchLookUpEdit).Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == (sender as SearchLookUpEdit).Properties.CloseUpKey.Key)
e.Cancel = true;
}
}

"Microsoft Blend"-like continuous Drag

Microsoft Blend allows changing numeric values of properties like Left, Top etc. through continuous drag. User clicks in the property value box, keeps the button down and drags left or right to decrease/increase the value. Simple.
The special thing about it is that if cursor reaches the left or right end of the screen and user still wants to drag more, they can continue dragging and the cursor will start over from the other end of the screen.
I'm trying to do this in one of my WPF applications using Thumb control. Using DragDetla event, if I find that the Thumb has reach the edge of the screen, I set its position to the far end. But this makes the value of e.HorizontalChange as big as the width of entire screen. How can I change Thumb's position during drag without affecting horizontal change value?
I have realized this in a WPF control by using a textbox and subscribing to events such as:
PreviewMouseDown
MouseUp and
MouseMove
MouseEnter
The drag until you reach screen limits requires a mouse capture or call to CaptureMouse method available on any UIElement. On the other side, you need to release the mouse at some point which requires a call of the ReleaseMouseCapture method. The solution could go like this:
Declare an enumeration to model the drag direction
internal enum MouseDirections
{
None,
LeftRight,
UpDown
}
Declare a class to keep trak of mouse origin (first location) and current location:
internal class MouseIncrementor
{
private MouseDirections _enumMouseDirection = MouseDirections.None;
private Point _objPoint;
private readonly Point _initialPoint;
public MouseIncrementor(Point objPoint, MouseDirections enumMouseDirection)
{
_objPoint = objPoint;
_initialPoint = _objPoint;
_enumMouseDirection = enumMouseDirection;
}
public MouseDirections MouseDirection
{
get
{
return _enumMouseDirection;
}
protected set
{
_enumMouseDirection = value;
}
}
public Point InitialPoint
{
get
{
return _initialPoint;
}
}
public Point Point
{
get
{
return _objPoint;
}
set
{
_objPoint = value;
}
}
internal MouseDirections SetMouseDirection(Point pos)
{
double deltaX = this.Point.X - pos.X;
double deltaY = this.Point.Y - pos.Y;
if (Math.Abs(deltaX) > Math.Abs(deltaY))
MouseDirection = MouseDirections.LeftRight;
else
{
if (Math.Abs(deltaX) < Math.Abs(deltaY))
MouseDirection = MouseDirections.UpDown;
}
return MouseDirection;
}
}
I have a custom control that contains a TextBox named _PART_TextBox:
TextBox _PART_TextBox;
...and field for the MouseIncrementor:
MouseIncrementor _objMouseIncr;
...these are wired up like this:
_PART_TextBox.MouseEnter += _PART_TextBox_MouseEnter;
_PART_TextBox.GotKeyboardFocus += _PART_TextBox_GotKeyboardFocus;
_PART_TextBox.LostKeyboardFocus += _PART_TextBox_LostKeyboardFocus;
_PART_TextBox.MouseMove += _PART_TextBox_MouseMove;
_PART_TextBox.MouseUp += _PART_TextBox_MouseUp;
_PART_TextBox.PreviewMouseDown += _PART_TextBox_PreviewMouseDown;
_PART_TextBox.LostMouseCapture += _PART_TextBox_LostMouseCapture;
and a number of event handlers are required to get this to work:
private void _PART_TextBox_LostMouseCapture(object sender, MouseEventArgs e)
{
_objMouseIncr = null;
}
private void _PART_TextBox_MouseUp(object sender, MouseButtonEventArgs e)
{
if (_objMouseIncr != null)
{
var mouseUpPosition = GetPositionFromThis(e);
if (_objMouseIncr.InitialPoint.Equals(mouseUpPosition))
{
_PART_TextBox.Focus();
}
}
_PART_TextBox.ReleaseMouseCapture();
_objMouseIncr = null;
}
private void _PART_TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (IsKeyboardFocusWithin == false)
{
_objMouseIncr = new MouseIncrementor(this.GetPositionFromThis(e), MouseDirections.None);
e.Handled = true;
}
}
private void _PART_TextBox_MouseMove(object sender, MouseEventArgs e)
{
// nothing to do here
if (_objMouseIncr == null)
return;
if (e.LeftButton != MouseButtonState.Pressed)
return;
if (CanIncreaseCommand() == false && CanDecreaseCommand() == false)
{
// since we can't parse the value, we are out of here, i.e. user put text in our number box
_objMouseIncr = null;
return;
}
var pos = GetPositionFromThis(e);
double deltaX = _objMouseIncr.Point.X - pos.X;
double deltaY = _objMouseIncr.Point.Y - pos.Y;
if (_objMouseIncr.MouseDirection == MouseDirections.None)
{
// this is our first time here, so we need to record if we are tracking x or y movements
if (_objMouseIncr.SetMouseDirection(pos) != MouseDirections.None)
_PART_TextBox.CaptureMouse();
}
if (_objMouseIncr.MouseDirection == MouseDirections.LeftRight)
{
if (deltaX > 0)
OnDecrement(LargeStepSize);
else
{
if (deltaX < 0)
OnIncrement(LargeStepSize);
}
}
else
{
if (_objMouseIncr.MouseDirection == MouseDirections.UpDown)
{
if (deltaY > 0)
{
if (CanIncreaseCommand() == true)
OnIncrease();
}
else
{
if (deltaY < 0)
{
if (CanDecreaseCommand() == true)
OnDecrease();
}
}
}
}
_objMouseIncr.Point = GetPositionFromThis(e);
}
private Point GetPositionFromThis(MouseEventArgs e)
{
return this.PointToScreen(e.GetPosition(this));
}
private void _PART_TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
_objMouseIncr = null;
(sender as TextBox).Cursor = Cursors.ScrollAll;
}
private void _PART_TextBox_MouseEnter(object sender, MouseEventArgs e)
{
if (IsMouseDragEnabled == false)
return;
if (IsKeyboardFocusWithin)
(sender as TextBox).Cursor = Cursors.IBeam;
else
(sender as TextBox).Cursor = Cursors.ScrollAll;
}
private void _PART_TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
_objMouseIncr = null;
(sender as TextBox).Cursor = Cursors.IBeam;
}
The full project is located here: https://github.com/Dirkster99/NumericUpDownLib
Please let me know if I am missing something or if there are additional questions.

Silverlight Mouse Events: MouseEnter and MouseLeave conflicts

I have a collection of buttons in a grid. For each one of these buttons, I want to handle the MouseEnter and MouseLeave events to animate the height of the button (and do some other interesting stuff). It all works good until I start moving my mouse too fast over and off the buttons which eventually cause the events to take place at before the other is complete. What's the best way of making sure the events wait for eachother before being triggered?
UPDATE:
Going by x0r's advice, I refactored my code into an internal class which inherits from Button and has the required methods to perform the animations. Unfortunately, this did not really solve the problem because - I think - I'm handling the Completed event of the first animation in two separate places. (correct me if I'm wrong). Here's my code:
internal class MockButton : Button
{
#region Fields
private Storyboard _mouseEnterStoryBoard;
private Storyboard _mouseLeaveStoryBoard;
private Double _width;
#endregion
#region Properties
internal Int32 Index { get; private set; }
#endregion
#region Ctors
internal MockButton(Int32 index) : this(index, 200)
{
}
internal MockButton(Int32 index, Double width)
{
this.Index = index;
this._width = width;
}
#endregion
#region Event Handlers
internal void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
}
internal void OnMouseLeave()
{
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
#endregion
}
UPDATE 2:
Some events are getting triggered multiple times. An example of that is the Click event on the close button of my Rule object...
public Rule(Action<Int32> closeAction)
{
this.Style = Application.Current.Resources["RuleDefaultStyle"] as Style;
this.CloseAction = closeAction;
this.Loaded += (s, e) =>
{
if (_closeButton != null)
{
_closeButton.Click += (btn, args) =>
{
if (this.CloseAction != null)
{
this.CloseAction.Invoke(this.Index);
}
};
if (_closeButtonShouldBeVisible)
{
_closeButton.Visibility = System.Windows.Visibility.Visible;
}
}
};
}
And below is the Action<Int32> I'm passing to the Rule object as the CloseAction:
private void RemoveRule(Int32 ruleIndex)
{
Rule ruleToRemove = Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex));
Storyboard sb = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
sb.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Opacity"));
animation.Duration = TimeSpan.FromMilliseconds(300);
animation.From = 1;
animation.To = 0;
sb.Children.Add(animation);
Storyboard.SetTarget(animation, ruleToRemove);
sb.Completed += (s, e) =>
{
if (Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex)) != null)
{
this.Rules.RemoveAt(ruleIndex);
}
};
sb.Begin();
}
UPDATE 3:
In order to avoid the animations running too early, I thought I could delay the MouseEnter event, so if the user just scrolls over the item too fast, it doesn't kick off. But I have a problem now: Say the user mouses over the item and then mouses out. If I use the Storyboard.BeginTime property, that won't safe guard against that behavior because eventhough the animation gets delayed, it's still going to start eventually... So is there a way I could prevent that from happening?
Any suggestions?
check in your mouseleave eventhandler if the first storyboard is still running and if that is the case attach the starting of the second storyboard to the Completed event of the first storybaord:
private void OnOddRowMouseLeave(object sender, MouseEventArgs e)
{
...
if(_firstStoryboard.GetCurrentState() != ClockState.Stopped)
_firstStoryboard.Completed += (s,e) => _secondStoryboard.Begin();
else
_secondStoryboard.Begin()
Everything that Silverlight does is asyncronous and so most likely what is happening is that because you are moving quickly in and out of the box the mouse leave is being fired before the mouseenter has a chance to finish. You could setup your two events so thay they have an indicator of whether or not the other is in process. For example you could do this
bool mouseOut =false;
bool mouseIn =false;
void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if(!this.mouseOut)
{
this.mouseIn = true;
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
if(this.mouseOut)
{
this.OnMouseLeave();
}
this.mouseIn = false;
}
}
void OnMouseLeave()
{
if(!this.mouseIn)
{
this.mouseOut = false;
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
else
{
this.mouseOut = true;
}
}
I haven't actually checked this code but this should help you to at least get closer to what you want. This should be quick enough that your user doesn't realize that it is not firing exactly on exit if they go over it quickly. But this should help to keep you from getting overlap.
Another way you could do this is setup the initial events as null, and have the mouse in event set the mouse in event when it is complete but the problem with that is that if the mouse out fires before the event is set then you don't event get the event firing.

Disable right click "Silverlight" popup in comboboxes

Hi
I'm trying to get rid of the annoying "About Silverlight" context menu that pops up whenever you right click in a Silverlight application. I've added the usual ways:
In App.xaml
rootVisual.MouseRightButtonDown += ((s, args) => args.Handled = true);
and the same for all ChildWindows.
The problem that persist is in all "pop up"-controls like comboboxes and datepicker calender popup. There I can't get rid of it. I would like to handle the right click in a style that I can make implicit for the entire application. Is this possible? Can I solve it some other smart way?
Best
Daniel
The answer was to inherit the combobox and make a custom control like this:
public class CellaComboBox : ComboBox
{
public CellaComboBox()
{
DropDownOpened += _dropDownOpened;
DropDownClosed += _dropDownClosed;
}
private static void _dropDownClosed(object sender, EventArgs e)
{
HandlePopupRightClick(sender, false);
}
private static void _dropDownOpened(object sender, EventArgs e)
{
HandlePopupRightClick(sender, true);
}
private static void HandlePopupRightClick(object sender, bool hook)
{
ComboBox box = (ComboBox)sender;
var popup = box.GetChildElement<Popup>();
if (popup != null)
{
HookPopupEvent(hook, popup);
}
}
static void HookPopupEvent(bool hook, Popup popup)
{
if (hook)
{
popup.MouseRightButtonDown += popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown += popup_MouseRightButtonDown;
}
else
{
popup.MouseRightButtonDown -= popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown -= popup_MouseRightButtonDown;
}
}
static void popup_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
with the extension method for framworkelement looking like this:
public static class FrameworkElementExtensions
{
public static TType GetChildElement<TType>(this DependencyObject parent) where TType : DependencyObject
{
TType result = default(TType);
if (parent != null)
{
result = parent as TType;
if (result == null)
{
for (int childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); ++childIndex)
{
var child = VisualTreeHelper.GetChild(parent, childIndex) as FrameworkElement;
result = GetChildElement<TType>(child) as TType;
if (result != null) return result;
}
}
}
return result;
}
}
You need to handle the DatePicker in the same way but instead of DropDownOpened and DropDownClosed you use CalenderOpened and CalenderClosed
C# Corner has an article for fixing the about popup on Silverlight 3:
Disable Context Menu in Silverlight 3 Application

Drag and Drop between 2 list boxes

Trying to implement drag and drop between 2 listboxes and all examples I've seen so far don't really smell good.
Can someone point me to or show me a good implementation?
Here's a sample form. Get started with a new WF project and drop two list boxes on the form. Make the code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
listBox1.Items.AddRange(new object[] { "one", "two", "three" });
listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
listBox1.MouseMove += new MouseEventHandler(listBox1_MouseMove);
listBox2.AllowDrop = true;
listBox2.DragEnter += new DragEventHandler(listBox2_DragEnter);
listBox2.DragDrop += new DragEventHandler(listBox2_DragDrop);
}
private Point mDownPos;
void listBox1_MouseDown(object sender, MouseEventArgs e) {
mDownPos = e.Location;
}
void listBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button != MouseButtons.Left) return;
int index = listBox1.IndexFromPoint(e.Location);
if (index < 0) return;
if (Math.Abs(e.X - mDownPos.X) >= SystemInformation.DragSize.Width ||
Math.Abs(e.Y - mDownPos.Y) >= SystemInformation.DragSize.Height)
DoDragDrop(new DragObject(listBox1, listBox1.Items[index]), DragDropEffects.Move);
}
void listBox2_DragEnter(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
if (obj != null && obj.source != listBox2) e.Effect = e.AllowedEffect;
}
void listBox2_DragDrop(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
listBox2.Items.Add(obj.item);
obj.source.Items.Remove(obj.item);
}
private class DragObject {
public ListBox source;
public object item;
public DragObject(ListBox box, object data) { source = box; item = data; }
}
}
the proper way to do a drag-drop control in .net is by running code in the 2nd control's DragDrop event handler.
It may "smell" weird, but this is how it works in .NET.
Google gave this: http://www.codeproject.com/KB/dotnet/csdragndrop01.aspx
It seems a pretty reasonable tutorial. If it smells bad, I think that's more to do with the API for drag and drop being awkward to use rather than the tutorial itself being poor.

Resources