How to remove dashed rectangle when doing drag and drop - c#

I'm following Bea Stollnitz's blog post on implementing drag and drop on an data bound ItemsControl. It works very nicely, but I have a question for anyone who's experienced something similar...
When I begin dragging the item, there is a small, dashed rectangle at the bottom of the mouse. I cannot figure out at all how to hide that rectangle. Does anyone know how to get rid of this? I would add a screenshot, but when I do a Print Screen, the rectangle doesn't appear.
I think it has something to do with a focus setting on the AdornerLayer that the "DraggedAdorner" is added to.
Thanks!

Try this in the Style of the Visual that is surrounded by the rectangle:
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
EDIT: The effect that you are seeing is a result of the DragDropEffects.Move assignment. You can mitigate this visual by simply changing the following line (Line #168 in the sample):
DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);
To this:
DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.None);
Thus setting the DragDropEffect to DragDropEffects.None
NOTE: In the sample, it evaluates the DragDropEffects value in the process of performing the drag & drop, so you would need to work around this (Probably a simple AttachedProperty, or even casting the Sender as a FrameworkElement and using the Tag property), but this should resolve the visual issue.
I hope this helps, and if I can help you further feel free to let me know. Good luck!

The dashed rectangle is actually part of the default cursor used when performing a drag and drop "Move" operation.
This is the default "Move" cursor:
And this is the default "Copy" cursor (if you hold CTRL while dragging):
You can get more control over what mouse cursor is shown by overriding UIElement.OnGiveFeedback, or subscribing to the UIElement.GiveFeedback Routed Event.
When handling the event and changing the cursor, make sure to set e.Handled = true; to prevent the cursor from flickering.
For example, use this override on the element initiating the Drag operation (see this walkthrough for more info):
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
if (e.Effects.HasFlag(DragDropEffects.Copy))
{
Mouse.SetCursor(Cursors.Cross);
}
else if (e.Effects.HasFlag(DragDropEffects.Move))
{
Mouse.SetCursor(Cursors.Pen);
}
else
{
Mouse.SetCursor(Cursors.No);
}
e.Handled = true;
}

Related

Is it possible to paint a WinForms control outside it's own region (effectively hiding it)?

Before you jump to conclusions and ask "Why don't you just Hide() the form?" let me explain.
I have the following:
A PDF Viewer control PDFViewer.
A form containing a TextBox TextBoxForm.
A parent form containing PDFViewer and TextBoxForm (with Owner property set to this parent).
TextBoxForm is an overlay to PDFViewer and should be hidden when outside the bounds of PDFViewer, which can happen if the user scrolls. Here's how I achieve that:
PDFViewer_OnScroll(object sender, HandledMouseEventArgs e){
TextBoxForm.SetBounds(GetBounds(PDFViewer));
TextBoxForm.Top = [some value];
}
Ìnside TextBoxForm I do the following:
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
UpdateFormRegion(); // Offset bounds so it's relative to the form itself instead of the screen
Invalidate();
}
This works fine when TextBoxForm is completely or partially inside the bounds of PDFViewer, but as soon as it's moved completely outside, whichever part of TextBoxForm was rendered before will still be rendered (I assumed it's because Windows decides there is no need to re-paint the form, when it's not inside it's own bounds).
It's easy enough to make a check for when this happens, but the reason I don't want to call TextBoxForm.Hide() is because that causes the TextBox to lose focus.
Any way I can get the form hidden without losing focus?
I just thought of a possible solution (not a very elegant one, however):
if (Right < _bounds.Left || Left > _bounds.Right || Bottom < _bounds.Top || Top > _bounds.Bottom)
{
Top = -20000;
Left = -20000;
}
Basically, whenever the form is moved/resized I check if it is now completely outside the supplied bounds and move it way off screen if true.
A less "hacky" solution is still welcome.

Unexpected output from checking if mouse within control

I am implementing a custom drag and drop interface with winForm Buttons and after viewing several solutions on how to obtain mouse position and check it against control bound have not been able to get it to work.
I have tried:
button.ClientRectangle.Contains(PointToClient(Cursor.Position))
and
button.ClientRectangle.Contains(PointToClient(Control.MousePosition))
Both of these have failed to work. Checking the mouse bounds seem like a simple operation, but I really am stumped.
My speculation of the unexpected values are:
Process of obtaining cursor position may be in wrong corner of cursor image
Method/Function does not work on Buttons for some reason
You are using the wrong object reference, calculating the mouse position relative to the form instead of the button. And you are writing it in a way that make this very hard to debug. Fix:
var pos = button.PointToClient(Cursor.Position);
System.Diagnostics.Debug.WriteLine(pos); // Now it is easy
if (button.ClientRectangle.Contains(pos)) {
// etc...
}

Moving buttons in a Canvas

The following code is supposed to move UIElements in a canvas, when the mouse hovers over them, and the user hits Ctrl.
void keydown(Object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
{
control++;
if (control == 1)
{
drag = true;
elem = (UIElement)Mouse.DirectlyOver;
}
else
drag = false;
control %= 2;
}
}
void mousemove(object sender, MouseEventArgs e)
{
Point p = e.GetPosition(canvas);
if (drag)
{
if (elem == null) return;
//Canvas.SetLeft(myButton, p.X); <-- this works, but then why doesn't it work when I generalize it?
Canvas.SetLeft(elem, p.X);
Canvas.SetTop(elem, p.Y);
}
}
Any Shapes component, e.g. Rectangles will move when I hover the mouse over them, and hit control..But it doesn't work with Buttons, TextBoxes, TextViews, etc. Can anyone explain?
The documentation for Mouse.DirectlyOver says:
Controls can be composed of multiple elements. DirectlyOver reports the specific element in the composite control the mouse pointer is over and not the control itself. For example, depending on which part of a Button the pointer is over, the DirectlyOver property could report the TextBox of the Content property or the ButtonChrome.
In other words: A Button is made up of several sub-elements, like a ButtonChrome and a TextBlock (usually not a TextBox -- I think that's a typo on the MSDN page). When you call Mouse.DirectlyOver, you're probably getting one of those elements, rather than the Button.
Since those elements are not parented to a Canvas (they're parented to something in the Button's control template, most likely a Grid), setting the Canvas.Left and Canvas.Top attached properties will have no effect.
You probably want to walk up the visual tree (using VisualTreeHelper.GetParent) until you find something you're interested in dragging. How you determine whether you're interested in any given element is up to you. You could go until you find something that's parented to your Canvas, or you could just go until you find something that's one of a given set of types (stopping when you find something that descends from Control might be a decent place to start).
My guess is that those events are swallowed by the implementation of the controls that don't work.
I'm not in front of my computer now, but isn't there a previewmousemove event? Try using that?
Update: scratch that!
Just use e.Souce. The event source is designed to be the element your likely to be interested in. You'll need to cast to UIElement, but I'm pretty sure that'll work.

Gradient Drawing Bug

I'm having trouble with a gradient drawing call. I have a Form that looks like this.
Screenshot http://img413.imageshack.us/img413/3570/30364682.png
The problem is every now and again the above gradient drawing bug will start happening. It should go right across of course. Sometimes it only takes some build-rebuild-mashing to fix and it'll simply just "start" after a build every now and again.
That control (the top white part) is a TableLayoutPanel. The BackColor is set to white and on the panel's Paint event I do this:
/// <summary>
/// Draws the background gradient.
/// </summary>
private void titleBarLayoutPanel_Paint(object sender, PaintEventArgs e)
{
Brush brush = new LinearGradientBrush(titleBarLayoutPanel.Bounds, TaskHeaderLeftColor, TaskHeaderRightColor, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brush, titleBarLayoutPanel.Bounds);
}
Should I be doing something else? The problem is that it works, and then without so much as a rebuild or build this will start happening!
EDIT I have since rebuilt the class library it is contained in (it's a generic Form) then rebuilt the app it's used in and the gradient is now filling across completely. This is bizarre.
Building and re-building your application, with no changes, normally doesn't solve this (or most any other bug for that matter) save the ones in which you run your application without doing a clean/rebuild first and then notice that the code you just wrote doesn't run (not sure that's possible these days with the IDEs). I see this a lot with newer devs when they keep rebuilding hoping that somehow the compiler will make the code "correct" or that maybe the compiler is simply not generating the correct code to begin with. (Please note that I do not mean the aforementioned statements to be taken disparagingly.)
To solve the issue at hand, you might try deriving your own TableLayoutPanel class in which you override the OnBackgroundPaint event, painting your own background, or simply returning if you don't want to paint your own background. (You seem to be painting the background in the Paint event). What you are doing in the code above is simply painting over the background already painted by the control, hence the "bug" you see, (double paint). It appears that the form is not resizable. Try making it resizable. Then resize it and watch it paint, or simply move other windows over it.
class CustomTableLayoutPanel : TableLayoutPanel
{
protected override void OnPaintBackground(PaintEventArgs e)
{
Brush brush = new LinearGradientBrush(this.ClientRectangle, TaskHeaderLeftColor, TaskHeaderRightColor, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brush, this.ClientRectangle);
//base.OnPaintBackground(e);
}
}
By the way, you should replace Bounds with ClientRectangle.
Bounds is the control's rectangle relative to its parent; ClientRectangle is relative to the control itself.
In this particular case, it won't make a difference, since the control is at 0, 0.

How to set/change/remove focus style on a Button in C#?

I have a couple of buttons of which I modified how they look. I have set them as flat buttons with a background and a custom border so they look all pretty and nothing like normal buttons anymore (actually, they look like Office 2003 buttons now ;-). The buttons have a border of one pixel.
However when the button gets selected (gets the focus through either a click or a keyboard action like pressing the tab key) the button suddenly gets and extra border around it of the same colour, so making it a two pixel border. Moreover when I disable the one pixel border, the button does not get a one pixel border on focus.
On the net this question is asked a lot like 'How can I disable focus on a Button', but that's not what I want: the focus should still exist, just not display in the way it does now.
Any suggestions? :-)
Is this the effect you are looking for?
public class NoFocusCueButton : Button
{
protected override bool ShowFocusCues
{
get
{
return false;
}
}
}
You can use this custom button class just like a regular button, but it won't give you an extra rectangle on focus.
I had the same issue with the annoying double border, and stumbled across this thread looking for an answer...
The way I solved this was to set the BorderSize to 0 then draw my own border in OnPaint
Note: Not the entire button, just the border
A simple example would be:
public class CustomButton : Button
{
public CustomButton()
: base()
{
// Prevent the button from drawing its own border
FlatAppearance.BorderSize = 0;
FlatStyle = System.Windows.Forms.FlatStyle.Flat;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw Border using color specified in Flat Appearance
Pen pen = new Pen(FlatAppearance.BorderColor, 1);
Rectangle rectangle = new Rectangle(0, 0, Size.Width - 1, Size.Height - 1);
e.Graphics.DrawRectangle(pen, rectangle);
pen.Dispose();
}
}
In my case, this is how I made a button that mimics a ToolStripButton, where the border is only visible when you hover over the button:
public class ToolButton : Button
{
private bool ShowBorder { get; set; }
public ToolButton()
: base()
{
// Prevent the button from drawing its own border
FlatAppearance.BorderSize = 0;
// Set up a blue border and back colors for the button
FlatAppearance.BorderColor = Color.FromArgb(51, 153, 255);
FlatAppearance.CheckedBackColor = Color.FromArgb(153, 204, 255);
FlatAppearance.MouseDownBackColor = Color.FromArgb(153, 204, 255);
FlatAppearance.MouseOverBackColor = Color.FromArgb(194, 224, 255);
FlatStyle = System.Windows.Forms.FlatStyle.Flat;
// Set the size for the button to be the same as a ToolStripButton
Size = new System.Drawing.Size(23, 22);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
// Show the border when you hover over the button
ShowBorder = true;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
// Hide the border when you leave the button
ShowBorder = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// The DesignMode check here causes the border to always draw in the Designer
// This makes it easier to place your button
if (DesignMode || ShowBorder)
{
Pen pen = new Pen(FlatAppearance.BorderColor, 1);
Rectangle rectangle = new Rectangle(0, 0, Size.Width - 1, Size.Height - 1);
e.Graphics.DrawRectangle(pen, rectangle);
pen.Dispose();
}
}
// Prevent Text from being set on the button (since it will be an icon)
[Browsable(false)]
public override string Text { get { return ""; } set { base.Text = ""; } }
[Browsable(false)]
public override ContentAlignment TextAlign { get { return base.TextAlign; } set { base.TextAlign = value; } }
}
Make a custom button:
public partial class CustomButton: Button
{
public ButtonPageButton()
{
InitializeComponent();
this.SetStyle(ControlStyles.Selectable, false);
}
}
That'll get rid of that annoying border! ;-)
Another option (although a bit hacktastic) is to attach an event-handler to the button's GotFocus event. In that event-handler, pass a value of False to the button's NotifyDefault() method. So, for instance:
void myButton_GotFocus(object sender, EventArgs e)
{
myButton.NotifyDefault(false);
}
I'm assuming this will work every time, but I haven't tested it extensively. It's working for me for now, so I'm satisfied with that.
There is another way which works well for flat styled buttons. Don't use buttons but labels. As you are completely replacing the UI for the button it does not matter whether your use a button control or a label. Just handle the click in the same way.
This worked for me, although not great practice it is a good hack and as long as you name the button obviously (and comment the source) other coders will pick up the idea.
Ryan
The second border which gets added is the Windows standard "default button" border. You may have noticed that if you tab through most dialog boxes with multiple buttons (such as any Control Panel properties window), the original "double-bordered" button becomes "normal," and the in-focus button becomes "double-bordered."
This isn't necessarily focus at work, but rather a visual indication of the action undertaken by hitting the Enter key.
It sounds, to me, like you don't really care about that internal working. You want the display to not have two borders -- totally understandable. The internal working is to explain why you're seeing this behavior. Now ... To try and fix it.
The first thing I'd try -- and bear in mind, I haven't validated this -- is a hack. When a button receives focus (thereby getting the double-border), turn off your single border. You might get the effect you want, and it's pretty simple. (Hook into the Focus event. Even better, subclass Button and override OnFocus, then use that subclass for your future buttons.)
However, that might introduce new, awkward visual side effects. In that vein -- and because hacks are rarely the best answer -- I have to "officially" recommend what others have said: Custom paint the button. Although the code here may be overkill, this link at CodeProject discusses how to do that (VB link; you'll need translate). You should, in a full-on custom mode, be able to get rid of that second border completely.
Certainly you can draw the button yourself. One of the state flags is focused.
So on the draw event if the flag is focused go ahead and draw the button how you like, otherwise just pass it on to the base method.
Consider implementing your own drawing code for the button. That way you have full control. In the past, I've implemented my own Control derivative that custom paints my button and implements all the button characteristics for my purposes, but you should be able to override the button's painting and do it yourself, thereby controlling how it draws in every state, including when focused.
Set the FocusVisualStyle dependency property to null in your style, and the dotted border will be gone.
From MSDN: Styling for Focus in Controls, and FocusVisualStyle
Windows Presentation Foundation (WPF)
provides two parallel mechanisms for
changing the visual appearance of a
control when it receives keyboard
focus. The first mechanism is to use
property setters for properties such
as IsKeyboardFocused within the style
or template that is applied to the
control. The second mechanism is to
provide a separate style as the value
of the FocusVisualStyle property; the
"focus visual style" creates a
separate visual tree for an adorner
that draws on top of the control,
rather than changing the visual tree
of the control or other UI element by
replacing it. This topic discusses the
scenarios where each of these
mechanisms is appropriate.
The extra border you see is defined by the FocusVisualStyle and not in the control template, so you need to remove or override the style to remove the border.
If you have a textbox and a button
then on textchange event of textbox
write button1.focus();
It will work.
You can also create an invisible button and make it active whenever you press another button.
I've had good luck merely setting the Focusable property of the button to be false:
<Button HorizontalAlignment="Left" Margin="0,2"
Command="{Binding OpenSuspendedJobCommand, Mode=OneWay}"
Focusable="False"
Style="{StaticResource ActionButton}" Content="Open Job..." />

Categories