How To Detect If WPF ComboBox Is In EditMode - wpf

I'm writing a .NET 4.5 C# / WPF app where I must specifically detect if a ComboBox is in edit mode. In other words, I need to know only if the mouse cursor is active and blinking in the editable portion of the ComboBox, and the ComboBox is ready for input from the user's typing on the keyboard.
I've tried "TextBoxBase.GotFocus", "TextBoxBase.GotKeyboardFocus", etc.
These events fire even when a user simply clicks on the ComboBox, which doesn't make any sense (why does a "GotKeyboardFocus" event fire even when I haven't even touched the keyboard?).
Anyways, how can I detect ONLY when the mouse cursor is active and blinking in the editable portion of the ComboBox, and the ComboBox is ready for input from the user's typing on the keyboard?

Got it...
void cmbMyComboBox_Loaded(object sender, RoutedEventArgs e)
{
var obj = (ComboBox)sender;
if (obj != null)
{
var t = (TextBox)obj.Template.FindName("PART_EditableTextBox", obj);
if (t != null)
{
t.MaxLength = 16;
t.GotFocus += (s, a) => { MyFunction(); } };
t.LostFocus += (s, a) => { MyOtherFunction(); } };
}
}
}

Related

Detect when caret position changes in RichTextBox

I am trying to implement very simple text formatting functionality for a RichTextBox in WPF. This just consists of a few bold, italic, etc ToggleButtons just above the RichTextBox. See image below, but ignore the top TextBox - the RichTextBox is the bigger one at the bottom.
Toggling formatting for either a selection or at the caret position (for text that will be typed in) is not a problem, as I'm doing this:
private void BoldButton_Checked(object sender, RoutedEventArgs e)
{
this.SetSelectionBold(true);
}
private void BoldButton_Unchecked(object sender, RoutedEventArgs e)
{
this.SetSelectionBold(false);
}
private void SetSelectionBold(bool isBold)
{
var selection = this.RichText.Selection;
if (selection != null)
{
selection.ApplyPropertyValue(TextElement.FontWeightProperty, isBold ? FontWeights.Bold : FontWeights.Normal);
}
}
However, if the user moves the caret somewhere else (e.g. from bold text to normal text) then I'd like the ToggleButtons to reflect that state, in much the same way as it works in Word. Is it possible to detect when the caret position changes, and take action accordingly?
Hook yourself into SelectionChanged event and get current caret position, and test if the property exists on that selection?
In the event, probably you want something like:
var selection = richTextBox.Selection;
if(selection != null)
{
if(selection.GetPropertyValue(TextElement.FontWeightProperty) == FontWeights.Bold)
// todo; enable your button
}
If that event is not triggered by caret positioning(the document doesn't say anything about that),
you probably need to inherit from RichTextBox and override OnSelectionChanged, after that you need to actually generate your own Caret, eg:
var currentCaretPlusOne = new TextRange(richTextBox.CaretPosition,
richTextBox.CaretPosition+1);
if(currentCaretPlusOne != null)
{
if(currentCaretPlusOne.GetPropertyValue(TextElement.FontWeightProperty)
== FontWeights.Bold)
// todo; enable your button
}

how to assign hot key for datagridview combobox column?

i have grid view have combobox column ["column2"]
if (keyData == (Keys.F11))
{
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
{
//here i want to change index automatically using hot key (keyboard short cut )
}
return true;
You can't directly assign ShortcutKey to a individual cell. Handle the KeyPress event of DataGridView for key combinations. In the event handler, put the below code
void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyData == (Keys.Alt | Keys.E))
{
dataGridView1.Rows[RowIndex].Cells[ColumnIndex].Selected = true;
dataGridView1.CurrentCell = dataGridView1.Rows[RowIndex].Cells[ColumnIndex];
dataGridView1.BeginEdit(false);
}
}
if any cell is already in edit mode then the editing control would get the KeyPress events not the DataGridView. If you want to overcome that you have to subclass the existing DataGridView control and override its ProcessCmdKey function. See the answer to this SO question for that.
To change the selected index of the editing combo, subscribe the EditingControlShowing event and in the event handler change the combobox index.
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox control = e.Control as ComboBox;
if (control !=null)
{
// set the selected index of the combo here.
}
}

WPF ListBox SelectionChanged sporadically not triggered on tablet

I'm trying to use a WPF listbox on a tablet.
In a dummy project I just made a listbox with a lot of items and when i select one this item will be showed in a textblock.
I have a selectionchanged event on the listBox
On my laptop everything works the way it should but when i run it on a tablet the selectionchanged event isn't triggered sporadically. On the screen the old selected item stays selected and the new selected one is highlighted but the item isn't shown in the textblock.
With remote debugging I have seen that the TouchDown, TouchMove and TouchUp event are all triggered, but some times the selectionChanged isn't triggered.
these things I've tried as well:
setting in Xaml inside the listbox:
ScrollViewer.PanningMode="None"
When I do this the selectionchanged event is always triggered but the user can't scroll down anymore with swiping (Which must be possible.
I think here lies the problem somewhere, but I don't have any solution yet.
Help Needed.
After a long time a solution for this problem was found.
first of we need some variables
private TouchPoint _movePoint;
private double _minimum = 0;
private double _maximum;
Me need to catch the TouchMove event of the listBox. This event triggers many times. We need get maximum and minimum Y-values of were the touch has been.
private void myListBox_TouchMove(object sender, TouchEventArgs e)
{
_movePoint := e.GetTouchPoint(myListBox);
if (_minimum.Equals(0))
{
_minimum := _movePoint.Position.Y;
_maximum := _movePoint.Position.Y;
return;
}
if (_movePoint.Position.Y < _minimum)
_minimum := _movePoint.Position.Y;
if (_movePoint.Position.Y > _maximum)
_maximum := _movePoint.Position.Y;
}
Now in the TouchUp event we look at the how far have been slided in the vertical direction. If this is not to big (in this example lower then 20), we gonna look at where the touchup event took place and look for the ListBoxItem that is on that place and set IsSelected=ture on this item.
private void myListBox_TouchUp(object sender, TouchEventArgs e)
{
var difference = _maximum - _minimum;
_maximum = 0;
_minimum=0;
if(difference < 20)
{
var touchPosition = e.GetTouchPoint(myListBox)
UIElement elem = myListBox.InputHitTest(touchPosition.Position) as UIElement;
while (elem != null)
{
if (elem == myListBox)
return;
ListBoxItem item = elem as ListBoxItem;
if (item != null)
{
item.IsSelected = true;
return;
}
elem = VisualTreeHelper.GetParent(elem) as UIElement;
}
}
}
This should work.

Releasing mouse capture and letting mouse click pass through

I have a control that is similar to a Popup or Menu. I want to display it and when the user clicks outside the bounds of the box, have it hide itself. I've used Mouse.Capture(this, CaptureMode.SubTree) as well as re-acquired the capture the same way Menu/Popup do in OnLostMouseCapture.
When the user clicks outside the bounds of the control, I release the mouse capture in OnPreviewMouseDown. I don't set e.Handled to true. The mouse click will make it to other controls on the main UI, but not to the close button (Red X) for the window. It requires 2 clicks to close the app.
Is there a way to tell WPF to restart the mouse click, or to send a repeated mouse click event?
Here's my code. Note I renamed it to MainMenuControl - I'm not building a Menu, so Menu/MenuItem and Popup aren't options.
public class MainMenuControl : Control
{
static MainMenuControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MainMenuControl), new FrameworkPropertyMetadata(typeof(MainMenuControl)));
}
public MainMenuControl()
{
this.Loaded += new RoutedEventHandler(MainMenuControl_Loaded);
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnPreviewMouseDownOutsideCapturedElementHandler);
}
void MainMenuControl_Loaded(object sender, RoutedEventArgs e)
{
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(MainMenuControl_IsVisibleChanged);
}
void MainMenuControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.IsVisible)
{
Mouse.Capture(this, CaptureMode.SubTree);
Debug.WriteLine("Mouse.Capture");
}
}
// I was doing this in OnPreviewMouseDown, but changing to this didn't have any effect
private void OnPreviewMouseDownOutsideCapturedElementHandler(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("OnPreviewMouseDownOutsideCapturedElementHandler");
if (!this.IsMouseInBounds())
{
if (Mouse.Captured == this)
{
Mouse.Capture(this, CaptureMode.None);
Debug.WriteLine("Mouse.Capture released");
}
Debug.WriteLine("Close Menu");
}
}
protected override void OnLostMouseCapture(MouseEventArgs e)
{
base.OnLostMouseCapture(e);
Debug.WriteLine("OnLostMouseCapture");
MainMenuControl reference = e.Source as MainMenuControl;
if (Mouse.Captured != reference)
{
if (e.OriginalSource == reference)
{
if ((Mouse.Captured == null) || (!reference.IsAncestorOf(Mouse.Captured as DependencyObject)))
{
//TODO: Close
Debug.WriteLine("Close Menu");
}
}
// if a child caused use to lose the capture, then recapture.
else if (reference.IsAncestorOf(e.OriginalSource as DependencyObject))
{
if (Mouse.Captured == null)
{
Mouse.Capture(reference, CaptureMode.SubTree);
Debug.WriteLine("Mouse.Capture");
e.Handled = true;
}
}
else
{
//TODO: Close
Debug.WriteLine("Close Menu");
}
}
}
private bool IsMouseInBounds()
{
Point point = Mouse.GetPosition(this);
Rect bounds = new Rect(0, 0, this.Width, this.Height);
return bounds.Contains(point);
}
}
The problem is that the mouse handling you are talking about is outside the WPF eventing system and part of the operating system so we're really talking about two fairly different mouse message queues that interact well enough most of the time but in these edge case we see that the interoperability is not perfect.
You could try to generate Win32 mouse messages or send your own window a close message but all those approaches are hacks. Since popups and menus exhibit exactly the same symptoms you describe, it doesn't seem like there is going to be an easy to way to accomplish what you want as you've described it.
Instead, I suggest that you consider giving up the mouse capture when the mouse leaves the north client area of the window or some other heuristic such as a specified distance from the control. I know this is probably not ideal but it might be a satisfactory compromise if you want the close button to work badly enough.

WinForms ListBox with readonly/disabled items

Is there a way to make some of the items in a ListBox readonly/disabled so they can't be selected? Or are there any similar controls to ListBox to provide this functionality?
ListBox doesn't have support for that. You can bolt something on, you could deselect a selected item. Here's a silly example that prevents even-numbered items from being selected:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e) {
for (int ix = listBox1.SelectedIndices.Count - 1; ix >= 0; ix--) {
if (listBox1.SelectedIndices[ix] % 2 != 0)
listBox1.SelectedIndices.Remove(listBox1.SelectedIndices[ix]);
}
}
But the flicker is quite noticeable and it messes up keyboard navigation. You can get better results by using CheckedListBox, you can prevent the user from checking the box for an item:
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
if (e.Index % 2 != 0) e.NewValue = CheckState.Unchecked;
}
But now you cannot override drawing to make it look obvious to the user that the item isn't selectable. No great solutions here, it is far simpler to just not display items in the box that shouldn't be selectable.
#Hans solution causing that the item id selected for a short time and then selection disappearing. I don't like that - this can be confusing for the enduser.
I prefer to hide some edit option buttons for the item that should be disabled:
if (lbSystemUsers.Items.Count > 0 && lbSystemUsers.SelectedIndices.Count > 0)
if (((RemoteSystemUserListEntity)lbSystemUsers.SelectedItem).Value == appLogin)
{
bSystemUsersDelete.Visible = false;
bSystemUsersEdit.Visible = false;
}
else
{
bSystemUsersDelete.Visible = true;
bSystemUsersEdit.Visible = true;
}
Here is the list that lists the users and disallow to edit user that is actually logged in to the edit panel.
ListBox doesn't have a ReadOnly (or similar) property, but you can make a custom ListBox control. Here's a solution that worked pretty well for me:
https://ajeethtechnotes.blogspot.com/2009/02/readonly-listbox.html
public class ReadOnlyListBox : ListBox
{
private bool _readOnly = false;
public bool ReadOnly
{
get { return _readOnly; }
set { _readOnly = value; }
}
protected override void DefWndProc(ref Message m)
{
// If ReadOnly is set to true, then block any messages
// to the selection area from the mouse or keyboard.
// Let all other messages pass through to the
// Windows default implementation of DefWndProc.
if (!_readOnly || ((m.Msg <= 0x0200 || m.Msg >= 0x020E)
&& (m.Msg <= 0x0100 || m.Msg >= 0x0109)
&& m.Msg != 0x2111
&& m.Msg != 0x87))
{
base.DefWndProc(ref m);
}
}
}
I know this is old thread, but i'll post a workaround for other readers in future.
listBox.Enabled = false;
listBox.BackColor = Color.LightGray;
This will change background color of list box to Light Gray. So this is not builtin "native way" to do it, but at least gives user some feedback that he is not supposed to / can't edit that field.
To get read-only behaviour I have MyCBLLocked, a boolean associated with the MyCBL checkbox list control, and on the CheckItem event I do:
private void MyCBL_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (MyCBLLocked)
e.NewValue = e.CurrentValue;
}
So instead of
MyCBL.Enabled = false;
I use
MyCBLLocked = true;
and the user can scroll through the many selections but not mess things up with changes.

Resources