RichTextBox loses focus and caret position after modifying ListBox item - winforms

I am trying to update Listbox Item text when modifying the RichTextBox, but the RTB keeps on losing focus, i even tried doing it in a BackgroundWorker, but still the same, here is my code:
private void RTB_TextChanged(object sender, EventArgs e)
{
if (ListBOX.SelectedIndex > -1)
{
if (!ListChangeBW.IsBusy)
{
ListChangeBW.RunWorkerAsync();
}
}
}
private void ListChangeBW_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
if (!InvokeRequired)
{
ListB.Items[ListB.SelectedIndex] = RTB.Text;
}
else
{
Invoke(new Action(() =>
{
ListB.Items[ListB.SelectedIndex] = RTB.Text;
}));
}
}
I know the last part of my code doesn't match up because i send the arguments but i thought it would just confuse you, the changement does work, it's just that the RTB loses focus, and if i make it gain focus, the caret is in the last position of the text, is there anyway to make the changement background or in another thread ?

Related

ListView jumping back to drag location when attempting to set keyboard focus

I've added standard drag drop functionality to a Listview. I've also added code to detect when I'm near the top or bottom in order to scroll when dragging.
Unfortunately, if I grab an unselected item, drag up/ down and scroll, on Drop the ListView will jump back to the vertical offset that it started with when drag began.
I've attempted to reset the vertical offset, but so far I am unable to prevent this jump. Is there a way to prevent this behavior?
EDIT:
This code is executed before the drag (used to give focus to a textbox). Although, if I prevent this code from being called then the jump will not occur.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var element = Keyboard.FocusedElement;
if (element is ListViewItem)
{
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate ()
{
(element as ListViewItem).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}));
}
}
The issue may be with the way I begin my drag operation. Given the invocation.
protected void BeginDrag(object sender, MouseEventArgs e, Action<object> action)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point position = e.GetPosition(this);
this.ListView.ReleaseMouseCapture();
if ((Math.Abs(position.X - _startPoint.X) > 10
|| Math.Abs(position.Y - _startPoint.Y) > 10)
&& !IsDragging)
{
try
{
IsDragging = true;
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal,
new System.Threading.ParameterizedThreadStart(action),
e);
}
catch (InvalidOperationException) { }
finally
{
IsDragging = false;
}
}
}
}
There was an issue if the BeginInvoke in SelectionChanged was called before the Invoke in the Drop. For one reason or another the BeginInvoke did not finish before the Invoke was called.
The Solution to this issue was to simply assure that the BeginInvoke for setting the focus was not called until AFTER the Invoke.
A fix is to attach the code to a different event, in this case I used MouseUp.
private void ListView_MouseUp(object sender, MouseButtonEventArgs e)
{
var element = Keyboard.FocusedElement;
if (element is ListViewItem)
{
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new Action(delegate ()
{
(element as ListViewItem).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}));
}
}
Not 100% perfect. When I drag and drop, the event for mouse up is not called. This means that the focus will not be set when I drag and drop; which may or may not be an issue.

SelectionForeColor not working for link cells in DataGridViewLinkColumn of DataGridView

In my Winform 4.5 app, I have a DataGridView with first column as a link column. I would like to have the link color of the selected link cell to be white. Since by default the background color of a selected row (or a cell) is blue and the ForeColor of all the links are also blue, when user selects a row (or a link cell) the text of the link is not readable. I tried writing the following code but it does not change the link color of selected link cell at all.
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
foreach (DataGridViewLinkCell cell in ((DataGridView)sender).SelectedCells)
{
if (cell.ColumnIndex == 0)
{
if (cell.Selected)
{
cell.Style = new DataGridViewCellStyle()
{
SelectionForeColor = SystemColors.HighlightText
};
}
}
}
}
I then modified the above code as follows. But it changes the link color of all the links to white - that makes non-selected link cells to be not readable since the backcolor of those links is also white:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
foreach (DataGridViewLinkCell cell in ((DataGridView)sender).SelectedCells)
{
if (cell.ColumnIndex == 0)
{
if (cell.Selected)
{
cell.LinkColor = SystemColors.HighlightText;
}
}
}
}
I tested both the codes by setting a breakpoint inside the foreach loop and selecting a link cell. I noticed that the code does go through exactly one iteration of the foreach loop correctly. Moreover, I have made no change to the default settings of the DataGridViewLinkColumn
Edit
By default the DataGridView looks like this on a row selection. Notice that the cell in the second column changes its ForeColor to white but not the cell in the first column:
I want it to looks like this on a row selection:
Edit The CellLeave event will always occur when an attempt is made to navigate away from a cell.
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
foreach (DataGridViewLinkCell cell in
((DataGridView) sender).SelectedCells.OfType<DataGridViewLinkCell>())
{
if (cell.Selected)
{
cell.LinkColor = SystemColors.HighlightText;
}
}
}
private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e)
{
foreach (DataGridViewLinkCell cell in
((DataGridView) sender).Rows[e.RowIndex].Cells.OfType<DataGridViewLinkCell>())
{
cell.LinkColor = cell.LinkVisited ? Color.Purple : Color.Blue;
}
}
I've experienced the same issue and I got it working using the CellFormatting event. Below you'll find a generic solution for this:
void grd_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
SetGridLinkColor(sender as DataGridView, e.RowIndex, e.ColumnIndex, Color.White);
}
public static void SetGridLinkColor(DataGridView grd, int rowIndex, int columnIndex, Color selectedColor)
{
if (grd == null || !(grd.Columns[columnIndex] is DataGridViewLinkColumn))
return;
if (grd.Rows[rowIndex].Selected)
{
((DataGridViewLinkCell)grd.Rows[rowIndex].Cells[columnIndex]).LinkColor = selectedColor;
((DataGridViewLinkColumn)grd.Columns[columnIndex]).VisitedLinkColor = selectedColor;
}
else
{
Color color = ((DataGridViewLinkColumn)grd.Columns[columnIndex]).LinkColor;
((DataGridViewLinkCell)grd.Rows[rowIndex].Cells[columnIndex]).LinkColor = color;
((DataGridViewLinkColumn)grd.Columns[columnIndex]).VisitedLinkColor = color;
}
}

Can I make a WPF ListBox change selection on left button press only

I have a WPF ListBox operating in single selection mode. I am adding drag and drop to move items around. Currently the ListBox selection responds to both left button pressed and then with mouse moves with left button down. So after I wait for the MinimumVerticalDragDistance to start a drag operation, a different item could be selected. Dragging either the unselected orginal item or dragging the new selected item is confusing. Adding 'e.Handled=true' in xxx_MouseMove or xxx_PreviewMouseMove does not do anything. Any ideas on suppressing this selection due to mouse moves with left button down?
The best kludge I came up with is to cancel the ListBox's "Selection by dragging" in the IsMouseCapturedChanged event.
public partial class MainWindow : Window
{
Rect? dragSourceGestureRect;
bool busy;
public MainWindow()
{
InitializeComponent();
listBox.ItemsSource = Enumerable.Range(1, 9);
listBox.PreviewMouseLeftButtonDown += listBox_PreviewMouseLeftButtonDown;
listBox.IsMouseCapturedChanged += listBox_IsMouseCapturedChanged;
listBox.MouseMove += listBox_MouseMove;
}
void listBox_IsMouseCapturedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (busy)
return;
if (!listBox.IsMouseCaptured)
dragSourceGestureRect = null;
else if (dragSourceGestureRect.HasValue)
{
busy = true;
{
//tell the ListBox to cancel it's "Selection by dragging"
listBox.ReleaseMouseCapture();
//Now recapture the mouse for canceling my dragging
listBox.CaptureMouse();
}
busy = false;
}
}
void listBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var center = e.GetPosition(listBox);
dragSourceGestureRect = new Rect(
center.X - SystemParameters.MinimumHorizontalDragDistance / 2,
center.Y - SystemParameters.MinimumVerticalDragDistance / 2,
SystemParameters.MinimumHorizontalDragDistance,
SystemParameters.MinimumVerticalDragDistance);
}
void listBox_MouseMove(object sender, MouseEventArgs e)
{
if (!dragSourceGestureRect.HasValue || dragSourceGestureRect.Value.Contains(e.GetPosition(listBox)))
return;
dragSourceGestureRect = null;
var data = new DataObject(DataFormats.UnicodeText, "The Data");
DragDrop.DoDragDrop(listBox, data, DragDropEffects.Copy);
e.Handled = true;
}
}

Get RowIndex via ContextMenu?

I'm trying to get a rowindex of row at which I right clicked to call a contextmenu.
DatagridView's property contextmenu is set to this contextmenu.
Is it possible in some simple way?
Best regards
Yes, you need to handle the MouseDown event for your DataGridView and then use the HitTest method to return row and/or column index for the given coordinates.
For example:
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
DataGridView.HitTestInfo hit = dataGridView1.HitTest(e.X, e.Y);
if (hit.Type == DataGridViewHitTestType.Cell)
{
Console.WriteLine(hit.RowIndex);
}
}
}
I change the selection in the CellContextMenuStripNeeded event and then use the SelectedRows member to find it.
private void dataGridView_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
{
var Dgv = sender as DataGridView;
if (Dgv != null)
{
// Change the selection to reflect the right-click
Dgv.ClearSelection();
Dgv.Rows[e.RowIndex].Selected = true;
}
}
private void myToolStripMenuItem_Click(object sender, EventArgs e)
{
// Now pick up the selection as we know this is the row we right-clicked on
if (dataGridView.SelectedRows.Count > 0)
{
DoSomethingAmazing(dataGridView.SelectedRows[0]);
}
}
This also has the desired effect of highlighting a row that you r-click on.

WinForms: How to customize a tooltip when it is about to be shown?

i want to have a tooltip for each item in a treeview, and each item in a listview, and different for each subitem (i.e. column) in the listview.
i can determine the text i want to show (using hit testing with the current mouse position, etc):
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (e.AssociatedControl == listView1)
{
toolTip1.SetToolTip(listView1, "foo");
}
}
but any attempt to set the tooltip text causes a stackoverflow.
How can i customize the tooltip (icon, title, text) just before it appears?
You need to guard your code in the Popup event handler so that if you are calling SetToolTip from within it, you don't call SetToolTip again.
Something like:
private bool updatingTooltip;
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (!this.updatingTooltip && (e.AssociatedControl == listView1))
{
this.updatingTooltip = true;
toolTip1.SetToolTip(listView1, "foo");
this.updatingTooltip = false;
}
}

Resources