How to select ListViewItem with left mouse button only? - c#

How can i prevent the right click from selecting an item in my listview both in click and double click?

You could use this code, I think it should do the work. You need to set some bool variable to indicate that the right mouse has been clicked in your MouseDown, then Clear selected items, if SelectedIndexChanged event handler fired because of the right click and then reset the indicator on MouseUp event. Check the code:
bool rightClicked = false;
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
rightClicked = true;
}
else
{
rightClicked = false;
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (rightClicked)
{
listView1.SelectedItems.Clear();
}
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
rightClicked = false;
}
EDIT: This is the best I could do, it preserves the selection but flickers. the solution could be implemented using some custom drawing of the items but that requires too much time. I leave that to you.
bool rightClicked = false;
int [] lviListIndex = null;
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
rightClicked = true;
lviListIndex = new int[listView1.SelectedItems.Count];
listView1.SelectedIndices.CopyTo(lviListIndex, 0);
}
else
{
rightClicked = false;
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (rightClicked)
{
listView1.SelectedIndices.Clear();
}
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
if (rightClicked)
{
listView1.SelectedIndexChanged -= new System.EventHandler(listView1_SelectedIndexChanged);
if (lviListIndex != null)
{
foreach (int index in lviListIndex)
{
listView1.SelectedIndices.Add(index);
}
}
lviListIndex = null;
listView1.SelectedIndexChanged += new System.EventHandler(listView1_SelectedIndexChanged);
}
rightClicked = false;
}

Subclass the listview and add this override:
protected override void WndProc(ref Message m)
{
const int WM_RBUTTONUP = 0x0205;
const int WM_RBUTTONDOWN = 0x0204;
if ((m.Msg != WM_RBUTTONDOWN) && (m.Msg != WM_RBUTTONUP))
{
base.WndProc(ref m);
}
}
This works by ignoring messages from the right button of the mouse.

you can check like this if (e.Button == MouseButtons.Left)

Related

Reorder customcontrol inside multiple flowLayoutPanel

I am trying to make an app to generate a tierlist.
Each tier consists of a flowlayout and you basically move the controls around by drag-and-drop.
The problem is that even though I can move the elements with drag-and-drop, the element is always added to the end of the list, and not where you drop the pointer.
Is there any way to maintain order during drag-and-drop?
These are the methods that are in the flowlayout
private void flow_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void flow_DragDrop(object sender, DragEventArgs e)
{
((UserControl1)e.Data.GetData(typeof(UserControl1))).Parent = (Panel)sender;
}
While these are from the usercontrol:
private void UserControl1_MouseDown(object sender, MouseEventArgs e)
{
this.DoDragDrop(this, DragDropEffects.Move);
}
You can opt this solution to reorder the dropped controls into a FlowLayoutPanel control. The part that utilizes the Control.ControlCollection.GetChildIndex and Control.ControlCollection.SetChildIndex methods.
Let's say you have a custom Control or UserControl named DragDropControl:
public class DragDropControl : Control
{
public DragDropControl()
{
AllowDrop = true;
BackColor = Color.LightSteelBlue;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
TextRenderer.DrawText(e.Graphics, Text, Font,
ClientRectangle, Color.Black,
TextFormatFlags.HorizontalCenter |
TextFormatFlags.VerticalCenter);
}
}
Note: From what I see in the images, just use a simple Label control instead.
Let's create a custom FlowLayoutPanel and encapsulate all the required functionalities to not repeat that in your implementation for each FLP.
public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
public DragDropFlowLayoutPanel()
{
AllowDrop = true;
}
[DefaultValue(true)]
public override bool AllowDrop
{
get => base.AllowDrop;
set => base.AllowDrop = value;
}
The custom FLP implements the mouse, drag and drop events of its children as well.
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (e.Control is DragDropControl)
{
e.Control.DragOver += OnControlDragOver;
e.Control.DragDrop += OnControlDragDrop;
e.Control.MouseDown += OnControlMouseDown;
}
}
protected override void OnControlRemoved(ControlEventArgs e)
{
base.OnControlRemoved(e);
e.Control.DragOver -= OnControlDragOver;
e.Control.DragDrop -= OnControlDragDrop;
e.Control.MouseDown -= OnControlMouseDown;
}
Handle the child controls MouseDown event to do the drag:
private void OnControlMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var control = sender as DragDropControl;
DoDragDrop(control, DragDropEffects.Move);
}
}
Handle the DragEnter and DragOver methods and events of the FLP and its children to set the drop effect.
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
if (e.Data.GetDataPresent(typeof(DragDropControl)))
e.Effect = DragDropEffects.Move;
}
protected override void OnDragOver(DragEventArgs e)
{
base.OnDragOver(e);
if (e.Data.GetDataPresent(typeof(DragDropControl)))
e.Effect = DragDropEffects.Move;
}
private void OnControlDragOver(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
{
var p = PointToClient(new Point(e.X, e.Y));
if (GetChildAtPoint(p) == ddc)
e.Effect = DragDropEffects.None;
else
e.Effect = DragDropEffects.Move;
}
}
Finally, call from the DragDrop method override and event the DropControl method and pass the DragEventArgs param.
protected override void OnDragDrop(DragEventArgs e)
{
base.OnDragDrop(e);
DropControl(e);
}
private void OnControlDragDrop(object sender, DragEventArgs e)
{
DropControl(e);
}
The dropped control takes the index of the control under the mouse position if any otherwise it will be inserted at the end of the Controls collection.
private void DropControl(DragEventArgs e)
{
if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
{
var p = PointToClient(new Point(e.X, e.Y));
var child = GetChildAtPoint(p);
var index = child == null
? Controls.Count
: Controls.GetChildIndex(child);
ddc.Parent = this;
Controls.SetChildIndex(ddc, index);
}
}
}
Put it all together.
public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
public DragDropFlowLayoutPanel()
{
AllowDrop = true;
}
[DefaultValue(true)]
public override bool AllowDrop
{
get => base.AllowDrop;
set => base.AllowDrop = value;
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (e.Control is DragDropControl)
{
e.Control.DragOver += OnControlDragOver;
e.Control.DragDrop += OnControlDragDrop;
e.Control.MouseDown += OnControlMouseDown;
}
}
protected override void OnControlRemoved(ControlEventArgs e)
{
base.OnControlRemoved(e);
e.Control.DragOver -= OnControlDragOver;
e.Control.DragDrop -= OnControlDragDrop;
e.Control.MouseDown -= OnControlMouseDown;
}
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
if (e.Data.GetDataPresent(typeof(DragDropControl)))
e.Effect = DragDropEffects.Move;
}
protected override void OnDragOver(DragEventArgs e)
{
base.OnDragOver(e);
if (e.Data.GetDataPresent(typeof(DragDropControl)))
e.Effect = DragDropEffects.Move;
}
protected override void OnDragDrop(DragEventArgs e)
{
base.OnDragDrop(e);
DropControl(e);
}
private void OnControlDragOver(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
{
var p = PointToClient(new Point(e.X, e.Y));
if (GetChildAtPoint(p) == ddc)
e.Effect = DragDropEffects.None;
else
e.Effect = DragDropEffects.Move;
}
}
private void OnControlDragDrop(object sender, DragEventArgs e)
{
DropControl(e);
}
private void OnControlMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var control = sender as DragDropControl;
DoDragDrop(control, DragDropEffects.Move);
}
}
private void DropControl(DragEventArgs e)
{
if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
{
var p = PointToClient(new Point(e.X, e.Y));
var child = GetChildAtPoint(p);
var index = child == null
? Controls.Count
: Controls.GetChildIndex(child);
ddc.Parent = this;
Controls.SetChildIndex(ddc, index);
}
}
}

How to read keystrokes in a custom Winforms control, regardless of focus

I have a custom component for WinForms, on which graphics are drawn.
Using the Ctrl+right/left mouse buttons, I can add or remove objects.
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = true;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (!this.EditorMode)
{
base.OnMouseDown(e);
return;
}
if (e.Button == MouseButtons.Left)
{
// adding new object
}
else if (e.Button == MouseButtons.Right)
{
// deleting object
}
}
Everything works fine until I add something else to the custom control.
The problem is that pressing the Ctrl key will no longer be handled by the controller, but by the element on which the focus is currently set.
And I need my keyboard shortcut to work regardless of which element the focus is on...
What is the best way to do this?
I tried to redefine Processcmdkey, but it does not allow me to know if the key was pressed or released
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.ControlKey | Keys.Control))
{
MessageBox.Show("ctrl");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
what should I do to get the desired result: regardless of focusing on child controls, I can always add new objects to the drawing?
My suggestion is to implement IMessageFilter in your MainForm and intercept
the WM_KEYDOWN message for Control-Left and Control-Right. The example below is a guideline for a UserControl that will add and remove buttons based on Control-Right and Control-Left respectively.
Adding and Removing the MessageFilter
The message filter will be added on the OnHandleCreated override and removed in the Dispose method of MainForm.
public partial class MainForm : Form, IMessageFilter
{
public MainForm() => InitializeComponent();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode ) || _isHandleInitialized)
{
_isHandleInitialized = true; ;
Application.AddMessageFilter(this);
}
}
bool _isHandleInitialized = false;
// In MainForm.Designer.cs
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
Application.RemoveMessageFilter(this);
}
base.Dispose(disposing);
}
}
Handling PreFilterMessage
The MainForm will provide three event hooks for ControlLeft, ControlRight, and NoCommand. These will be fired in the PreFilterMessage method.
public partial class MainForm : Form, IMessageFilter
{
const int WM_KEYDOWN = 0x100;
public bool PreFilterMessage(ref Message m)
{
if (Form.ActiveForm == this)
{
switch (m.Msg)
{
case WM_KEYDOWN:
var key = (Keys)m.WParam | ModifierKeys;
switch (key)
{
case Keys.Control | Keys.Left:
ControlLeft?.Invoke(this, EventArgs.Empty);
Text = "Control.Left";
break;
case Keys.Control | Keys.Right:
ControlRight?.Invoke(this, EventArgs.Empty);
Text = "Control.Right";
break;
default:
// Don't event if it's "just" the Control key
if(ModifierKeys == Keys.None)
{
Text = "Main Form";
NoCommand?.Invoke(this, EventArgs.Empty);
}
break;
}
break;
}
}
return false;
}
public event EventHandler ControlLeft;
public event EventHandler ControlRight;
public event EventHandler NoCommand;
}
Responding to events in the UserControl
The MainForm events will be subscribed to in the OnHandleCreated override of UserControlResponder.
public partial class UserControlResponder : UserControl
{
public UserControlResponder()
{
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode || _isHandleInitialized))
{
_isHandleInitialized = true;
var main = (MainForm)Parent;
main.ControlLeft += onControlLeft;
main.ControlRight += onControlRight;
main.NoCommand += onNoCommand;
}
}
private bool _isHandleInitialized = false;
char _tstCount = 'A';
private void onControlRight(object sender, EventArgs e)
{
BackColor = Color.LightBlue;
BorderStyle = BorderStyle.None;
var button = new Button
{
Text = $"Button {_tstCount++}",
Size = new Size(150, 50),
};
button.Click += onAnyButtonClick;
flowLayoutPanel.Controls.Add(button);
}
private void onControlLeft(object sender, EventArgs e)
{
BackColor = Color.LightGreen;
BorderStyle = BorderStyle.None;
if(flowLayoutPanel.Controls.Count != 0)
{
var remove = flowLayoutPanel.Controls[flowLayoutPanel.Controls.Count - 1];
if(remove is Button button)
{
button.Click -= onAnyButtonClick;
}
flowLayoutPanel.Controls.RemoveAt(flowLayoutPanel.Controls.Count - 1);
}
}
private void onNoCommand(object sender, EventArgs e)
{
BackColor = Color.Transparent;
BorderStyle = BorderStyle.FixedSingle;
}
private void onAnyButtonClick(object sender, EventArgs e)
{
((MainForm)Parent).Text = $"{((Button)sender).Text} Clicked";
}
}

Can't hide labels and text boxes when button is checked

When salaried button is checked, change label "Hour Pay: " to "Salary" and hide the labels and text boxes below it. When Hourly button is checked, return everything to its initial form.
My main issue is when i execute code it does not hide labels and text boxes.
private void addButton_Click(object sender, EventArgs e)
{
}
private void icontype_CheckedChanged(object sender, EventArgs e)
{
if (salariedRadioButton.Checked == true)
{
hourLabel.Visible = false;
}
else if (hourlyRadioButton.Checked == true)
{
hourLabel.Visible = true;
}
}
private void hourTextBox_TextChanged(object sender, EventArgs e)
{
try
{
weekTextBox.Text =(float.Parse(hourTextBox.Text)40).ToString();
}
catch
{
}
try
{
yearTextBox.Text = (float.Parse(weekTextBox.Text) 52).ToString();
}
catch
{
}
}
I'm not sure what's your expected result, for the hide part, I think you can try this, thanks.
public Form1()
{
InitializeComponent();
this.salariedRadioButton.CheckedChanged += icontype_CheckedChanged;
this.hourlyRadioButton.CheckedChanged += icontype_CheckedChanged;
}
private void icontype_CheckedChanged(object sender, EventArgs e)
{
if (salariedRadioButton.Checked == true)
{
hourLabel.Visible = false;
weekTextBox.Visible = false;
}
else if(hourlyRadioButton.Checked == true)
{
hourLabel.Visible = true;
weekTextBox.Visible = true;
}
}

Custom ToggleButton doesn't respond to ManipulationCompleted event

I have a custom UI element that inherits from System.Windows.Controls.Primitives.ToggleButton. I'm also routing my mouse events through a custom TouchDevice that raises touch events instead.
For some reason, the ManipulationCompleted event never fires. First, the custom TouchDevice can be found here: http://blakenui.codeplex.com/SourceControl/changeset/view/67526#Blake.NUI.WPF/Touch/MouseTouchDevice.cs
Here are the relevant parts of my class:
public class ToggleSwitch: ToggleButton {
private Grid _root;
private readonly IList<int> _activeTouchDevices;
private const double UNCHECKED_TRANSLATION = 0;
private TranslateTransform _backgroundTranslation;
private TranslateTransform _thumbTranslation;
private Grid _root;
private Grid _track;
private FrameworkElement _thumb;
private double _checkedTranslation;
private double _dragTranslation;
private bool _wasDragged;
private bool _isDragging;
public override void OnApplyTemplate()
{
...
MouseTouchDevice.RegisterEvents(_root);
_root.IsManipulationEnabled = true;
_root.TouchDown += OnTouchDown;
_root.TouchUp += OnTouchUp;
_root.GotTouchCapture += OnGotTouchCapture;
_root.LostTouchCapture += OnLostTouchCapture;
_root.ManipulationStarted += OnManipulationStarted;
_root.ManipulationDelta += OnManipulationDelta;
_root.ManipulationCompleted += OnManipulationCompleted;
}
private void OnTouchDown(object sender, TouchEventArgs e)
{
e.TouchDevice.Capture(_root);
}
private void OnGotTouchCapture(object sender, TouchEventArgs e)
{
if (e.TouchDevice.Captured == _root)
{
Manipulation.AddManipulator(_root,e.TouchDevice);
_activeTouchDevices.Add(e.TouchDevice.Id);
}
}
private void OnManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
e.Handled = true;
_isDragging = true;
_dragTranslation = Translation;
ChangeVisualState(true);
Translation = _dragTranslation;
}
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
e.Handled = true;
var horizontalChange = e.DeltaManipulation.Translation.X;
var direction = Math.Abs(horizontalChange) >= Math.Abs(e.DeltaManipulation.Translation.Y) ? Orientation.Horizontal : Orientation.Vertical;
if (direction == Orientation.Horizontal && horizontalChange != 0.0)
{
_wasDragged = true;
_dragTranslation += horizontalChange;
Translation = Math.Max(UNCHECKED_TRANSLATION, Math.Min(_checkedTranslation, _dragTranslation));
}
}
private void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
e.Handled = true;
_isDragging = false;
var click = false;
if (_wasDragged)
{
var edge = (IsChecked ?? false) ? _checkedTranslation : UNCHECKED_TRANSLATION;
if (Translation != edge)
{
click = true;
}
}
else
{
click = true;
}
if (click)
{
OnClick();
}
_wasDragged = false;
}
}
The OnManipulationCompleted method is never entered.

How can the initiator determine that the drag has ended?

Let's assume the following Situation:
a Control (e.g. a Button) has an attached behavior to enable a Drag&Drop-Operation
<Button Content="test">
<i:Interaction.Behaviors>
<SimpleDragBehavior/>
</i:Interaction.Behaviors>
</Button>
And the SimpleDragBehavior
public class SimpleDragBehavior: Behavior<Button>
{
protected override void OnAttached ()
{
AssociatedObject.MouseLeftButtonDown += OnAssociatedObjectMouseLeftButtonDown;
AssociatedObject.MouseLeftButtonUp += OnAssociatedObjectMouseLeftButtonUp;
AssociatedObject.MouseMove += OnAssociatedObjectMouseMove;
mouseIsDown = false;
}
private bool mouseIsDown;
private void OnAssociatedObjectMouseMove (object sender, MouseEventArgs e)
{
if (mouseIsDown)
{
AssociatedObject.Background = new SolidColorBrush(Colors.Red);
DragDrop.DoDragDrop((DependencyObject)sender,
AssociatedObject.Content,
DragDropEffects.Link);
}
}
private void OnAssociatedObjectMouseLeftButtonUp (object sender, MouseButtonEventArgs e)
{
mouseIsDown = false;
}
private void OnAssociatedObjectMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
{
mouseIsDown = true;
}
}
The task now is to determine when the drag ends, to restore the orignal backgound of the button.
This is no problem when droped on an drop-target. But how do i recognize a drop on something which isn't a drop-target? In the worst case: outside the window?
DragDrop.DoDragDrop returns after drag-and-drop operation is completed.
Yes, "Initiates a drag-and-drop operation" is confusing, since it could be read as "start drag-and-drop and return":
private void OnAssociatedObjectMouseMove (object sender, MouseEventArgs e)
{
if (mouseIsDown)
{
AssociatedObject.Background = new SolidColorBrush(Colors.Red);
var effects = DragDrop.DoDragDrop((DependencyObject)sender,
AssociatedObject.Content,
DragDropEffects.Link);
// this line will be executed, when drag/drop will complete:
AssociatedObject.Background = //restore color here;
if (effects == DragDropEffects.None)
{
// nothing was dragged
}
else
{
// inspect operation result here
}
}
}

Categories

Resources