Flicker on DragMove of Border-less WPF Custom Window - wpf

I have a custom, border-less window intended to act as a base form for future WPF projects. I've managed to simulate near all of the native window behaviours, but I'm having a problem with DragMove. I wanted the window to reposition itself above the task bar when the user drags the its title bar behind the windows task bar, so that the whole window is visible. I managed to do this by overriding the OnLocationChanged method, because all the mouse related handlers are blocked during DragMove.
This works like a charm, except I get a flicker when dragging the title bar along the windows task bar. I can see it is trying to redraw the window where it would have been, down below, and then it immediately redraws the window again in the intended place.
How can I interrupt this redrawing properly? Many Thanks!
This, this, this and this SO question did not help me at all, unfortunately. Here is an extract of my code:
protected override void OnLocationChanged(EventArgs e)
{
base.OnLocationChanged(e);
var activeScreenHeight = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(this).Handle).WorkingArea.Bottom - ToolbarSnapThreshold;
if (activeScreenHeight < this.RestoreBounds.Top)
{
this.Top = activeScreenHeight - this.RestoreBounds.Height + ToolbarSnapThreshold;
}
}
protected void MaximizeOrRestore()
{
this.WindowState = (this.WindowState == WindowState.Normal) ? WindowState.Maximized : WindowState.Normal;
this.lastNonMinimizedState = this.WindowState;
}
protected void RestoreAndDragMove(MouseButtonState leftMouseButtonState)
{
if (leftMouseButtonState == MouseButtonState.Pressed)
{
if (this.WindowState == WindowState.Maximized)
{
var activeScreenWidth = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(this).Handle).Bounds.Width;
var windowCurrentWidth = this.RestoreBounds.Width;
var windowPositionAdjustX = this.relativeMousePositionX - (windowCurrentWidth / 2);
if (windowPositionAdjustX < 0)
{
windowPositionAdjustX = 0;
}
else if (windowPositionAdjustX + windowCurrentWidth > activeScreenWidth)
{
windowPositionAdjustX = activeScreenWidth - windowCurrentWidth;
}
this.WindowState = WindowState.Normal;
this.lastNonMinimizedState = this.WindowState;
this.Left = windowPositionAdjustX - this.relativeMousePositionX + this.originalMousePositionX;
this.Top = 0 - this.relativeMousePositionY + this.originalMousePositionY;
}
this.DragMove();
}
}
private void Window_TitleBarClicked(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
this.relativeMousePositionX = e.GetPosition(this).X;
this.relativeMousePositionY = e.GetPosition(this).Y;
this.originalMousePositionX = System.Windows.Forms.Cursor.Position.X;
this.originalMousePositionY = System.Windows.Forms.Cursor.Position.Y;
if (e.ClickCount == 2)
{
this.MaximizeOrRestore();
}
}
}

Related

"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.

How do I hittest for a TabControl tab?

Given a point relative to a Page, how do I hittest for a TabControl's tab? VisualTreeHelper.HitTest gives me the contents, but when I go up the visual tree I see nothing that would tell me that I have actually hit a tab. I don't even see the tab control itself.
public class ViewManipulationAgent : IDisposable
{
private const int _limit = 125;
private INavigationService _navigationService;
private FrameworkElement _container;
private FrameworkElement _element;
private TranslateTransform _translate;
private IInputElement _touchTarget;
// When I use this object,
// a_container is the main Frame control in my application.
// a_element is a page within that frame.
public ViewManipulationAgent(FrameworkElement a_container, FrameworkElement a_element)
{
_navigationService = a_navigationService;
_container = a_container;
_element = a_element;
// Since I set IsManipulationEnabled to true all touch commands are suspended
// for all commands on the page (a_element) unless I specifically cancel (see below)
_element.IsManipulationEnabled = true;
_element.PreviewTouchDown += OnElementPreviewTouchDown;
_element.ManipulationStarting += OnElementManipulationStarting;
_element.ManipulationDelta += OnElementManipulationDelta;
_element.ManipulationCompleted += OnElementManipulationCompleted;
_translate = new TranslateTransform(0.0, 0.0);
_element.RenderTransform = _translate;
}
// Since the ManipulationStarting doesn't provide position I capture the position
// here and then hit test elements to find any controls for which I want to bypass
// manipulation.
private void OnElementPreviewTouchDown(object sender, TouchEventArgs e)
{
var position = e.GetTouchPoint(_element).Position;
_touchTarget = null;
HitTestResult result = VisualTreeHelper.HitTest(_element, position);
if (result.VisualHit == null)
return;
var button = VisualTreeHelperEx.FindAncestorByType<ButtonBase>(result.VisualHit) as ButtonBase;
if (button != null)
{
_touchTarget = button;
return;
}
var slider = VisualTreeHelperEx.FindAncestorByType<Slider>(result.VisualHit) as Slider;
if (slider != null)
{
_touchTarget = slider;
return;
}
}
// Here is where I cancel manipulation if a specific touch target was found in the
// above event.
private void OnElementManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
if (_touchTarget != null)
{
e.Cancel(); // <- I have to cancel manipulation or the buttons and other
// controls cannot be manipulated by the touch interface.
return;
}
e.ManipulationContainer = _container;
}
private void OnElementManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var element = e.Source as FrameworkElement;
if (element == null)
return;
var translate = _translate.X + e.DeltaManipulation.Translation.X;
if (translate > _limit)
{
GoBack();
translate = _limit;
}
if (translate < -_limit)
{
GoForward();
translate = -_limit;
}
_translate.X = translate;
}
private void GoForward()
{
var navigationService = ServiceLocator.Current.GetInstance<INavigationService>();
navigationService.GoForward();
}
private void GoBack()
{
var navigationService = ServiceLocator.Current.GetInstance<INavigationService>();
navigationService.GoBack();
}
private void OnElementManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
_touchTarget = null;
_translate.X = 0;
}
public void Dispose()
{
_element.PreviewTouchDown -= OnElementPreviewTouchDown;
_element.ManipulationStarting -= OnElementManipulationStarting;
_element.ManipulationDelta -= OnElementManipulationDelta;
_element.ManipulationCompleted -= OnElementManipulationCompleted;
}
}

FocusVisualStyle In RadioButton not Work

This is not working for me, focus on radio button only works when pressed the Tab key! Does anyone know how to fix?
void SelectPaymentModeView_Loaded(object sender, RoutedEventArgs e)
{
this.radPaymentMode.Focus();
}
The contents of the radiobutton is text... I also try Keyboard.Focus(this.radPaymentMode);
See the complete code:
PaymentMode[] modes = data[1] as PaymentMode[];
if (modes.Length > 0)
{
for (int i = 0; i < modes.Length; i++)
{
RadioButton rad = new RadioButton();
rad.Name = "radPayment" + i;
rad.GroupName = "PaymentModes";
rad.Content = modes[i].Name;
rad.DataContext = modes[i];
rad.Margin = new Thickness(110, 0, 0, 5);
rad.VerticalAlignment = System.Windows.VerticalAlignment.Center;
rad.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
Grid.SetRow(rad, 3 + i);
Grid.SetColumn(rad, 1);
gridPaymentModes.RowDefinitions.Insert(3, new RowDefinition());
gridPaymentModes.Children.Add(rad);
radPaymentModes.Add(rad);
if (!string.IsNullOrEmpty((this.DataContext as Order).Payment))
{
String paymentOrder = rad.Content as String;
if (paymentOrder.Equals((this.DataContext as Order).Payment))
{
rad.IsChecked = true;
}
}
rad.Checked += new RoutedEventHandler(rad_Checked);
}
radPaymentModes[0].Loaded += SelectPaymentModeView_Loaded;
}
void SelectPaymentModeView_Loaded(object sender, RoutedEventArgs e)
{
FocusManager.SetFocusedElement(FocusManager.GetFocusScope((sender as RadioButton)), (sender as RadioButton));
}
The keyboard focus manager makes the dotted focus adorner visible when the keyboard is used to tab to a control (WPF wants to hide the focus rect when the mouse is used for example so there's less visual clutter).
To force it, use code like this (assuming btnRadio is your button):
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(btnRadio), btnRadio);

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

Recommendation for Windows.Forms Control that Supports Mouse Panning and Zooming

I found Customize a panel with Autoscroll property at http://www.codeproject.com/KB/miscctrl/CustomAutoScrollPanel.aspx that is wrapper around a Panel with AutoScroll = True.
I like this control because it provides the "performScrollHorizontal" and "performScrollVertical" methods. Yet, it uses the Windows API functions instead of ScrollableControl and its VerticalScroll and HorizontalScroll properties.
Is this a good control to use? I think it should be using ScrollableControl instead of the Windows API. What do you think? Is there a better control available? Do I even need a control? I would think that ScrollableControl provides everything I would need.
EDIT: I found the HScrollBar and VScrollBar controls. Should I be using them?
These two other controls are nice but do not give me a way to control the scroll bars like the above control does.
A scrollable, zoomable, and scalable picture box at
http://www.codeproject.com/KB/miscctrl/ScalablePictureBox.aspx
Pan and Zoom Very Large Images at
http://www.codeproject.com/KB/GDI-plus/PanZoomExample.aspx
What I really want is a control:
that scrolls when the user moves the mouse toward the edge of the control,
allows the user to pan
allows the user to zoom
supports using the mouse with Shift or Ctrl or Alt keys pressed
Any recommendations, help, or areas to look at is greatly appreciated. A control would be nice as I am not that good yet.
Some code to play with. It supports focus, panning and scrolling. Zooming is work-to-do, my laptop's mousepad got in the way of testing it. Use a timer to implement auto-scrolling at the edges.
using System;
using System.Drawing;
using System.Windows.Forms;
class ZoomPanel : Panel {
public ZoomPanel() {
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.AutoScroll = this.TabStop = true;
}
public Image Image {
get { return mImage; }
set {
mImage = value;
Invalidate();
mZoom = 1.0;
this.AutoScrollMinSize = (mImage != null) ? mImage.Size : Size.Empty;
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.Cursor = Cursors.SizeAll;
mLastPos = e.Location;
this.Focus();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) this.Cursor = Cursors.Default;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.AutoScrollPosition = new Point(
-this.AutoScrollPosition.X - e.X + mLastPos.X,
-this.AutoScrollPosition.Y - e.Y + mLastPos.Y);
mLastPos = e.Location;
Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
if (mImage != null) {
mZoom *= 1.0 + 0.3 * e.Delta / 120;
this.AutoScrollMinSize = new Size((int)(mZoom * mImage.Width),
(int)(mZoom * mImage.Height)); \
// TODO: calculate new AutoScrollPosition...
Invalidate();
}
base.OnMouseWheel(e);
}
protected override void OnPaint(PaintEventArgs e) {
if (mImage != null) {
var state = e.Graphics.Save();
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
e.Graphics.DrawImage(mImage,
new Rectangle(0, 0, this.AutoScrollMinSize.Width, this.AutoScrollMinSize.Height));
e.Graphics.Restore(state);
}
//if (this.Focused) ControlPaint.DrawFocusRectangle(e.Graphics,
// new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height));
base.OnPaint(e);
}
protected override void OnEnter(EventArgs e) { Invalidate(); base.OnEnter(e); }
protected override void OnLeave(EventArgs e) { Invalidate(); base.OnLeave(e); }
private double mZoom = 1.0;
private Point mLastPos;
private Image mImage;
}

Resources