Custom ToggleButton doesn't respond to ManipulationCompleted event - c#

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.

Related

A new message box appears with each second after it appears

I ran into an issue where i tried displaying a display box after a countdown reached a certain time but for some odd reason it replicates with each second despite it having already passed the initial time it was supposed to appear. This is what i tried to do but now the timer has stopped and the time remaining column has stopped runng.
public partial class Form1 : Form
{
private List<CSession> sessionlist = new List<CSession>();
private TimeSpan workingTimeSpan = new TimeSpan();
private TimeSpan fiveMinutes = new TimeSpan(0,1,0);
private TimeSpan oneSecond = new TimeSpan(0,0,1);
public Form1()
{
InitializeComponent();
timer1.Enabled = true;
timer1.Start();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void AddTime_Click(object sender, EventArgs e)
{
workingTimeSpan += fiveMinutes;
DisplayWorkingTimeSpan();
}
private void DisplayWorkingTimeSpan()
{
TimerLabel.Text = workingTimeSpan.ToString();
}
private void DecreaseTime_Click(object sender, EventArgs e)
{
TimeSpan fiveMinutes = new TimeSpan(0,5,0);
workingTimeSpan -= fiveMinutes;
DisplayWorkingTimeSpan();
}
private void Confirm_Click(object sender, EventArgs e)
{
CSession newSession = new CSession();
if(PasswordText.Text == "")
{
MessageBox.Show("Password not entered");
return;
}
newSession.password = PasswordText.Text;
newSession.purchased_time = workingTimeSpan;
newSession.remaining_time = workingTimeSpan;
newSession.status = "Online";
sessionlist.Add(newSession);
PasswordText.Text = "";
TimerLabel.Text = "";
workingTimeSpan = new TimeSpan();
}
private void DisplayAllSessions()
{
listView1.Items.Clear();
foreach(CSession c in sessionlist)
{
string[] row = { c.password, c.purchased_time.ToString(), c.remaining_time.ToString(), c.status };
ListViewItem i = new ListViewItem(row);
listView1.Items.Add(i);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
foreach(CSession c in sessionlist)
{
if (c.remaining_time.TotalMinutes == 5)
{
timer1.Stop();
MessageBox.Show("Time almost up for client.");
}
if (c.remaining_time.TotalSeconds < 1)
{
c.status = "Offline";
}
if(c.status == "Online")
{
c.remaining_time -= oneSecond;
}
}
DisplayAllSessions();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
}
Add a flag that gets toggled when you display the message, so you won't display it again:
private bool MessageDisplayed = false;
private void timer1_Tick(object sender, EventArgs e)
{
foreach(CSession c in sessionlist)
{
if (c.remaining_time.TotalMinutes == 5 && !MessageDisplayed) // <-- check the flag
{
MessageDisplayed = true;
MessageBox.Show("Time almost up for client.");
}
if (c.remaining_time.TotalSeconds < 1)
{
c.status = "Offline";
}
if(c.status == "Online")
{
c.remaining_time -= oneSecond;
}
}
DisplayAllSessions();
}
Now you can leave the timer running and your message will only appear once.

How to paint owner drawn ListViewItem differently when hovered?

Below is the code. I saw that the MouseMove event is not always triggered, especially when the mouse is moved very fast, and because of this two items can be marked hovered at the same time, so I now use a variable to hold the last hovered item, but the problem is that there are too many redraws. I also saw that DrawListViewItemEventArgs.State property is just ShowKeyboardCues when it should also contain Hot.
private void Form1_Load(object sender, EventArgs e)
{
listView1.OwnerDraw = true;
listView1.View = View.LargeIcon;
listView1.DrawItem += ListView1_DrawItem;
listView1.MouseMove += ListView1_MouseMove;
for (int i = 1; i <= 6; ++i)
{
listView1.Items.Add($"item {i}", 0);
}
}
private void ListView1_MouseMove(object sender, MouseEventArgs e)
{
ListViewItem item = listView1.GetItemAt(e.X, e.Y);
if (item != null)
{
if (LastHoveredItem != null && LastHoveredItem.Index == item.Index)
{
return;
}
listView1.RedrawItems(item.Index, item.Index, false);
}
}
internal ListViewItem LastHoveredItem = null;
private void ListView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
bool hot = e.Item.Bounds.Contains(listView1.PointToClient(Cursor.Position));
if (LastHoveredItem != null)
{
listView1.RedrawItems(LastHoveredItem.Index, LastHoveredItem.Index, false);
}
if (hot)
{
LastHoveredItem = e.Item;
e.Graphics.FillRectangle(Brushes.Green, e.Bounds);
}
else
{
LastHoveredItem = null;
}
e.DrawText();
}
Here are a few new methods and the existing methods are updated:
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
listView1.OwnerDraw = true;
listView1.View = View.LargeIcon;
listView1.DrawItem += ListView1_DrawItem;
listView1.MouseEnter += ListView1_MouseEnter;
listView1.MouseMove += ListView1_MouseMove;
listView1.MouseLeave += ListView1_MouseLeave;
for (int i = 1; i <= 10; ++i)
{
listView1.Items.Add($"item {i}", 0);
}
}
private void ListView1_MouseEnter(object sender, EventArgs e)
{
CheckHoveredAndInvalidate();
}
private void ListView1_MouseLeave(object sender, EventArgs e)
{
RemoveHoveredAndInvalidate();
}
internal static Rectangle GetEntireItemBounds(ListViewItem it)
{
return it.GetBounds(ItemBoundsPortion.Entire);
}
internal ListViewItem GetEntireItemAtCursorPosition()
{
Point p = listView1.PointToClient(Cursor.Position);
foreach (ListViewItem it in listView1.Items)
{
if (GetEntireItemBounds(it).Contains(p))
{
return it;
}
}
return null;
}
private void ListView1_MouseMove(object sender, MouseEventArgs e)
{
CheckHoveredAndInvalidate();
}
private void CheckHoveredAndInvalidate()
{
ListViewItem item = GetEntireItemAtCursorPosition();
if (item == null)
{
RemoveHoveredAndInvalidate();
}
else if (item != null)
{
if (LastHoveredItem != null)
{
if (LastHoveredItem != item)
{
ListViewItem item2 = LastHoveredItem;
LastHoveredItem = item;
listView1.Invalidate(GetEntireItemBounds(item2));
listView1.Invalidate(GetEntireItemBounds(item));
}
else if (LastHoveredItem == item)
{
}
}
else if (LastHoveredItem == null)
{
LastHoveredItem = item;
listView1.Invalidate(GetEntireItemBounds(item));
}
}
}
private void RemoveHoveredAndInvalidate()
{
if (LastHoveredItem != null)
{
ListViewItem item2 = LastHoveredItem;
LastHoveredItem = null;
listView1.Invalidate(GetEntireItemBounds(item2));
}
else if (LastHoveredItem == null)
{
}
}
private void ListView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (LastHoveredItem == e.Item)
{
e.Graphics.FillRectangle(Brushes.Yellow, e.Item.Bounds);
}
else
{
e.Graphics.FillRectangle(Brushes.Green, e.Item.Bounds);
}
}
The text drawing part has been omitted.

ArgumentOutOfRangeException on an object listview when hitting tab

I have an object list view that has two text columns. When I edit the left column and hit tab to go to the right I receive a "ArgumentOutOfRangeException" with an index of -1. Looks like the index is something internal to the list view because I debugged my application and found no errors. Here is the code :
public partial class SummaryOverviewSettingsDlg : Form
{
private List<SummaryDataset> _localSummaryDatasets = new List<SummaryDataset>();
private bool _includeLimits;
private SummaryOverviewSettings _summaryOverviewSettings;
public bool IncludeLimits { get { return _includeLimits; } }
public SummaryOverviewSettingsDlg(SummaryOverviewSettings summaryOverviewSettings)
{
InitializeComponent();
if (summaryOverviewSettings.Datasets != null)
{
_localSummaryDatasets.AddRange(summaryOverviewSettings.Datasets);
}
_summaryOverviewSettings = summaryOverviewSettings;
}
private void DataFilesListDlg_Load(object sender, EventArgs e)
{
foreach(var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
LimitsCheckbox.Checked = _summaryOverviewSettings.ShowLimits;
}
private void OlvFilePaths_CellRightClick(object sender, CellRightClickEventArgs e)
{
var contextMenuSymbol = new ContextMenuStrip();
ToolStripItem item;
item = contextMenuSymbol.Items.Add("Add sample");
item.Click += ContextMenuAddFilePath;
if (e.Model != null)
{
contextMenuSymbol.Items.Add("-");
item = contextMenuSymbol.Items.Add("Delete sample");
item.Click += ContextMenuDeleteFilePath;
}
olvFilePaths.ContextMenuStrip = contextMenuSymbol;
}
private void ContextMenuAddFilePath(object sender, EventArgs e)
{
var item = new SummaryDataset()
{
SampleName = "Sample",
Path = "Path"
};
_localSummaryDatasets.Add(item);
// Rebuild the list in the GUI
olvFilePaths.ClearObjects();
foreach (var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
olvFilePaths.AutoResizeColumns();
}
private void ContextMenuDeleteFilePath(object sender, EventArgs e)
{
if (olvFilePaths.SelectedObject != null)
{
var item = (SummaryDataset)olvFilePaths.SelectedObject;
olvFilePaths.RemoveObject(item);
_localSummaryDatasets.Remove(item);
}
}
private void OlvFilePaths_CellEditFinished(object sender, CellEditEventArgs e)
{
if (e.Control is TextBox textBox)
{
var oldValue = (string)e.Value;
var newValue = (string)e.NewValue;
var col = e.Column.AspectName;
var index = e.ListViewItem.Index;
if (newValue != oldValue)
{
if (col == "SampleName")
{
_localSummaryDatasets[index].SampleName = newValue;
}
else
{
_localSummaryDatasets[index].Path = newValue;
}
}
}
// Rebuild the list in the GUI
olvFilePaths.ClearObjects();
foreach (var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
olvFilePaths.AutoResizeColumns();
}
private void OkButton_Click(object sender, EventArgs e)
{
_summaryOverviewSettings.Datasets.Clear();
_summaryOverviewSettings.Datasets.AddRange(_localSummaryDatasets);
_summaryOverviewSettings.ShowLimits = _includeLimits;
DialogResult = DialogResult.OK;
Close();
}
private void ButtonCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void LimitsCheckbox_CheckedChanged(object sender, EventArgs e)
{
_includeLimits = LimitsCheckbox.Checked;
}
}

var labels = this.Controls.OfType<Label>(); Control all labels except 1

I have a maze game that uses labels as walls and .IntersectsWith to handle colision. My problem is that since my "player" is also a label it messes things up with my code.
What i want is for the player to be able to move meanwhile it also cant colide with all the other labels.
What the problem really is is that this part makes the player unable to move for some reason. Never mind the if () with break it was just an experiment.
var labels = this.Controls.OfType<Label>();
foreach (var label in labels)
{
if (label.Bounds.IntersectsWith(player.Bounds))
{
break;
}
if (player.Bounds.IntersectsWith(label.Bounds))
{
namespace mazeGame
{
public partial class Form1 : Form
{
bool down;
bool left;
bool right;
bool up;
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Right)
{
right = true;
left = false;
up = false;
down = false;
}
if (e.KeyCode == Keys.Left)
{
left = true;
right = false;
up = false;
down = false;
}
if (e.KeyCode == Keys.Up)
{
up = true;
left = false;
right = false;
down = false;
}
if (e.KeyCode == Keys.Down)
{
down = true;
left = false;
up = false;
right = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
var labels = this.Controls.OfType<Label>();
foreach (var label in labels)
{
if (label.Bounds.IntersectsWith(player.Bounds))
{
break;
}
if (player.Bounds.IntersectsWith(label.Bounds))
{
if (right == true)
{
right = false;
left = true;
}
else if (left == true)
{
left = false;
right = true;
}
else if (up == true)
{
up = false;
down = true;
}
else if (down == true)
{
down = false;
up = true;
}
}
if (right == true)
{
player.Left += 1;
}
if (left == true)
{
player.Left -= 1;
}
if (up == true)
{
player.Top -= 1;
}
if (down == true)
{
player.Top += 1;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
}
}
}
You can exclude your player label from your selection in this manner:
var labels = this.Controls.OfType<Label>().Where(l => l.Name != "Player")

How to select ListViewItem with left mouse button only?

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)

Categories

Resources