UWP goBack to cached page closes app - c#

I have a RootPage as root view. In my RootPage I've added this to support the back button:
SystemNavigationManager.GetForCurrentView().BackRequested += SystemNavigationManager_BackRequested;
private void SystemNavigationManager_BackRequested(object sender, BackRequestedEventArgs e) {
bool handled = e.Handled;
if (this.AppFrame == null)
return;
if (this.AppFrame.CanGoBack && !handled) {
// If not, set the event to handled and go back to the previous page in the app.
handled = true;
this.AppFrame.GoBack();
}
e.Handled = handled;
}
This works as expected (the user goes to the previous page if there is one). But when I enable caching for one page it doesn't work anymore:
this.NavigationCacheMode = NavigationCacheMode.Enabled;
I've also tried Required but that gives the same result.
When I press the back button now it just closes the app instead of going back. The code for going back is executed though, so I don't know what I'm doing wrong.
Update
I've located the exception. I have a custom TextBox which fills the Text with a currency symbol and a space. This is essentially what creates the exception:
this.Loaded += (sender, args) => {
Text = "$ ";
};
Note: it is not the currency symbol that breaks the app, but the fact that I set the Text in Loaded when returning to this Page (page cache should handle this now, so that's conflicting I guess)
Update 2
Code sample to reproduce problem. Custom TextBox:
public class RegexTextBox : TextBox {
private string regex = "";
private string previousText = "";
private int previousSelectionStart = 0;
public RegexTextBox() {
this.Loaded += (sender, args) => {
switch(RegexType) {
case RegexTextBoxTypes.Number:
regex = "^([0-9]+|)$";
break;
case RegexTextBoxTypes.Currency:
regex = "^€ ([0-9]+|)$";
Text = "€ ";
break;
}
this.TextChanging += input_TextChanging;
this.SelectionChanged += input_SelectionChanged;
previousText = Text;
previousSelectionStart = SelectionStart;
};
}
private void input_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args) {
if (regex.Length > 0) {
if (!Regex.IsMatch(Text, regex)) {
Text = previousText;
SelectionStart = previousSelectionStart;
}
previousText = Text;
}
}
private void input_SelectionChanged(object sender, RoutedEventArgs e) {
previousSelectionStart = SelectionStart;
}
public bool isEmpty() {
if(RegexType == RegexTextBoxTypes.Currency) {
return !Regex.IsMatch(Text, "^€ ([0-9]+)$");
}
return Text.Length == 0;
}
public void ClearText() {
if(RegexType == RegexTextBoxTypes.Currency) {
Text = "€ ";
} else {
Text = "";
}
}
public string GetPlainText() {
if (RegexType == RegexTextBoxTypes.Currency) {
return Text.Substring(2, Text.Length - 2);
}
return Text;
}
public RegexTextBoxTypes RegexType {
get { return (RegexTextBoxTypes)GetValue(RegexTypeProperty); }
set { SetValue(RegexTypeProperty, value); }
}
public static readonly DependencyProperty RegexTypeProperty =
DependencyProperty.Register("RegexType", typeof(RegexTextBoxTypes), typeof(RegexTextBox), new PropertyMetadata(RegexTextBoxTypes.Normal));
}
public enum RegexTextBoxTypes {
Normal,
Number,
Currency
}
In your xaml put this in your grid (controls namespace points to the location of the custom TextBox):
<controls:RegexTextBox
InputScope="Number"
RegexType="Currency"/>
Then in the constructor of the page put this:
this.NavigationCacheMode = NavigationCacheMode.Required;
Make sure the custom TextBox is filled with some numbers. Then navigate to a new page and back, when navigating back it should close the app due to an uncaught exception (though visual studio won't show there has been an exception).

Related

How to create numeric Textbox Custom Control with dependency Property in WPF?

I want create a custom control for Numeric Text box with dependency property in WPF , in my solution , I add one WPF application and custom control (WPF) ,then in public class , I create dependency property ....
Now I don't know how can i write my rule for text box and which event is true?
Another question : What is my rule for numeric text box , that this text box must be give number and . and Separating .this custom Text box is for accounting system.
public static readonly DependencyProperty NumberTextbox;
static Numeric()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Numeric), new FrameworkPropertyMetadata(typeof(Numeric)));
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata("Enter Your Text", OnKeyDown);
NumberTextbox =DependencyProperty.Register("Text", typeof(TextBox), typeof(FrameworkElement), metadata);
}
public string NumberTXT
{
get { return (string)GetValue(NumberTextbox); }
set { SetValue(NumberTextbox, value); }
}
I recommend to you add another Dependency Property in example code below I named it Value
Also format your number by comma or NumberFormatInfo.CurrentInfo.NumberDecimalSeparator
and control caret location changes by two property SelectionLength and SelectionStart.
Finally for more detail and complete code WPF Maskable Number Entry TextBox
region Value property
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(NumericTextBox), new PropertyMetadata(new Double(), OnValueChanged));
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
//var numericBoxControl = (NumericTextBox)sender;
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); Text = value.ToString("###,###,###"); }
}
endregion
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
var txt = e.Text.Replace(",", "");
e.Handled = !IsTextAllowed(txt);
if (IsTextAllowed(txt))
{
if (Text.Length == 3)
{
Text = Text.Insert(1,",");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.Key == Key.Back)
{
if (Text.Length == 5)
{
Text = Text.Replace(",", "");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
var txt = Text.Replace(",", "");
SetValue(ValueProperty, txt.Length==0?0:double.Parse(txt));
base.OnTextChanged(e);
}
private static bool IsTextAllowed(string text)
{
try
{
double.Parse(text);
return true;
}
catch (FormatException)
{
return false;
}
}
I don't understand your question exactly and why you need dependency proerties to make a numeric text box custom control. What you can do is to inherit from textbox and handle the PreviewTextInput, like it is solved in this question by Ray:
then you get:
public class NumericTextBox : TextBox
{
public NumericTextBox()
{
PreviewTextInput += NumericTextBox_PreviewTextInput;
}
void NumericTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !isTextAllowed(e.Text);
}
private static bool isTextAllowed(string text)
{
var regex = new Regex("[^0-9]+");
return !regex.IsMatch(text);
}
}
And you can use it like that:
<myNameSpace:NumericTextBox />
And now you can add any other validation you want.
I would also implement a solution for the pasting issue, something like (see also in the link):
private void textBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
var text = (String)e.DataObject.GetData(typeof(String));
if (!isTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
Good job, but let me do the following task with a UserControl in C #:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace NumericBox
{
public partial class NumericBox : TextBox
{
public NumericBox
{
this.TextAlign = HorizontalAlignment.Right;
this.Text = "0,00";
this.KeyPress += NumericBox_KeyPress;
}
public double NumericResult
{
get{
double d = Convert.ToDouble(this.Text.Replace('.', ','));
return d;
}
}
private void NumericBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
e.Handled = true;
if ((e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1))
e.Handled = true;
if (e.KeyChar == 13)
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
}
}

How to cancel tab change in WPF TabControl

I have found multiple questions about this problem on SO, however I still can't quite get a realiable solution. Here is what I came up with after reading the answers.
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="300" Width="300" x:Name="this">
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Tabs, ElementName=this}" x:Name="TabControl"/>
</Window>
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var tabs = new ObservableCollection<string> {"Tab1", "Tab2", "Tab3"};
Tabs = CollectionViewSource.GetDefaultView(tabs);
Tabs.CurrentChanging += OnCurrentChanging;
Tabs.CurrentChanged += OnCurrentChanged;
Tabs.MoveCurrentToFirst();
CurrentTab = tabs.First();
}
private void OnCurrentChanging(object sender, CurrentChangingEventArgs e)
{
//only show message box when tab is changed by user input
if (!_cancelTabChange)
{
if (MessageBox.Show("Change tab?", "Message", MessageBoxButton.YesNo) == MessageBoxResult.No)
{
_cancelTabChange = true;
return;
}
}
_cancelTabChange = false;
}
private void OnCurrentChanged(object sender, EventArgs e)
{
if (!_cancelTabChange)
{
//Update current tab property, if user did not cancel transition
CurrentTab = (string)Tabs.CurrentItem;
}
else
{
//navigate back to current tab otherwise
Dispatcher.BeginInvoke(new Action(() => Tabs.MoveCurrentTo(CurrentTab)));
}
}
public string CurrentTab { get; set; }
public static readonly DependencyProperty TabsProperty = DependencyProperty.Register("Tabs", typeof(ICollectionView), typeof(MainWindow), new FrameworkPropertyMetadata(default(ICollectionView)));
public ICollectionView Tabs
{
get { return (ICollectionView)GetValue(TabsProperty); }
set { SetValue(TabsProperty, value); }
}
private bool _cancelTabChange;
}
Basically I want to display a confirmation message, when user navigates to different tab, and if he clicks "no" - abort the transition. This code does not work though. If you click multiple times on "Tab2", each time choosing "no" in message box, at some point it stops working: events stop triggering. Event will trigger again if you click on "Tab3", but if you choose "yes" it opens second tab and not third. I am having trouble figuring out wtf is going on. :)
Does anyone see a bug in my solution? Or is there an easier way to display a confirmation message, when user switches tabs? I am also willing to use any opensource tab control, which does have a proper SelectionChanging event. I could not find any though.
I am using .Net 4.0.
Edit:
If I comment the message box out:
private void OnCurrentChanging(object sender, CurrentChangingEventArgs e)
{
//only show message box when tab is changed by user input
if (!_cancelTabChange)
{
//if (MessageBox.Show("Change tab?", "Message", MessageBoxButton.YesNo) == MessageBoxResult.No)
//{
Debug.WriteLine("Canceled");
_cancelTabChange = true;
return;
//}
}
_cancelTabChange = false;
}
Everything works fine. Weird.
This solution http://coderelief.net/2011/11/07/fixing-issynchronizedwithcurrentitem-and-icollectionview-cancel-bug-with-an-attached-property/
seems to work quite well with
<TabControl ... yournamespace:SelectorAttachedProperties.IsSynchronizedWithCurrentItemFixEnabled="True" .../>
private void OnCurrentChanging(object sender, CurrentChangingEventArgs e)
{
if (MessageBox.Show("Change tab?", "Message", MessageBoxButton.YesNo) == MessageBoxResult.No)
{
e.Cancel = true;
}
}
public static class SelectorAttachedProperties
{
private static Type _ownerType = typeof(SelectorAttachedProperties);
#region IsSynchronizedWithCurrentItemFixEnabled
public static readonly DependencyProperty IsSynchronizedWithCurrentItemFixEnabledProperty =
DependencyProperty.RegisterAttached("IsSynchronizedWithCurrentItemFixEnabled", typeof(bool), _ownerType,
new PropertyMetadata(false, OnIsSynchronizedWithCurrentItemFixEnabledChanged));
public static bool GetIsSynchronizedWithCurrentItemFixEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsSynchronizedWithCurrentItemFixEnabledProperty);
}
public static void SetIsSynchronizedWithCurrentItemFixEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsSynchronizedWithCurrentItemFixEnabledProperty, value);
}
private static void OnIsSynchronizedWithCurrentItemFixEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Selector selector = d as Selector;
if (selector == null || !(e.OldValue is bool && e.NewValue is bool) || e.OldValue == e.NewValue)
return;
bool enforceCurrentItemSync = (bool)e.NewValue;
ICollectionView collectionView = null;
EventHandler itemsSourceChangedHandler = null;
itemsSourceChangedHandler = delegate
{
collectionView = selector.ItemsSource as ICollectionView;
if (collectionView == null)
collectionView = CollectionViewSource.GetDefaultView(selector);
};
SelectionChangedEventHandler selectionChangedHanlder = null;
selectionChangedHanlder = delegate
{
if (collectionView == null)
return;
if (selector.IsSynchronizedWithCurrentItem == true && selector.SelectedItem != collectionView.CurrentItem)
{
selector.IsSynchronizedWithCurrentItem = false;
selector.SelectedItem = collectionView.CurrentItem;
selector.IsSynchronizedWithCurrentItem = true;
}
};
if (enforceCurrentItemSync)
{
TypeDescriptor.GetProperties(selector)["ItemsSource"].AddValueChanged(selector, itemsSourceChangedHandler);
selector.SelectionChanged += selectionChangedHanlder;
}
else
{
TypeDescriptor.GetProperties(selector)["ItemsSource"].RemoveValueChanged(selector, itemsSourceChangedHandler);
selector.SelectionChanged -= selectionChangedHanlder;
}
}
#endregion IsSynchronizedWithCurrentItemFixEnabled
}
For some reason adding TabControl.Focus() fixes things:
private void OnCurrentChanged(object sender, EventArgs e)
{
if (!_cancelTabChange)
{
//Update current tab property, if user did not cancel transition
CurrentTab = (string)Tabs.CurrentItem;
}
else
{
//navigate back to current tab otherwise
Dispatcher.BeginInvoke(new Action(() =>
{
Tabs.MoveCurrentTo(CurrentTab);
TabControl.Focus();
}));
}
}
I still have no clue what on Earth is going on here. So I will gladly accept the answer, which sheds some light on this issue.
inside the tabControl_SelectionChanged event handler:
if (e.OriginalSource == tabControl) //if this event fired from your tabControl
{
e.Handled = true;
if (!forbiddenPage.IsSelected) //User leaving the tab
{
if (forbiddenTest())
{
forbiddenPage.IsSelected = true;
MessageBox.Show("you must not leave this page");
}
}
Note that setting forbiddenPage.IsSelected = true causes a loop and you reenter
this event handler. This time, however, we exit because the page selected IS the forbidden page.
private void MainTabControl_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ReasonBecauseLeaveTabItemIsForbidden)
{
if (MainTabControl.SelectedIndex == IndexOfTabItem)
{
MessageBox.Show(SomeMessageWhyLeaveTabItemIsForbidden);
}
MainTabControl.SelectedIndex = IndexOfTabItem;
}
}
IndexOfTabItem - index of TabItem that disabled for leaving.
He who must be obeyed requested that the application ask the user if they wish to leave the page so here is the slightly changed code:
private Object _selectedTab;
public Object SelectedTab
{
get
{
return _selectedTab;
}
set
{
if (
!(_selectedTab is ADR_Scanner.ViewModel.ConfigurationViewModel) ||
!_configurationViewModel.HasChanged ||
(System.Windows.Forms.MessageBox.Show("Are you sure you want to leave this page without saving the configuration changes", ADR_Scanner.App.Current.MainWindow.Title, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Error) == System.Windows.Forms.DialogResult.Yes)
)
{
_selectedTab = value;
}
OnPropertyChanged("SelectedTab");
}
}
I think this small change does pretty much what you wanted.
There is a much easier solution. Add a binding to the selected item in the XAML:
<TabControl SelectedItem="{Binding SelectedTab}" ...
Then in the view model:
private Object _selectedTab;
public Object SelectedTab
{
get
{
return _selectedTab;
}
set
{
if (_selectedTab is ADR_Scanner.ViewModel.ConfigurationViewModel && _configurationViewModel.HasChanged)
{
System.Windows.Forms.MessageBox.Show("Please save the configuration changes", ADR_Scanner.App.ResourceAssembly.GetName().Name, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
else
{
_selectedTab = value;
}
OnPropertyChanged("SelectedTab");
}
}
Obviously you replace ADR_Scanner.ViewModel.ConfigurationViewModel with your own view model class. Lastly make sure you initialise _selectedTab in your constructor otherwise the TabControl will have no initial selection.

Capture user click and typing in a custom WPF TextBox-like control?

Trying to make an equation editor like that in Microsoft Word in C# and WPF. XML cannot be used; it has to be purely programmatic.
Right now I have LineGUIObject : System.Windows.Controls.WrapPanel, which is like System.Windows.Controls.TextBox, except that instead of just showing strings it shows each element of a List<System.Windows.UIElement> in order.
Now I want for a user to be able to click on an instance of LineGUIObject and type into it. The holdup is that I don't know how to capture the user's click or read the input that they type. How can this be done?
Note: This question is not asking how to handle input once captured; just how to get the input in the first place. For example, is there some event that fires off after the user clicks it or something? I can't seem to find one for System.Windows.Controls.WrapPanel, which might imply that I need to use another type of object, or..?
Current code:
public class LineGUIObject
: System.Windows.Controls.WrapPanel
{
private List<System.Windows.UIElement> _uiElementList;
private CursorGUIObject _cursor;
private int? _cursorIndex;
public LineGUIObject(System.Windows.Threading.Dispatcher dispatcher)
: base()
{
this.UIElementList = new List<System.Windows.UIElement>();
this.Cursor = new CursorGUIObject(dispatcher, 25, 1.5, 250);
this.UIElementList.Add(this.Cursor);
this.AddText("[junk string just to see this otherwise invisible object while debugging]");
}
protected void InterpretUserKeyStroke(/* ??? */)
{
//How do we get this method to be called on user input,
//e.g. when the user types "1"?
throw new NotImplementedException();
}
protected void AddText(string text)
{
this.UIElementList.Add(new System.Windows.Controls.TextBlock(new System.Windows.Documents.Run(text)));
this.UpdateDisplay();
}
protected List<System.Windows.UIElement> UIElementList { get { return this._uiElementList; } private set { this._uiElementList = value; } }
protected CursorGUIObject Cursor { get { return this._cursor; } private set { this._cursor = value; } }
protected int? CursorIndex
{
get { return this._cursorIndex; }
set
{
int? nullablePriorIndex = this.CursorIndex;
if (nullablePriorIndex != null)
{
int priorIndex = nullablePriorIndex.Value;
this.UIElementList.RemoveAt(priorIndex);
}
if (value == null)
{
this._cursorIndex = null;
}
else
{
int newIndex = value.Value;
if (newIndex < 0)
{
newIndex = 0;
}
else
{
int thisListCount = this.UIElementList.Count;
if (newIndex > thisListCount) { newIndex = thisListCount; }
}
this.UIElementList.Insert(newIndex, this.Cursor);
this._cursorIndex = newIndex;
}
this.UpdateDisplay();
}
}
protected void UpdateDisplay()
{
this.Children.Clear();
foreach (System.Windows.UIElement uiElement in this.UIElementList) { this.Children.Add(uiElement); }
}
}
public class CursorGUIObject
: System.Windows.Controls.WrapPanel
{
public const double MINIMUM_BLINK_TIME_IN_MS = 5;
public const double MINIMUM_HEIGHT = 0.5;
public const double MINIMUM_WIDTH = 0.5;
private object ToggleVisibilityLock = new object();
private delegate void TimerIntervalDelegate();
private System.Windows.Shapes.Rectangle _rectangle;
private System.Timers.Timer _timer;
private System.Windows.Threading.Dispatcher _dispatcher;
public CursorGUIObject(System.Windows.Threading.Dispatcher dispatcher, double height, double width, double blinkTimeInMS)
{
this.Dispatcher = dispatcher;
System.Windows.Shapes.Rectangle rectangle = new System.Windows.Shapes.Rectangle();
rectangle.Width = width > MINIMUM_WIDTH ? width : MINIMUM_WIDTH;
rectangle.Height = height > MINIMUM_HEIGHT ? height : MINIMUM_HEIGHT;
rectangle.Fill = System.Windows.Media.Brushes.Black;
this.Rectangle = rectangle;
this.Children.Add(rectangle);
System.Timers.Timer timer = new System.Timers.Timer(blinkTimeInMS > MINIMUM_BLINK_TIME_IN_MS ? blinkTimeInMS : MINIMUM_BLINK_TIME_IN_MS);
this.Timer = timer;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
~CursorGUIObject()
{
System.Timers.Timer timer = this.Timer;
if (timer != null) { timer.Dispose(); }
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Delegate timerDelegate = new TimerIntervalDelegate(ToggleVisibility);
this.Dispatcher.BeginInvoke(timerDelegate);
}
protected void ToggleVisibility()
{
lock (ToggleVisibilityLock)
{
if (this.Rectangle.Visibility.Equals(System.Windows.Visibility.Hidden))
{
this.Rectangle.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Rectangle.Visibility = System.Windows.Visibility.Hidden;
}
}
}
protected System.Windows.Shapes.Rectangle Rectangle { get { return this._rectangle; } private set { this._rectangle = value; } }
protected System.Timers.Timer Timer { get { return this._timer; } private set { this._timer = value; } }
protected System.Windows.Threading.Dispatcher Dispatcher { get { return this._dispatcher; } private set { this._dispatcher = value; } }
}
Pretty much all WPF controls provide access to the UIElement.PreviewMouseDown Event, which you can use to monitor mouse clicks. So, this event lets you monitor when each object is clicked on. Next, I'd advise you to use a small Popup control to popup a TextBox that the user could enter a value with:
<Popup Name="Popup">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="5">
<TextBox Text="{Binding InputText}" />
</Border>
</Popup>
Depending on how you have set up your project, you could open the Popup from the event handler:
private void YourObject_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Popup.IsOpen = true;
}
Turns out that LineGUIObject just needed to have this.Focusable = true; set in its constructor so that it could receive the keyboard's focus when clicked.
Now that it can be focused on, this.KeyUp += LineGUIObject_KeyUp; also in the constructor, and
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
this.AddText(e.Key.ToString());
}
Even this had a problem at first since my LineGUIObject was nested in a ScrollViewer which kept stealing focus immediately after the LineGUIObject would receive it. This was fixed by making the ScrollViewer to be unable to get focus, i.e. <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Focusable="False"/>.

Implement jQuery Tag-it functionality in C# Win Form

I would like to implement a functionality similar to jQuery TagIt into C# windows forms.
Here is the link for jQuery - http://aehlke.github.com/tag-it/
I am thinking of creating a user control for this functionality.
Any help on how to go about this?
You're in luck, as there's already a container control called a FlowLayoutPanel that will get you 90% of the way there as a flowing tag host.
FlowLayoutPanel Class # MSDN
This control is available from the Toolbox in Visual Studio, so a good starting point would be to make a custom control based upon a FlowLayoutPanel, and another custom control to represent a tag based upon a Label or Checkbox, and update the drawing and behavior (in the case of a Label) to respond to clicks and to display the [x] to dismiss the tag.
Label Class # MSDN
CheckBox Class # MSDN
After making these two controls, you will need to do additional work to make your FlowLayoutPanel-derived control add/remove Label-based controls representing the state of the tags present.
Update: One thing I missed, a TextBox or other input field would need to be added in place to support adding new tags.
TextBox # MSDN
public class TagLayoutPanel : FlowLayoutPanel
{
//local variables/controls
private TextBox _entryBox;
private Dictionary<string,string> _currentTags;
//events
public delegate void TagsUpdatedHandler(TagEventArgs e);
public event TagsUpdatedHandler TagsUpdated;
//constructor(s)
public TagLayoutPanel()
{
Init();
}
public List<string> GetCurrentTags()
{
var lst = new List<string>();
if (_currentTags != null)
lst = _currentTags.Keys.ToList();
return lst;
}
private void Init()
{
_currentTags = new Dictionary<string, string>();
//Entry box
this.Padding = new Padding(3);
this.BackColor = Color.White;
_entryBox = new TextBox();
_entryBox.BackColor = Color.White;
_entryBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
_entryBox.KeyUp += _entryBox_KeyUp;
this.Controls.Add(_entryBox);
}
private void _entryBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
var tag = _entryBox.Text;
if (!string.IsNullOrEmpty(tag))
{
this.AddTag(tag);
_entryBox.Text = String.Empty;
}
}
}
protected override void OnGotFocus(EventArgs e)
{
//Set focus to the textentry box.
base.OnGotFocus(e);
this._entryBox.Focus();
}
public void AddTag(string tag)
{
bool added = false;
if (!_currentTags.ContainsKey(tag))
{
_currentTags.Add(tag, tag);
added = true;
}
if(added)
{
Redraw();
Notify();
}
}
private void Notify()
{
if(TagsUpdated != null)
TagsUpdated(new TagEventArgs(GetCurrentTags().ToArray()));
}
public void Redraw()
{
this.Controls.Clear();
foreach (var tag in _currentTags.Keys)
{
DrawTag(tag);
}
AddEntry();
}
private void AddEntry()
{
this.Controls.Add(_entryBox);
_entryBox.Focus();
}
public void DrawTag(string tag)
{
var lbl = new Label();
lbl.MouseMove += lbl_MouseMove;
lbl.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
lbl.BackColor = Color.LightGray;
lbl.Name = "lbl_" + tag.Replace(" ", "");
lbl.ImageAlign = ContentAlignment.TopRight;
lbl.Text = tag;
lbl.Tag = tag;
lbl.Image = Resources.close_x; //Replace with your own image.
lbl.Click += lbl_Click;
this.Controls.Add(lbl);
}
private void lbl_Click(object sender, EventArgs e)
{
var lbl = (Label)sender;
bool removed = false;
foreach(var item in this.Controls)
{
if(item is Label)
{
if(((Label)item).Tag.ToString() == lbl.Tag.ToString())
{
_currentTags.Remove(lbl.Tag.ToString());
removed = true;
break;
}
}
}
if(removed)
{
Redraw();
Notify();
}
}
private void lbl_MouseMove(object sender, MouseEventArgs e)
{
var lbl = (Label)sender;
var startImgX = lbl.Width - 20;
var endImgY = lbl.Height - 15;
if (e.X >= startImgX && e.Y <= endImgY)
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Hand;
else
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Arrow;
}
}
public class TagEventArgs : EventArgs
{
public string[] Tags { get; private set; }
public TagEventArgs(string[] tags)
{
Tags = tags;
}
}

Only numbers in the text box

if (!(char.IsDigit(e.KeyChar)))
{
e.Handled = true;
}
The above code is not working properly
Below is the image error :
The problem space is "Clipboard"
If this is for WinForms, my suggestion would be to use a MaskedTextBox instead. This is a purpose-built control for allowing only certain kinds of user-input.
You can set the mask through the designer or in code.
For example, for a 5-digit numeric:
maskedTextBox1.Mask = "00000";
maskedTextBox1.ValidatingType = typeof(int);
Yes, this is the typical nemesis for keyboard filtering. The TextBox control doesn't have any built-in events to intercept a paste from the clipboard. You'll have to detect the Ctrl+V keypress yourself and screen Clipboard.GetText().
The logic is tricky to get right. Here's a class that can make all this a little easier. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto a form. Double click it and write the ValidateChar event handler. Like this one, only allowing entering digits:
private void validatingTextBox1_ValidateChar(object sender, ValidateCharArgs e) {
if (!"0123456789".Contains(e.KeyChar)) e.Cancel = true;
}
The code:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Text;
[DefaultEvent("ValidateChar")]
class ValidatingTextBox : TextBox {
public event EventHandler<ValidateCharArgs> ValidateChar;
protected virtual void OnValidateChar(ValidateCharArgs e) {
var handler = ValidateChar;
if (handler != null) handler(this, e);
}
protected override void OnKeyPress(KeyPressEventArgs e) {
if (e.KeyChar >= ' ') { // Allow the control keys to work as normal
var args = new ValidateCharArgs(e.KeyChar);
OnValidateChar(args);
if (args.Cancel) {
e.Handled = true;
return;
}
}
base.OnKeyPress(e);
}
private void HandlePaste() {
if (!Clipboard.ContainsText()) return;
string text = Clipboard.GetText();
var toPaste = new StringBuilder(text.Length);
foreach (char ch in text.ToCharArray()) {
var args = new ValidateCharArgs(ch);
OnValidateChar(args);
if (!args.Cancel) toPaste.Append(ch);
}
if (toPaste.Length != 0) {
Clipboard.SetText(toPaste.ToString());
this.Paste();
}
}
bool pasting;
protected override void WndProc(ref Message m) {
if (m.Msg == 0x302 && !pasting) {
pasting = true;
HandlePaste();
pasting = false;
}
else base.WndProc(ref m);
}
}
class ValidateCharArgs : EventArgs {
public ValidateCharArgs(char ch) { Cancel = false; KeyChar = ch; }
public bool Cancel { get; set; }
public char KeyChar { get; set; }
}
Handle TextChanged event or use a MaskedTextBox.
if (textBox1.Text.Count(a => !char.IsDigit(a)) > 0)
{
textBox1.Text = new string(textBox1.Text.Where(a => char.IsDigit(a)).ToArray());
}
I answered a similar question on StackOverflow once.
Here's the link to the question: Best way to limit textbox decimal input in c#
Essentially, you'll have to put my class in your code and apply it to all textboxes you want to restrict data entered.
The TextBoxFilter class I wrote allows you to limit entry to Alphabet, Numerics, AlphaNumerics, Currency and UserSpecified input.
control.TextChanged += (s, a) => {
string value = string.Empty;
foreach (char ch in control.Text.ToCharArray())
{
if (char.IsDigit(ch))
{
value += ch.ToString();
}
}
control.Text = value;
};

Categories