I am attempting to highlight text in a databound ListBox and highlight matching strings exactly like the email application on Windows Phone 7.
The search button pulls up a Popup, and on the TextChanged event, I'm filtering from a master list and re-setting the DataContext:
private void txtSearch_TextChanged(object sender, TextChangedEventArgs e)
{
results = allContent.Where(
x => x.Content.Contains(txtSearch.Text)
).ToList();
DataContext = results;
}
That part works great. The problem is with highlighting the matched text. I've tried iterating over the ListBoxItems in various events (Loaded, ItemsChanged) but they're always empty.
Any ideas about how text highlighting might be done in a databound ListItem's child TextBox?
Here is the solution that I went with:
private void ResultsText_Loaded(object sender, RoutedEventArgs e)
{
var textBlock = sender as TextBlock;
if (txtSearch.Text.Length > 0 && textBlock.Text.Length > 0)
{
BoldText(ref textBlock, txtSearch.Text, Color.FromArgb(255, 254, 247, 71));
}
}
public static void BoldText(ref TextBlock tb, string partToBold, Color color)
{
string Text = tb.Text;
tb.Inlines.Clear();
Run r = new Run();
r.Text = Text.Substring(0, Text.IndexOf(partToBold));
tb.Inlines.Add(r);
r = new Run();
r.Text = partToBold;
r.FontWeight = FontWeights.Bold;
r.Foreground = new SolidColorBrush(color);
tb.Inlines.Add(r);
r = new Run();
r.Text = Text.Substring(Text.IndexOf(partToBold) + partToBold.Length, Text.Length - (Text.IndexOf(partToBold) + partToBold.Length));
tb.Inlines.Add(r);
}
Related
WPF Datagrid it is adding new row while pressing enter on first cell I want it to add new row once I press last cell of Datagrid.
Please check demo app is here:
WPFDemo
Thank you,
Jitendra Jadav
The DataGrid also adds a new row even when you don't even press ENTER. If you don't want this behaviour, you would probably be better of setting the CanUserAddRows property to false and add the items yourself to the source collection. Something like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
dataGrid.CanUserAddRows = false;
//add blank row
var itemsSource = dataGrid.ItemsSource as ObservableCollection<ItemModel>;
if (itemsSource != null)
itemsSource.Add(new ItemModel());
Loaded += MainWindow_Loaded;
dataGrid.PreviewKeyDown += DataGrid_PreviewKeyDown;
}
private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
if (Keyboard.FocusedElement is UIElement elementWithFocus)
{
if (dataGrid.Columns.Count - 1 == dataGrid.CurrentCell.Column.DisplayIndex)
{
var itemsSource = dataGrid.ItemsSource as ObservableCollection<ItemModel>;
if (itemsSource != null)
{
var newItem = new ItemModel();
itemsSource.Add(newItem);
dataGrid.SelectedItem = newItem;
Dispatcher.BeginInvoke(new Action(()=>
{
DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromItem(newItem) as DataGridRow;
DataGridCell cell = Helper.GetCell(dataGrid, row, 0);
if (cell != null)
dataGrid.CurrentCell = new DataGridCellInfo(cell);
}), DispatcherPriority.Background);
}
}
else
{
elementWithFocus.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
}
}
}
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Helper.SelectRowByIndex(dataGrid, 0);
}
}
Does anyone know how to get the button visible all the time?.(Not only in edit mode of the cell)
I would like to take your attention to the answer of this question.
how to add ellipse button and textbox in current cell of datagridview in winforms
I could enhance this solution to see the button control in the cell for all the time. What I want is to get the popup box for the first click of the cell. This is the code to paint the button in uneditted mode.
// Finally paint the NumericUpDown control
Rectangle srcRect = new Rectangle(0, 0, valBounds.Width, valBounds.Height);
if (srcRect.Width > 0 && srcRect.Height > 0)
{
Bitmap renderingBitmap = new Bitmap(22, 18);
new TextButton().button.DrawToBitmap(renderingBitmap, srcRect);
graphics.DrawImage(renderingBitmap, new Rectangle(new Point(cellBounds.X+cellBounds.Width-24, valBounds.Location.Y-2), valBounds.Size),
srcRect, GraphicsUnit.Pixel);
}
A better option would be to embed a button on your DataGridView. This would give you more control over the use of DataGridView. See the following snippet:
Button b1 = new Button();
int cRow = 0, cCol = 0;
private void Form1_Load(object sender, EventArgs e)
{
b1.Text = "...";
b1.Visible = false;
this.dataGridView1.Controls.Add(b1);
b1.BringToFront();
this.dataGridView1.Paint += new PaintEventHandler(dataGridView1_Paint);
this.b1.Click += new EventHandler(b1_Click);
}
void b1_Click(object sender, EventArgs e)
{
//Implement your logic here
}
void dataGridView1_Paint(object sender, PaintEventArgs e)
{
if(cRow != 0 && cCol != 0)
{
Rectangle rect = new Rectangle();
rect = dataGridView1.GetCellDisplayRectangle(cRow ,cCol , false);
rect.X = rect.X + (2*dataGridView1.Columns[1].Width / 3);
rect.Width = dataGridView1.Columns[1].Width / 3;
b1.Bounds = rect;
b1.Visible = true;
}
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
cRow = e.RowIndex;
cCol = e.ColumnIndex;
}
In the above snippet the location of ellipses button is set to last clicked cell. The visibility is always true after a cell is clicked. In my opinion this would provide a far better control over the button's function,and is easier to maintain.
How to make a custom date time picker control using mask edit textbox in C# WinForm. By default it should display the text 'MM/DD/YYYY' and after clicking on it, it should show the mask edit TextBox.
you can show a example by using below code snippet
in the designer.cs add following lines
this.maskedTextBox1.Location = new System.Drawing.Point(29, 251);
this.maskedTextBox1.Mask = "00/00/0000";
this.maskedTextBox1.Name = "maskedTextBox1";
this.maskedTextBox1.Size = new System.Drawing.Size(100, 20);
this.maskedTextBox1.TabIndex = 10;
this.maskedTextBox1.Text = "31071770";
this.maskedTextBox1.TextMaskFormat = System.Windows.Forms.MaskFormat.ExcludePromptAndLiterals;
this.maskedTextBox1.Enter += new System.EventHandler(this.maskedTextBox1_Enter);
this.maskedTextBox1.Leave += new System.EventHandler(this.maskedTextBox1_Leave);
and in the code.cs add these functions
private void maskedTextBox1_Enter(object sender, EventArgs e)
{
if (maskedTextBox1.Text == "31071770")
{
maskedTextBox1.Text = "";
}
}
private void maskedTextBox1_Leave(object sender, EventArgs e)
{
if (maskedTextBox1.Text == "")
{
maskedTextBox1.Text = "31071770";
}
}
o/p
31/07/1970
Note : At last you have to validate that date
TextBox has a default context menu. I would like to add an item to it. OK, that means cloning the default one, and adding an extra item to that.
I'd like to reuse some code here. I have five textboxes. Each needs the additional item on its context menu. The item needs act on the textbox that was clicked. I know "copy and paste" is the recommended method of code reuse in WPF, but if possible I'd prefer not to define five menus in XAML and five commands in the code behind.
Is there any reasonably clean and quick way to do this in WPF?
public partial class MyGhastlyView
{
/* blah blah */
private void MenuCut_Click(object sender, RoutedEventArgs e)
{
try
{
(sender as MenuItem).GetPlacementTarget<TextBox>().Cut();
}
catch (Exception)
{
}
}
/* blah blah */
}
public static class FurshlugginerExtensions
{
public static bool TryGetPlacementTarget<TTargetType>(this MenuItem mi,
out TTargetType target) where TTargetType : class
{
target = null;
var cm = mi.GetContextMenu();
if (null != cm)
{
target = cm.PlacementTarget as TTargetType;
}
return null != target;
}
public static TTargetType GetPlacementTarget<TTargetType>(this MenuItem mi)
where TTargetType : class
{
var cm = mi.GetContextMenu();
return (cm == null)
? null
: cm.PlacementTarget as TTargetType;
}
public static ContextMenu GetContextMenu(this MenuItem mi)
{
var logicalParent = LogicalTreeHelper.GetParent(mi);
if (logicalParent is ContextMenu)
{
return logicalParent as ContextMenu;
}
else if (logicalParent is MenuItem)
{
return (logicalParent as MenuItem).GetContextMenu();
}
return null;
}
}
UPDATE
What I'm looking for turns out to be a RoutedUICommand, with some futzing around in XAML. It knows what you clicked on (with some Kafkaesque exceptions due to event bubbling -- but can just set the CommandParameter on the ContextMenu).
Unfortunately, ContextMenuOpening event will not work here. For whatever reason, TextBox does not expose its context menu, and is always null unless you set it with your own. Perhaps it simply pops a private menu on right mouse click.
Charles Petzold speaks about that with RichTextBox here. (Both TextBox and RichTextBox derive from TextBoxBase, which appears to define that behavior)
It seems you will have to create your own, and duplicate the existing items.
Several articles demonstrate exactly this, like the one here.
Hope this helps.
EDIT:
However if you insist on editing the current menu, it appears someone has done so here (using an extension method and reflection).
After further investigation of the above attempt, it seems that the author is creating an instance of an EditorContextMenu (private class which derives from ContextMenu in System.Windows.Documents) and assigning it to the TextBox ContextMenu property, then adding the parameter menu items to the newly created menu. In effect, overriding the current menu. While you do get the original implementation, I am not sure I would favor this solution.
EDIT 2:
The following code will create only one instance of custom menu, bind Ctrl-D to the textboxes, along with the correlating ContextMenu item.
public static RoutedCommand ItemActionCommand = new RoutedCommand();
public MainWindow()
{
InitializeComponent();
CommandBinding commandBinding = new CommandBinding(ItemActionCommand, new ExecutedRoutedEventHandler(ItemActionCommandEventHandler));
KeyBinding keyBinding = new KeyBinding(ItemActionCommand, new KeyGesture(Key.D, ModifierKeys.Control));
MenuItem item = new MenuItem();
item.Click += CustomContextMenuItem_Click; // not really necessary
item.Header = "Custom Menu Item";
item.InputGestureText = "Ctrl+D";
item.Command = ItemActionCommand;
ContextMenu menu = new ContextMenu();
menu.Items.Add(item);
Grid container = new Grid();
this.Content = container;
for (int i = 0; i < 5; i++)
container.Children.Add(this.CreateTextBox("Value: " + i.ToString(), (i + 1) * 30.0d, menu, commandBinding, keyBinding));
}
private void ItemActionCommandEventHandler(object sender, ExecutedRoutedEventArgs e)
{
TextBox textBox = e.Source as TextBox;
Debug.Assert(textBox != null);
// perform actions against textbox here
}
private void CustomContextMenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem item = sender as MenuItem;
Debug.Assert(item != null);
TextBox textBox = ((ContextMenu)item.Parent).PlacementTarget as TextBox;
Debug.Assert(textBox != null);
// no need to do anything here since the command handler above will fire
// but for the sake of completeness
}
private TextBox CreateTextBox(string text, double topOffset, ContextMenu menu, CommandBinding commandBinding, KeyBinding keyBinding)
{
TextBox textbox = new TextBox();
textbox.HorizontalAlignment = HorizontalAlignment.Center;
textbox.VerticalAlignment = VerticalAlignment.Top;
textbox.Margin = new Thickness(0.0d, topOffset, 0.0d, 0.0d);
textbox.CommandBindings.Add(commandBinding);
textbox.InputBindings.Add(keyBinding);
textbox.ContextMenu = menu;
textbox.Width = 150.0d;
textbox.Height = 25.0d;
textbox.Text = text;
return textbox;
}
Screenshot:
It is possible with an AttachedProperty and the handling of the ContextMenuOpening event. Look here and here. Should take around 100 lines of code and one line in xaml.
For completenes sake:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication1
{
public class CustomMenuAction
{
public static bool GetHasMenuItemAction(DependencyObject obj)
{
return (bool)obj.GetValue(HasMenuItemActionProperty);
}
public static void SetHasMenuItemAction(DependencyObject obj, bool value)
{
obj.SetValue(HasMenuItemActionProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasMenuItemActionProperty =
DependencyProperty.RegisterAttached("HasMenuItemAction", typeof(bool), typeof(CustomMenuAction), new PropertyMetadata(default(bool),OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if((bool)e.NewValue)
{
var textbox = d as TextBox;
if(textbox != null)
{
textbox.ContextMenu = GetCustomContextMenu();
textbox.ContextMenuOpening += textbox_ContextMenuOpening;
}
}
}
private static ContextMenu GetCustomContextMenu()
{
var contextMenu = new ContextMenu();
var standardCommands = GetStandardCommands();
foreach (var item in standardCommands)
{
contextMenu.Items.Add(item);
}
return contextMenu;
}
private static IList<MenuItem> GetStandardCommands()
{
//From https://stackoverflow.com/a/210981/3411327
List<MenuItem> standardCommands = new List<MenuItem>();
MenuItem item = new MenuItem();
item.Command = ApplicationCommands.Cut;
standardCommands.Add(item);
item = new MenuItem();
item.Command = ApplicationCommands.Copy;
standardCommands.Add(item);
item = new MenuItem();
item.Command = ApplicationCommands.Paste;
standardCommands.Add(item);
return standardCommands;
}
static void textbox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
//From MSDN example: http://msdn.microsoft.com/en-us/library/bb613568.aspx
var textbox = e.Source as TextBox;
ContextMenu cm = textbox.ContextMenu;
foreach (MenuItem mi in cm.Items)
{
if ((String)mi.Header == "Item4") return;
}
MenuItem mi4 = new MenuItem();
mi4.Header = "Item4";
mi4.Click += (o, args) =>
{
var menuItem = o as MenuItem;
MessageBox.Show(menuItem.Header.ToString(), textbox.Text);
};
textbox.ContextMenu.Items.Add(mi4);
}
}
}
<TextBox namespace:CustomMenuAction.HasMenuItemAction="True"></TextBox>
How can set MaxLength for TreeNode Name and text property? This is a windows forms application, where user right clicks a treeview to add a node and the maxlength of the treenode name should be 40 chars. Currently I check this in AfterlabelEdit event, and throw a message if no. of chars exceeds. But the requiremnet says to limit the length without showing the message box as we do in textboxes.
Thanks.
You could display a text box over the treeview and set the MaxLength on that.
One way to do that is create a text box with the form:
private TextBox _TextBox;
public Form1()
{
InitializeComponent();
_TextBox = new TextBox();
_TextBox.Visible = false;
_TextBox.LostFocus += new EventHandler(_TextBox_LostFocus);
_TextBox.Validating += new CancelEventHandler(_TextBox_Validating);
this.Controls.Add(_TextBox);
}
private void _TextBox_LostFocus(object sender, EventArgs e)
{
_TextBox.Visible = false;
}
private void _TextBox_Validating(object sender, CancelEventArgs e)
{
treeView1.SelectedNode.Text = _TextBox.Text;
}
Then in the tree view BeforeLabelEdit set the MaxLength of the text box and show it over the currently selected Node:
private void treeView1_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
_TextBox.MaxLength = 10;
e.CancelEdit = true;
TreeNode selectedNode = treeView1.SelectedNode;
_TextBox.Visible = true;
_TextBox.Text = selectedNode.Text;
_TextBox.SelectAll();
_TextBox.BringToFront();
_TextBox.Left = treeView1.Left + selectedNode.Bounds.Left;
_TextBox.Top = treeView1.Top + selectedNode.Bounds.Top;
_TextBox.Focus();
}
You'll probably want to add some additional functionality to the text box so it sizes correctly based on the width of the tree view and also so it accepts the new text on the user hitting return, etc.