How to disable Togglebutton from extended combobox class - c#

I have a class that extends Combobox. In this class I can ONLY disable the togglebutton. I still want users to click into the combobox and type something in to filter its contents.
If you know any other way to destroy the togglebutton please let me know (as long as it doesn't involve recreating the template - I've tried and failed and the generated combobox template code is to large to dump in stackoverflow so I can't solve it that way. )
In the beginning I wanted to be able to toggle the togglebutton (if there less than 10 item left in the filter allow a user to click the togglebutton to show the list of items if there are more the togglebutton would disappear) . At this point I'll take any solution that shrinks, hides, moves, intentionally breaks, removes, buries alive, replaces either the togglebutton or the mouse on click event that tells the combobox that it should show its popup or anything in between that will stop this action.
This is my third question trying to find something that will work. Honestly any suggestions would be helpful (just no copy and edit the template please go to my other question where I ask help on how to implement that solution)
using Analytics_Module.Models;
using System;
using System.Collections;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace Analytics_Module.UI_Components
{
class MultiselectFilteredComboBox : ComboBox
{
////
// Public Fields
////
/// <summary>
/// The search string treshold length.
/// </summary>
/// <remarks>
/// It's implemented as a Dependency Property, so you can set it in a XAML template
/// </remarks>
public static readonly DependencyProperty MinimumSearchLengthProperty =
DependencyProperty.Register(
"MinimumSearchLength",
typeof(int),
typeof(MultiselectFilteredComboBox),
new UIPropertyMetadata(3));
////
// Private Fields
////
/// <summary>
/// Caches the previous value of the filter.
/// </summary>
private string oldFilter = string.Empty;
/// <summary>
/// Holds the current value of the filter.
/// </summary>
private string currentFilter = string.Empty;
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
////
// Constructors
////
/// <summary>
/// Initializes a new instance of the FilteredComboBox class.
/// </summary>
/// <remarks>
/// You could set 'IsTextSearchEnabled' to 'false' here,
/// to avoid non-intuitive behavior of the control
/// </remarks>
public MultiselectFilteredComboBox()
{
}
////
// Properties
////
/// <summary>
/// Gets or sets the search string treshold length.
/// </summary>
/// <value>The minimum length of the search string that triggers filtering.</value>
[Description("Length of the search string that triggers filtering.")]
[Category("Filtered ComboBox")]
[DefaultValue(3)]
public int MinimumSearchLength
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (int)this.GetValue(MinimumSearchLengthProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(MinimumSearchLengthProperty, value);
}
}
/// <summary>
/// Gets a reference to the internal editable textbox.
/// </summary>
/// <value>A reference to the internal editable textbox.</value>
/// <remarks>
/// We need this to get access to the Selection.
/// </remarks>
protected TextBox EditableTextBox
{
get
{
return this.GetTemplateChild("PART_EditableTextBox") as TextBox;
}
}
////
// Event Raiser Overrides
////
/// <summary>
/// Keep the filter if the ItemsSource is explicitly changed.
/// </summary>
/// <param name="oldValue">The previous value of the filter.</param>
/// <param name="newValue">The current value of the filter.</param>
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
/// <summary>
/// Confirm or cancel the selection when Tab, Enter, or Escape are hit.
/// Open the DropDown when the Down Arrow is hit.
/// </summary>
/// <param name="e">Key Event Args.</param>
/// <remarks>
/// The 'KeyDown' event is not raised for Arrows, Tab and Enter keys.
/// It is swallowed by the DropDown if it's open.
/// So use the Preview instead.
/// </remarks>
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Tab || e.Key == Key.Enter)
{
// Explicit Selection -> Close ItemsPanel
this.IsDropDownOpen = false;
}
else if (e.Key == Key.Escape)
{
// Escape -> Close DropDown and redisplay Filter
this.IsDropDownOpen = false;
this.SelectedIndex = -1;
this.Text = this.currentFilter;
}
else
{
if (e.Key == Key.Down)
{
// Arrow Down -> Open DropDown
this.IsDropDownOpen = true;
}
base.OnPreviewKeyDown(e);
}
// Cache text
this.oldFilter = this.Text;
}
/// <summary>
/// Modify and apply the filter.
/// </summary>
/// <param name="e">Key Event Args.</param>
/// <remarks>
/// Alternatively, you could react on 'OnTextChanged', but navigating through
/// the DropDown will also change the text.
/// </remarks>
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.Key == Key.Up || e.Key == Key.Down)
{
// Navigation keys are ignored
}
else if (e.Key == Key.Tab || e.Key == Key.Enter)
{
// Explicit Select -> Clear Filter
this.ClearFilter();
}
else
{
// The text was changed
if (this.Text != this.oldFilter)
{
// Clear the filter if the text is empty,
// apply the filter if the text is long enough
if (this.Text.Length == 0 || this.Text.Length >= this.MinimumSearchLength)
{
this.RefreshFilter();
this.IsDropDownOpen = true;
// Unselect
this.EditableTextBox.SelectionStart = int.MaxValue;
}
}
base.OnKeyUp(e);
// Update Filter Value
this.currentFilter = this.Text;
}
}
/// <summary>
/// Make sure the text corresponds to the selection when leaving the control.
/// </summary>
/// <param name="e">A KeyBoardFocusChangedEventArgs.</param>
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
//this.ClearFilter();
//int temp = this.SelectedIndex;
//this.SelectedIndex = -1;
//this.Text = string.Empty;
//this.SelectedIndex = temp;
//base.OnPreviewLostKeyboardFocus(e);
}
////
// Helpers
////
/// <summary>
/// Re-apply the Filter.
/// </summary>
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
}
}
/// <summary>
/// Clear the Filter.
/// </summary>
private void ClearFilter()
{
this.currentFilter = string.Empty;
this.RefreshFilter();
}
/// <summary>
/// The Filter predicate that will be applied to each row in the ItemsSource.
/// </summary>
/// <param name="value">A row in the ItemsSource.</param>
/// <returns>Whether or not the item will appear in the DropDown.</returns>
private bool FilterPredicate(object value)
{
MultiSelectDropDownListEntry tmp = (MultiSelectDropDownListEntry)value;
// No filter, no text
if (value == null)
{
return false;
}
// No text, no filter
if (this.Text.Length == 0)
{
return true;
}
// Case insensitive search
return tmp.Name.ToString().ToLower().Contains(this.Text.ToLower());
}
}
}

Found a way.
I added this to the class
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
(((this.GetVisualChild(0) as Grid).Children)[1] as System.Windows.Controls.Primitives.ToggleButton).IsEnabled = false;
}

Related

WPF Tab Control Attached Property

I am having difficulty getting an attached property working on a WPF Tab Control. I have implemented the class defined in the CodeProject tutorial
http://www.codeproject.com/Articles/349140/WPF-TabControl-focus-behavior-with-invisible-tabs
defined below.
namespace MyNamespace
{
public static class TabControlBehavior
{
public static readonly DependencyProperty FocusFirstVisibleTabProperty =
DependencyProperty.RegisterAttached("FocusFirstVisibleTab",
typeof(bool),
typeof(TabControlBehavior),
new FrameworkPropertyMetadata(OnFocusFirstVisibleTabPropertyChanged));
/// <summary>Gets the focus first visible tab value of the given element.
/// </summary>
/// <param name="element">The element.</param>
/// <returns></returns>
public static bool GetFocusFirstVisibleTab(TabControl element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool)element.GetValue(FocusFirstVisibleTabProperty);
}
/// <summary>Sets the focus first visible tab value of the given element.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="value">if set to <c>true</c> [value].</param>
public static void SetFocusFirstVisibleTab(TabControl element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(FocusFirstVisibleTabProperty, value);
}
/// <summary>Determines whether the value of the dependency property <c>IsFocused</c> has change.
/// </summary>
/// <param name="d">The dependency object.</param>
/// <param name="e">The <see
/// cref="System.Windows.DependencyPropertyChangedEventArgs"/>
/// instance containing the event data.</param>
private static void OnFocusFirstVisibleTabPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tabControl = d as TabControl;
if (tabControl != null)
{
// Attach or detach the event handlers.
if ((bool)e.NewValue)
{
// Enable the attached behavior.
tabControl.Items.CurrentChanged += new EventHandler(TabControl_Items_CurrentChanged);
var collection = tabControl.Items as INotifyCollectionChanged;
if (collection != null)
{
collection.CollectionChanged +=
new NotifyCollectionChangedEventHandler(TabControl_Items_CollectionChanged);
}
}
else
{
// Disable the attached behavior.
tabControl.Items.CurrentChanged -= new EventHandler(TabControl_Items_CurrentChanged);
var collection = tabControl.Items as INotifyCollectionChanged;
if (collection != null)
{
collection.CollectionChanged -=
new NotifyCollectionChangedEventHandler(TabControl_Items_CollectionChanged);
}
// Detach handlers from the tab items.
foreach (var item in tabControl.Items)
{
TabItem tab = item as TabItem;
if (tab != null)
{
tab.IsVisibleChanged -=
new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
}
}
}
}
}
/// <summary>Handles the CollectionChanged event of the TabControl.Items collection.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see
/// cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/>
/// instance containing the event data.</param>
static void TabControl_Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Attach event handlers to each tab so that when the Visibility property changes of the selected tab,
// the focus can be shifted to the next (or previous, if not next tab available) tab.
var collection = sender as ItemCollection;
if (collection != null)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
// Attach event handlers to the Visibility and IsEnabled properties.
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
TabItem tab = item as TabItem;
if (tab != null)
{
tab.IsVisibleChanged +=
new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
}
}
}
// Detach event handlers from old items.
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
TabItem tab = item as TabItem;
if (tab != null)
{
tab.IsVisibleChanged -=
new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
// Attach event handlers to the Visibility and IsEnabled properties.
foreach (var item in collection)
{
TabItem tab = item as TabItem;
if (tab != null)
{
tab.IsVisibleChanged +=
new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
}
}
break;
case NotifyCollectionChangedAction.Move:
default:
break;
}
// Select the first element if necessary.
if (collection.Count > 0 && collection.CurrentItem == null)
{
collection.MoveCurrentToFirst();
}
}
}
/// <summary>Handles the CurrentChanged event of the TabControl.Items collection.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/>
/// instance containing the event data.</param>
static void TabControl_Items_CurrentChanged(object sender, EventArgs e)
{
var collection = sender as ItemCollection;
if (collection != null)
{
UIElement element = collection.CurrentItem as UIElement;
if (element != null && element.Visibility != Visibility.Visible)
{
element.Dispatcher.BeginInvoke(new Action(() => collection.MoveCurrentToNext()),
System.Windows.Threading.DispatcherPriority.Input);
}
}
}
/// <summary>Handles the IsVisibleChanged event of the tab item.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see
/// cref="System.Windows.DependencyPropertyChangedEventArgs"/>
/// instance containing the event data.</param>
static void TabItem_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
TabItem tab = sender as TabItem;
if (tab != null && tab.IsSelected && tab.Visibility != Visibility.Visible)
{
// Move to the next tab item.
TabControl tabControl = tab.Parent as TabControl;
if (tabControl != null)
{
if (!tabControl.Items.MoveCurrentToNext())
{
// Could not move to next, try previous.
tabControl.Items.MoveCurrentToPrevious();
}
}
}
}
}
}
I then try to set the attached dependency property in my xaml code as follows :
<Window x:Class="MyNamespace.MyApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:MyNamespace">
<Window.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type TabControl}">
<Setter Property="local:TabControlBehavior.FocusFirstVisibleTab" Value="True" />
</Style>
</ResourceDictionary>
</Window.Resources>
</Window>
However I can't compile due to the following error
MC4003: Cannot resolve the Style Property 'FocusFirstVisibleTab'. Verify that the owning type is the Style's TargetType, or use Class.Property syntax to specify the Property.
Thanks in advance for any help.
The problem was resolved by renaming MyNamespace to something other than the namespace containing my application.

AutoSuggestion in a WPF combobox

My combobox returns a set of values from s stored procedure as this
private void BindCombo()
{
DataCombo.FillCombo(ComboDS(2313001), cmbClass, 0);
DataCombo.FillCombo(DDCombo(5007), cmbGroup, 0);
}
I managed to give a rudimentary auto complete suggestion as IsTextSearchenabled but cannot get a auto suggestion box that i would like.
I have seen loads of examples of autocomplete/suggestive textboxes but none of them seem to suit me.
this code apparently suits me.
but how would i use the auto suggest here
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DotNetZen.AutoFilteredComboBox
{
public class AutoFilteredComboBox : ComboBox
{
private int silenceEvents = 0;
/// <summary>
/// Creates a new instance of <see cref="AutoFilteredComboBox" />.
/// </summary>
public AutoFilteredComboBox()
{
DependencyPropertyDescriptor textProperty = DependencyPropertyDescriptor.FromProperty(
ComboBox.TextProperty, typeof(AutoFilteredComboBox));
textProperty.AddValueChanged(this, this.OnTextChanged);
this.RegisterIsCaseSensitiveChangeNotification();
}
#region IsCaseSensitive Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="IsCaseSensitive" /> dependency property.
/// </summary>
public static readonly DependencyProperty IsCaseSensitiveProperty =
DependencyProperty.Register("IsCaseSensitive", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box treats the case sensitivity of typed text.
/// </summary>
/// <value>The way the combo box treats the case sensitivity of typed text.</value>
[System.ComponentModel.Description("The way the combo box treats the case sensitivity of typed text.")]
[System.ComponentModel.Category("AutoFiltered ComboBox")]
[System.ComponentModel.DefaultValue(true)]
public bool IsCaseSensitive
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(IsCaseSensitiveProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(IsCaseSensitiveProperty, value);
}
}
protected virtual void OnIsCaseSensitiveChanged(object sender, EventArgs e)
{
if (this.IsCaseSensitive)
this.IsTextSearchEnabled = false;
this.RefreshFilter();
}
private void RegisterIsCaseSensitiveChangeNotification()
{
System.ComponentModel.DependencyPropertyDescriptor.FromProperty(IsCaseSensitiveProperty, typeof(AutoFilteredComboBox)).AddValueChanged(
this, this.OnIsCaseSensitiveChanged);
}
#endregion
#region DropDownOnFocus Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="DropDownOnFocus" /> dependency property.
/// </summary>
public static readonly DependencyProperty DropDownOnFocusProperty =
DependencyProperty.Register("DropDownOnFocus", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(true));
/// <summary>
/// Gets or sets the way the combo box behaves when it receives focus.
/// </summary>
/// <value>The way the combo box behaves when it receives focus.</value>
[System.ComponentModel.Description("The way the combo box behaves when it receives focus.")]
[System.ComponentModel.Category("AutoFiltered ComboBox")]
[System.ComponentModel.DefaultValue(true)]
public bool DropDownOnFocus
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(DropDownOnFocusProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(DropDownOnFocusProperty, value);
}
}
#endregion
#region | Handle selection |
/// <summary>
/// Called when <see cref="ComboBox.ApplyTemplate()"/> is called.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.EditableTextBox.SelectionChanged += this.EditableTextBox_SelectionChanged;
}
/// <summary>
/// Gets the text box in charge of the editable portion of the combo box.
/// </summary>
protected TextBox EditableTextBox
{
get
{
return ((TextBox)base.GetTemplateChild("PART_EditableTextBox"));
}
}
private int start = 0, length = 0;
private void EditableTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (this.silenceEvents == 0)
{
this.start = ((TextBox)(e.OriginalSource)).SelectionStart;
this.length = ((TextBox)(e.OriginalSource)).SelectionLength;
this.RefreshFilter();
}
}
#endregion
#region | Handle focus |
/// <summary>
/// Invoked whenever an unhandled <see cref="UIElement.GotFocus" /> event
/// reaches this element in its route.
/// </summary>
/// <param name="e">The <see cref="RoutedEventArgs" /> that contains the event data.</param>
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (this.ItemsSource != null && this.DropDownOnFocus)
{
this.IsDropDownOpen = true;
}
}
#endregion
#region | Handle filtering |
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
this.IsDropDownOpen = true;
}
}
private bool FilterPredicate(object value)
{
// We don't like nulls.
if (value == null)
return false;
// If there is no text, there's no reason to filter.
if (this.Text.Length == 0)
return true;
string prefix = this.Text;
// If the end of the text is selected, do not mind it.
if (this.length > 0 && this.start + this.length == this.Text.Length)
{
prefix = prefix.Substring(0, this.start);
}
return value.ToString()
.StartsWith(prefix, !this.IsCaseSensitive, CultureInfo.CurrentCulture);
}
#endregion
/// <summary>
/// Called when the source of an item in a selector changes.
/// </summary>
/// <param name="oldValue">Old value of the source.</param>
/// <param name="newValue">New value of the source.</param>
protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
private void OnTextChanged(object sender, EventArgs e)
{
if (!this.IsTextSearchEnabled && this.silenceEvents == 0)
{
this.RefreshFilter();
// Manually simulate the automatic selection that would have been
// available if the IsTextSearchEnabled dependency property was set.
if (this.Text.Length > 0)
{
foreach (object item in CollectionViewSource.GetDefaultView(this.ItemsSource))
{
int text = item.ToString().Length, prefix = this.Text.Length;
this.SelectedItem = item;
this.silenceEvents++;
this.EditableTextBox.Text = item.ToString();
this.EditableTextBox.Select(prefix, text - prefix);
this.silenceEvents--;
break;
}
}
}
}
}
}
Also found the AutoFilteredComboBox to be very simple to work with. Though I have made some changes:
Removed use of DependencyPropertyDescriptor to avoid memory leak of combobox-objects
Introduced FilterItem-event and FilterList-event to allow one to customize the filtering
Changed default filtering from starts-with-string to contains-string
Removed support of having IsTextSearchEnabled enabled
Shows dropdown as soon one changes the search string, so search result is displayed
Example of how it is used:
<Controls:AutoFilteredComboBox ItemsSource="{Binding ViewModel.AvailableItems}"
SelectedValue="{Binding ViewModel.SelectedItem, Mode=TwoWay}"
IsEditable="True" IsTextSearchEnabled="False"/>
Improved version of AutoFilteredComboBox:
public class AutoFilteredComboBox : ComboBox
{
bool _ignoreTextChanged;
string _currentText;
/// <summary>
/// Creates a new instance of <see cref="AutoFilteredComboBox" />.
/// </summary>
public AutoFilteredComboBox()
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) return;
}
public event Func<object, string, bool> FilterItem;
public event Action<string> FilterList;
#region IsCaseSensitive Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="IsCaseSensitive" /> dependency property.
/// </summary>
public static readonly DependencyProperty IsCaseSensitiveProperty =
DependencyProperty.Register("IsCaseSensitive", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box treats the case sensitivity of typed text.
/// </summary>
/// <value>The way the combo box treats the case sensitivity of typed text.</value>
[Description("The way the combo box treats the case sensitivity of typed text.")]
[Category("AutoFiltered ComboBox")]
[DefaultValue(true)]
public bool IsCaseSensitive
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(IsCaseSensitiveProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(IsCaseSensitiveProperty, value);
}
}
#endregion
#region DropDownOnFocus Dependency Property
/// <summary>
/// The <see cref="DependencyProperty"/> object of the <see cref="DropDownOnFocus" /> dependency property.
/// </summary>
public static readonly DependencyProperty DropDownOnFocusProperty =
DependencyProperty.Register("DropDownOnFocus", typeof(bool), typeof(AutoFilteredComboBox), new UIPropertyMetadata(false));
/// <summary>
/// Gets or sets the way the combo box behaves when it receives focus.
/// </summary>
/// <value>The way the combo box behaves when it receives focus.</value>
[Description("The way the combo box behaves when it receives focus.")]
[Category("AutoFiltered ComboBox")]
[DefaultValue(false)]
public bool DropDownOnFocus
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (bool)this.GetValue(DropDownOnFocusProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(DropDownOnFocusProperty, value);
}
}
#endregion
#region | Handle focus |
/// <summary>
/// Invoked whenever an unhandled <see cref="UIElement.GotFocus" /> event
/// reaches this element in its route.
/// </summary>
/// <param name="e">The <see cref="RoutedEventArgs" /> that contains the event data.</param>
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (this.ItemsSource != null && this.DropDownOnFocus)
{
this.IsDropDownOpen = true;
}
}
#endregion
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(OnTextChanged));
KeyUp += AutoFilteredComboBox_KeyUp;
this.IsTextSearchEnabled = false;
}
void AutoFilteredComboBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down)
{
if (this.IsDropDownOpen == true)
{
// Ensure that focus is given to the dropdown list
if (Keyboard.FocusedElement is TextBox)
{
Keyboard.Focus(this);
if (this.Items.Count > 0)
{
if (this.SelectedIndex == -1 || this.SelectedIndex==0)
this.SelectedIndex = 0;
}
}
}
}
if (Keyboard.FocusedElement is TextBox)
{
if (e.OriginalSource is TextBox)
{
// Avoid the automatic selection of the first letter (As next letter will cause overwrite)
TextBox textBox = e.OriginalSource as TextBox;
if (textBox.Text.Length == 1 && textBox.SelectionLength == 1)
{
textBox.SelectionLength = 0;
textBox.SelectionStart = 1;
}
}
}
}
#region | Handle filtering |
private void RefreshFilter()
{
if (this.ItemsSource != null)
{
Action<string> filterList = FilterList;
if (filterList != null)
{
filterList(_currentText);
}
else
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();
}
this.SelectedIndex = -1; // Prepare so arrow down selects first
this.IsDropDownOpen = true;
}
}
private bool FilterPredicate(object value)
{
// We don't like nulls.
if (value == null)
return false;
// If there is no text, there's no reason to filter.
if (string.IsNullOrEmpty(_currentText))
return true;
Func<object, string, bool> filterItem = FilterItem;
if (filterItem != null)
return filterItem(value, _currentText);
if (IsCaseSensitive)
return value.ToString().Contains(_currentText);
else
return value.ToString().ToUpper().Contains(_currentText.ToUpper());
}
#endregion
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
try
{
_ignoreTextChanged = true; // Ignore the following TextChanged
base.OnSelectionChanged(e);
}
finally
{
_ignoreTextChanged = false;
}
}
/// <summary>
/// Called when the source of an item in a selector changes.
/// </summary>
/// <param name="oldValue">Old value of the source.</param>
/// <param name="newValue">New value of the source.</param>
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
if (FilterList == null)
view.Filter += this.FilterPredicate;
}
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
view.Filter -= this.FilterPredicate;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (_ignoreTextChanged)
return;
_currentText = Text;
if (!this.IsTextSearchEnabled)
{
this.RefreshFilter();
}
}
I have found a super simple workaround to my problem.
i created a preview text input event of the combobox.
then i just wrote
Combobox.IsDropDownOpen = true
may not the most elegant but works in my case

Numeric Only TextBox

I've looked all over the place, but it seems that examples I have seen allow only numbers 0-9
I'm writing a Pythagorean Theorem program. I wish to have the phone (Windows Phone 7) check if there are ANY alpha (A-Z, a-z), symbols (#,%), or anything other than a number in the textbox. If not, then it will continue computing. I want to check so there will be no future errors.
This is basically a bad pseudocode of what I want it to do
txtOne-->any alpha?--No-->any symbols--No-->continue...
I would actually prefer a command to check if the string is completely a number.
Thanks in advance!
An even better way to ensure that your textbox is a number is to handle the KeyPress event. You can then choose what characters you want to allow. In the following example we disallow all characters that are not digits:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// If the character is not a digit, don't let it show up in the textbox.
if (!char.IsDigit(e.KeyChar))
e.Handled = true;
}
This ensures that your textbox text is a number because it only allows digits to be entered.
This is something I just came up with to allow decimal values (and apparently the backspace key):
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsDigit(e.KeyChar))
{
return;
}
if (e.KeyChar == (char)Keys.Back)
{
return;
}
if (e.KeyChar == '.' && !textBox1.Text.Contains('.'))
{
return;
}
e.Handled = true;
}
There are several ways to do this:
You can use TryParse() and check if the return value is not false.
You can use Regex to validate:
Match match = Regex.Match(textBox.Text, #"^\d+$");
if (match.Success) { ... }
// or
if (Regex.IsMatch(textBox.Text, #"^\d+$")) { ... }
Or you can simply only give them the Numeric keyboard. There are several different keyboard layouts you can use. Link
If you want to do more indepth, I have used the KeyDown and KeyUp events to check what was entered and handle the keypress. Link
You can define the textbox's input scope.
Example:
<TextBox InputScope="Digits"></TextBox>
You can use TryParse and see if there is a result.
See http://msdn.microsoft.com/en-us/library/system.int64.tryparse.aspx
Int64 output;
if (!Int64.TryParse(input, out output)
{
ShowErrorMessage();
return
}
Continue..
/// <summary>
/// A numeric-only textbox.
/// </summary>
public class NumericOnlyTextBox : TextBox
{
#region Properties
#region AllowDecimals
/// <summary>
/// Gets or sets a value indicating whether [allow decimals].
/// </summary>
/// <value>
/// <c>true</c> if [allow decimals]; otherwise, <c>false</c>.
/// </value>
public bool AllowDecimals
{
get { return (bool)GetValue(AllowDecimalsProperty); }
set { SetValue(AllowDecimalsProperty, value); }
}
/// <summary>
/// The allow decimals property
/// </summary>
public static readonly DependencyProperty AllowDecimalsProperty =
DependencyProperty.Register("AllowDecimals", typeof(bool),
typeof(NumericOnlyTextBox), new UIPropertyMetadata(false));
#endregion
#region MaxValue
/// <summary>
/// Gets or sets the max value.
/// </summary>
/// <value>
/// The max value.
/// </value>
public double? MaxValue
{
get { return (double?)GetValue(MaxValueProperty); }
set { SetValue(MaxValueProperty, value); }
}
/// <summary>
/// The max value property
/// </summary>
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(double?),
typeof(NumericOnlyTextBox), new UIPropertyMetadata(null));
#endregion
#region MinValue
/// <summary>
/// Gets or sets the min value.
/// </summary>
/// <value>
/// The min value.
/// </value>
public double? MinValue
{
get { return (double?)GetValue(MinValueProperty); }
set { SetValue(MinValueProperty, value); }
}
/// <summary>
/// The min value property
/// </summary>
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(double?),
typeof(NumericOnlyTextBox), new UIPropertyMetadata(null));
#endregion
#endregion
#region Contructors
/// <summary>
/// Initializes a new instance of the <see cref="NumericOnlyTextBox" /> class.
/// </summary>
public NumericOnlyTextBox()
{
this.PreviewTextInput += OnPreviewTextInput;
}
#endregion
#region Methods
/// <summary>
/// Numeric-Only text field.
/// </summary>
/// <param name="text">The text.</param>
/// <returns></returns>
public bool NumericOnlyCheck(string text)
{
// regex that matches disallowed text
var regex = (AllowDecimals) ? new Regex("[^0-9.]+") : new Regex("[^0-9]+");
return !regex.IsMatch(text);
}
#endregion
#region Events
/// <summary>
/// Called when [preview text input].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="TextCompositionEventArgs" /> instance
/// containing the event data.</param>
/// <exception cref="System.NotImplementedException"></exception>
private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
// Check number
if (this.NumericOnlyCheck(e.Text))
{
// Evaluate min value
if (MinValue != null && Convert.ToDouble(this.Text + e.Text) < MinValue)
{
this.Text = MinValue.ToString();
this.SelectionStart = this.Text.Length;
e.Handled = true;
}
// Evaluate max value
if (MaxValue != null && Convert.ToDouble(this.Text + e.Text) > MaxValue)
{
this.Text = MaxValue.ToString();
this.SelectionStart = this.Text.Length;
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
#endregion
}

ASP.NET - Extend gridview to allow filtering, sorting, paging, etc

I have seen threads on many sites regarding extending the gridview control so obviously this will be a duplicate. But I haven't found any that truly extend the control to the extent that you could have custom sorting (with header images), filtering by putting drop downs or textboxes in header columns (on a column by column basis) and custom paging (one that doesn't return all records but just returns the ones requested for the given page).
Are there any good tutorials that show the inner-workings of the gridview and how to override the proper functions? I've seen several snippets here and there but none seem to really work and explain things well.
Any links would be appreciated. Thanks!
I've extended the GridView control myself to allow sorting with images, custom paging (so you can select how many records per page from a drop-down) and a few other things. However, you won't be able to do custom paging that just returns the records for the requested page, as that is something your datasource needs to handle and not the GridView.
All I can really do is give you some code and hope it helps. It's pretty old code (pre C#3.0) but may be of some use:
First of all here's the custom GridView control that extends the standard GridView:
using System;
using System.Collections;
using System.Drawing;
using System.Web.UI.WebControls;
using Diplo.WebControls.DataControls.PagerTemplates;
using Image=System.Web.UI.WebControls.Image;
namespace Diplo.WebControls.DataControls
{
/// <summary>
/// Extended <see cref="GridView"/> with some additional cool properties
/// </summary>
public class DiploGridView : GridView
{
#region Properties
/// <summary>
/// Gets or sets a value indicating whether a sort graphic is shown in column headings
/// </summary>
/// <value><c>true</c> if sort graphic is displayed; otherwise, <c>false</c>.</value>
public bool EnableSortGraphic
{
get
{
object o = ViewState["EnableSortGraphic"];
if (o != null)
{
return (bool)o;
}
return true;
}
set
{
ViewState["EnableSortGraphic"] = value;
}
}
/// <summary>
/// Gets or sets the sort ascending image when <see cref="EnableSortGraphic"/> is <c>true</c>
/// </summary>
public string SortAscendingImage
{
get
{
object o = ViewState["SortAscendingImage"];
if (o != null)
{
return (string)o;
}
return Page.ClientScript.GetWebResourceUrl(GetType(), SharedWebResources.ArrowUpImage);
}
set
{
ViewState["SortAscendingImage"] = value;
}
}
/// <summary>
/// Gets or sets the sort descending image <see cref="EnableSortGraphic"/> is <c>true</c>
/// </summary>
public string SortDescendingImage
{
get
{
object o = ViewState["SortDescendingImage"];
if (o != null)
{
return (string)o;
}
return Page.ClientScript.GetWebResourceUrl(GetType(), SharedWebResources.ArrowDownImage);
}
set
{
ViewState["SortDescendingImage"] = value;
}
}
/// <summary>
/// Gets or sets the custom pager settings mode.
/// </summary>
public CustomPagerMode CustomPagerSettingsMode
{
get
{
object o = ViewState["CustomPagerSettingsMode"];
if (o != null)
{
return (CustomPagerMode)o;
}
return CustomPagerMode.None;
}
set
{
ViewState["CustomPagerSettingsMode"] = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether the columns in the grid can be re-sized in the UI
/// </summary>
/// <value><c>true</c> if column resizing is allowed; otherwise, <c>false</c>.</value>
public bool AllowColumnResizing
{
get
{
object o = ViewState["AllowColumnResizing"];
if (o != null)
{
return (bool)o;
}
return false;
}
set
{
ViewState["AllowColumnResizing"] = value;
}
}
/// <summary>
/// Gets or sets the highlight colour for the row
/// </summary>
public Color RowStyleHighlightColour
{
get
{
object o = ViewState["RowStyleHighlightColour"];
if (o != null)
{
return (Color)o;
}
return Color.Empty;
}
set
{
ViewState["RowStyleHighlightColour"] = value;
}
}
#endregion Properties
#region Enums
/// <summary>
/// Represents additional custom paging modes
/// </summary>
public enum CustomPagerMode
{
/// <summary>
/// No custom paging mode
/// </summary>
None,
/// <summary>
/// Shows the rows drop-down list <i>and</i> the previous and next buttons
/// </summary>
RowsPagePreviousNext,
/// <summary>
/// Only shows the previous and next buttons
/// </summary>
PagePreviousNext
}
#endregion
#region Overridden Events
/// <summary>
/// Initializes the pager row displayed when the paging feature is enabled.
/// </summary>
/// <param name="row">A <see cref="T:System.Web.UI.WebControls.GridViewRow"></see> that represents the pager row to initialize.</param>
/// <param name="columnSpan">The number of columns the pager row should span.</param>
/// <param name="pagedDataSource">A <see cref="T:System.Web.UI.WebControls.PagedDataSource"></see> that represents the data source.</param>
protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
switch (CustomPagerSettingsMode)
{
case CustomPagerMode.RowsPagePreviousNext:
PagerTemplate = new RowsPagePreviousNext(pagedDataSource, this);
break;
case CustomPagerMode.PagePreviousNext:
PagerTemplate = new PagePreviousNext(pagedDataSource, this);
break;
case CustomPagerMode.None:
break;
default:
break;
}
base.InitializePager(row, columnSpan, pagedDataSource);
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.PreRender"></see> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
protected override void OnPreRender(EventArgs e)
{
if (AllowColumnResizing && Visible)
{
string vars = String.Format("var _DiploGridviewId = '{0}';\n", ClientID);
if (!Page.ClientScript.IsClientScriptBlockRegistered("Diplo_GridViewVars"))
{
Page.ClientScript.RegisterClientScriptBlock(GetType(), "Diplo_GridViewVars", vars, true);
}
Page.ClientScript.RegisterClientScriptInclude("Diplo_GridView.js",
Page.ClientScript.GetWebResourceUrl(GetType(), "Diplo.WebControls.SharedWebResources.Diplo_GridView_Resize.js"));
}
base.OnPreRender(e);
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowCreated"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Web.UI.WebControls.GridViewRowEventArgs"></see> that contains event data.</param>
protected override void OnRowCreated(GridViewRowEventArgs e)
{
if (EnableSortGraphic)
{
if (!((e.Row == null)) && e.Row.RowType == DataControlRowType.Header)
{
foreach (TableCell cell in e.Row.Cells)
{
if (cell.HasControls())
{
LinkButton button = ((LinkButton)(cell.Controls[0]));
if (!((button == null)))
{
Image image = new Image();
image.ImageUrl = "images/default.gif";
image.ImageAlign = ImageAlign.Baseline;
if (SortExpression == button.CommandArgument)
{
image.ImageUrl = SortDirection == SortDirection.Ascending ? SortAscendingImage : SortDescendingImage;
Literal space = new Literal();
space.Text = " ";
cell.Controls.Add(space);
cell.Controls.Add(image);
}
}
}
}
}
}
if (RowStyleHighlightColour != Color.Empty)
{
if (e.Row != null)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes.Add("onmouseover", String.Format("this.style.backgroundColor='{0}'", ColorTranslator.ToHtml(RowStyleHighlightColour)));
e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor=''");
}
}
}
base.OnRowCreated(e);
}
/// <summary>
/// Creates the control hierarchy that is used to render a composite data-bound control based on the values that are stored in view state.
/// </summary>
protected override void CreateChildControls()
{
base.CreateChildControls();
CheckShowPager();
}
private void CheckShowPager()
{
if (CustomPagerSettingsMode != CustomPagerMode.None && AllowPaging)
{
if (TopPagerRow != null)
{
TopPagerRow.Visible = true;
}
if (BottomPagerRow != null)
{
BottomPagerRow.Visible = true;
}
}
}
/// <summary>
/// Creates the control hierarchy used to render the <see cref="T:System.Web.UI.WebControls.GridView"></see> control using the specified data source.
/// </summary>
/// <param name="dataSource">An <see cref="T:System.Collections.IEnumerable"></see> that contains the data source for the <see cref="T:System.Web.UI.WebControls.GridView"></see> control.</param>
/// <param name="dataBinding">true to indicate that the child controls are bound to data; otherwise, false.</param>
/// <returns>The number of rows created.</returns>
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
int i = base.CreateChildControls(dataSource, dataBinding);
CheckShowPager();
return i;
}
#endregion Overridden Events
}
}
Then there is a custom paging class that is used as a paging template:
using System;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI;
namespace Diplo.WebControls.DataControls.PagerTemplates
{
/// <summary>
/// Paging template for the <see cref="DiploGridView"/>
/// </summary>
public class RowsPagePreviousNext : ITemplate
{
readonly PagedDataSource _pagedDataSource;
readonly DiploGridView DiploGridView;
/// <summary>
/// Initializes a new instance of the <see cref="RowsPagePreviousNext"/> class.
/// </summary>
/// <param name="pagedDataSource">The <see cref="PagedDataSource"/>.</param>
/// <param name="DiploGrid">A reference to the <see cref="DiploGridView"/>.</param>
public RowsPagePreviousNext(PagedDataSource pagedDataSource, DiploGridView DiploGrid)
{
_pagedDataSource = pagedDataSource;
DiploGridView = DiploGrid;
}
/// <summary>
/// When implemented by a class, defines the <see cref="T:System.Web.UI.Control"></see> object that child controls and templates belong to. These child controls are in turn defined within an inline template.
/// </summary>
/// <param name="container">The <see cref="T:System.Web.UI.Control"></see> object to contain the instances of controls from the inline template.</param>
void ITemplate.InstantiateIn(Control container)
{
Literal space = new Literal();
space.Text = " ";
HtmlGenericControl divLeft = new HtmlGenericControl("div");
divLeft.Style.Add("float", "left");
divLeft.Style.Add(HtmlTextWriterStyle.Width, "25%");
Label lb = new Label();
lb.Text = "Show rows: ";
divLeft.Controls.Add(lb);
DropDownList ddlPageSize = new DropDownList();
ListItem item;
ddlPageSize.AutoPostBack = true;
ddlPageSize.ToolTip = "Select number of rows per page";
int max = (_pagedDataSource.DataSourceCount < 50) ? _pagedDataSource.DataSourceCount : 50;
int i;
const int increment = 5;
bool alreadySelected = false;
for (i = increment; i <= max; i = i + increment)
{
item = new ListItem(i.ToString());
if (i == _pagedDataSource.PageSize)
{
item.Selected = true;
alreadySelected = true;
}
ddlPageSize.Items.Add(item);
}
item = new ListItem("All", _pagedDataSource.DataSourceCount.ToString());
if (_pagedDataSource.DataSourceCount == _pagedDataSource.PageSize && alreadySelected == false)
{
item.Selected = true;
alreadySelected = true;
}
if (_pagedDataSource.DataSourceCount > (i - increment) && alreadySelected == false)
{
item.Selected = true;
}
ddlPageSize.Items.Add(item);
ddlPageSize.SelectedIndexChanged += new EventHandler(ddlPageSize_SelectedIndexChanged);
divLeft.Controls.Add(ddlPageSize);
HtmlGenericControl divRight = new HtmlGenericControl("div");
divRight.Style.Add("float", "right");
divRight.Style.Add(HtmlTextWriterStyle.Width, "75%");
divRight.Style.Add(HtmlTextWriterStyle.TextAlign, "right");
Literal lit = new Literal();
lit.Text = String.Format("Found {0} record{1}. Page ",
_pagedDataSource.DataSourceCount,
(_pagedDataSource.DataSourceCount == 1) ? String.Empty : "s" );
divRight.Controls.Add(lit);
TextBox tbPage = new TextBox();
tbPage.ToolTip = "Enter page number";
tbPage.Columns = 2;
tbPage.MaxLength = 3;
tbPage.Text = (_pagedDataSource.CurrentPageIndex + 1).ToString();
tbPage.CssClass = "pagerTextBox";
tbPage.AutoPostBack = true;
tbPage.TextChanged += new EventHandler(tbPage_TextChanged);
divRight.Controls.Add(tbPage);
if (_pagedDataSource.PageCount < 2)
tbPage.Enabled = false;
lit = new Literal();
lit.Text = " of " + _pagedDataSource.PageCount;
divRight.Controls.Add(lit);
divRight.Controls.Add(space);
Button btn = new Button();
btn.Text = "";
btn.CommandName = "Page";
btn.CommandArgument = "Prev";
btn.SkinID = "none";
btn.Enabled = !_pagedDataSource.IsFirstPage;
btn.CssClass = (btn.Enabled) ? "buttonPreviousPage" : "buttonPreviousPageDisabled";
if (btn.Enabled)
btn.ToolTip = "Previous page";
divRight.Controls.Add(btn);
btn = new Button();
btn.Text = "";
btn.CommandName = "Page";
btn.CommandArgument = "Next";
btn.SkinID = "none";
btn.CssClass = "buttonNext";
btn.Enabled = !_pagedDataSource.IsLastPage;
btn.CssClass = (btn.Enabled) ? "buttonNextPage" : "buttonNextPageDisabled";
if (btn.Enabled)
btn.ToolTip = "Next page";
divRight.Controls.Add(btn);
container.Controls.Add(divLeft);
container.Controls.Add(divRight);
}
/// <summary>
/// Handles the TextChanged event of the tbPage control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
void tbPage_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
int page;
if (int.TryParse(tb.Text, out page))
{
if (page <= _pagedDataSource.PageCount && page > 0)
{
DiploGridView.PageIndex = page - 1;
}
}
}
}
/// <summary>
/// Handles the SelectedIndexChanged event of the ddlPageSize control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList list = sender as DropDownList;
if (list != null) DiploGridView.PageSize = Convert.ToInt32(list.SelectedValue);
}
}
}
I can't really talk you through it as server controls are complex, I just hope it gives you some help.

How to slow down or stop key presses in XNA

I've begun writing a game using XNA Framework and have hit some simple problem I do not know how to solve correctly.
I'm displaying a menu using Texture2D and using the keyboard (or gamepad) I change the menu item that is selected. My problem is that the current function used to toggle between menu items is way too fast. I might click the down button and it will go down 5 or 6 menu items (due to the fact that Update() is called many time thus updating the selected item).
ex.
(> indicate selected)
> MenuItem1
MenuItem2
MenuItem3
MenuItem4
MenuItem5
I press the down key for just a second), then I have this state:
MenuItem1
MenuItem2
MenuItem3
> MenuItem4
MenuItem5
What I want is (until I press the key again)
MenuItem1
> MenuItem2
MenuItem3
MenuItem4
MenuItem5
What I am looking for is a way to either have the player click the up/down key many time in order to go from one menu item to the other, or to have some sort of minimum wait time before going to the next menu item.
the best way to implement this is to cache the keyboard/gamepad state from the update statement that just passed.
KeyboardState oldState;
...
var newState = Keyboard.GetState();
if (newState.IsKeyDown(Keys.Down) && !oldState.IsKeyDown(Keys.Down))
{
// the player just pressed down
}
else if (newState.IsKeyDown(Keys.Down) && oldState.IsKeyDown(Keys.Down))
{
// the player is holding the key down
}
else if (!newState.IsKeyDown(Keys.Down) && oldState.IsKeyDown(Keys.Down))
{
// the player was holding the key down, but has just let it go
}
oldState = newState;
In your case, you probably only want to move "down" in the first case above, when the key was just pressed.
I've built a (large) class that helps a lot with any and all XNA input related tasks, it makes what you're asking for easy.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace YourNamespaceHere
{
/// <summary>
/// an enum of all available mouse buttons.
/// </summary>
public enum MouseButtons
{
LeftButton,
MiddleButton,
RightButton,
ExtraButton1,
ExtraButton2
}
public class InputHelper
{
private GamePadState _lastGamepadState;
private GamePadState _currentGamepadState;
#if (!XBOX)
private KeyboardState _lastKeyboardState;
private KeyboardState _currentKeyboardState;
private MouseState _lastMouseState;
private MouseState _currentMouseState;
#endif
private PlayerIndex _index = PlayerIndex.One;
private bool refreshData = false;
/// <summary>
/// Fetches the latest input states.
/// </summary>
public void Update()
{
if (!refreshData)
refreshData = true;
if (_lastGamepadState == null && _currentGamepadState == null)
{
_lastGamepadState = _currentGamepadState = GamePad.GetState(_index);
}
else
{
_lastGamepadState = _currentGamepadState;
_currentGamepadState = GamePad.GetState(_index);
}
#if (!XBOX)
if (_lastKeyboardState == null && _currentKeyboardState == null)
{
_lastKeyboardState = _currentKeyboardState = Keyboard.GetState();
}
else
{
_lastKeyboardState = _currentKeyboardState;
_currentKeyboardState = Keyboard.GetState();
}
if (_lastMouseState == null && _currentMouseState == null)
{
_lastMouseState = _currentMouseState = Mouse.GetState();
}
else
{
_lastMouseState = _currentMouseState;
_currentMouseState = Mouse.GetState();
}
#endif
}
/// <summary>
/// The previous state of the gamepad.
/// Exposed only for convenience.
/// </summary>
public GamePadState LastGamepadState
{
get { return _lastGamepadState; }
}
/// <summary>
/// the current state of the gamepad.
/// Exposed only for convenience.
/// </summary>
public GamePadState CurrentGamepadState
{
get { return _currentGamepadState; }
}
/// <summary>
/// the index that is used to poll the gamepad.
/// </summary>
public PlayerIndex Index
{
get { return _index; }
set {
_index = value;
if (refreshData)
{
Update();
Update();
}
}
}
#if (!XBOX)
/// <summary>
/// The previous keyboard state.
/// Exposed only for convenience.
/// </summary>
public KeyboardState LastKeyboardState
{
get { return _lastKeyboardState; }
}
/// <summary>
/// The current state of the keyboard.
/// Exposed only for convenience.
/// </summary>
public KeyboardState CurrentKeyboardState
{
get { return _currentKeyboardState; }
}
/// <summary>
/// The previous mouse state.
/// Exposed only for convenience.
/// </summary>
public MouseState LastMouseState
{
get { return _lastMouseState; }
}
/// <summary>
/// The current state of the mouse.
/// Exposed only for convenience.
/// </summary>
public MouseState CurrentMouseState
{
get { return _currentMouseState; }
}
#endif
/// <summary>
/// The current position of the left stick.
/// Y is automatically reversed for you.
/// </summary>
public Vector2 LeftStickPosition
{
get
{
return new Vector2(
_currentGamepadState.ThumbSticks.Left.X,
-CurrentGamepadState.ThumbSticks.Left.Y);
}
}
/// <summary>
/// The current position of the right stick.
/// Y is automatically reversed for you.
/// </summary>
public Vector2 RightStickPosition
{
get
{
return new Vector2(
_currentGamepadState.ThumbSticks.Right.X,
-_currentGamepadState.ThumbSticks.Right.Y);
}
}
/// <summary>
/// The current velocity of the left stick.
/// Y is automatically reversed for you.
/// expressed as:
/// current stick position - last stick position.
/// </summary>
public Vector2 LeftStickVelocity
{
get
{
Vector2 temp =
_currentGamepadState.ThumbSticks.Left -
_lastGamepadState.ThumbSticks.Left;
return new Vector2(temp.X, -temp.Y);
}
}
/// <summary>
/// The current velocity of the right stick.
/// Y is automatically reversed for you.
/// expressed as:
/// current stick position - last stick position.
/// </summary>
public Vector2 RightStickVelocity
{
get
{
Vector2 temp =
_currentGamepadState.ThumbSticks.Right -
_lastGamepadState.ThumbSticks.Right;
return new Vector2(temp.X, -temp.Y);
}
}
/// <summary>
/// the current position of the left trigger.
/// </summary>
public float LeftTriggerPosition
{
get { return _currentGamepadState.Triggers.Left; }
}
/// <summary>
/// the current position of the right trigger.
/// </summary>
public float RightTriggerPosition
{
get { return _currentGamepadState.Triggers.Right; }
}
/// <summary>
/// the velocity of the left trigger.
/// expressed as:
/// current trigger position - last trigger position.
/// </summary>
public float LeftTriggerVelocity
{
get
{
return
_currentGamepadState.Triggers.Left -
_lastGamepadState.Triggers.Left;
}
}
/// <summary>
/// the velocity of the right trigger.
/// expressed as:
/// current trigger position - last trigger position.
/// </summary>
public float RightTriggerVelocity
{
get
{
return _currentGamepadState.Triggers.Right -
_lastGamepadState.Triggers.Right;
}
}
#if (!XBOX)
/// <summary>
/// the current mouse position.
/// </summary>
public Vector2 MousePosition
{
get { return new Vector2(_currentMouseState.X, _currentMouseState.Y); }
}
/// <summary>
/// the current mouse velocity.
/// Expressed as:
/// current mouse position - last mouse position.
/// </summary>
public Vector2 MouseVelocity
{
get
{
return (
new Vector2(_currentMouseState.X, _currentMouseState.Y) -
new Vector2(_lastMouseState.X, _lastMouseState.Y)
);
}
}
/// <summary>
/// the current mouse scroll wheel position.
/// See the Mouse's ScrollWheel property for details.
/// </summary>
public float MouseScrollWheelPosition
{
get
{
return _currentMouseState.ScrollWheelValue;
}
}
/// <summary>
/// the mouse scroll wheel velocity.
/// Expressed as:
/// current scroll wheel position -
/// the last scroll wheel position.
/// </summary>
public float MouseScrollWheelVelocity
{
get
{
return (_currentMouseState.ScrollWheelValue - _lastMouseState.ScrollWheelValue);
}
}
#endif
/// <summary>
/// Used for debug purposes.
/// Indicates if the user wants to exit immediately.
/// </summary>
public bool ExitRequested
{
#if (!XBOX)
get
{
return (
(IsCurPress(Buttons.Start) &&
IsCurPress(Buttons.Back)) ||
IsCurPress(Keys.Escape));
}
#else
get { return (IsCurPress(Buttons.Start) && IsCurPress(Buttons.Back)); }
#endif
}
/// <summary>
/// Checks if the requested button is a new press.
/// </summary>
/// <param name="button">
/// The button to check.
/// </param>
/// <returns>
/// a bool indicating whether the selected button is being
/// pressed in the current state but not the last state.
/// </returns>
public bool IsNewPress(Buttons button)
{
return (
_lastGamepadState.IsButtonUp(button) &&
_currentGamepadState.IsButtonDown(button));
}
/// <summary>
/// Checks if the requested button is a current press.
/// </summary>
/// <param name="button">
/// the button to check.
/// </param>
/// <returns>
/// a bool indicating whether the selected button is being
/// pressed in the current state and in the last state.
/// </returns>
public bool IsCurPress(Buttons button)
{
return (
_lastGamepadState.IsButtonDown(button) &&
_currentGamepadState.IsButtonDown(button));
}
/// <summary>
/// Checks if the requested button is an old press.
/// </summary>
/// <param name="button">
/// the button to check.
/// </param>
/// <returns>
/// a bool indicating whether the selected button is not being
/// pressed in the current state and is being pressed in the last state.
/// </returns>
public bool IsOldPress(Buttons button)
{
return (
_lastGamepadState.IsButtonDown(button) &&
_currentGamepadState.IsButtonUp(button));
}
#if (!XBOX)
/// <summary>
/// Checks if the requested key is a new press.
/// </summary>
/// <param name="key">
/// the key to check.
/// </param>
/// <returns>
/// a bool that indicates whether the selected key is being
/// pressed in the current state and not in the last state.
/// </returns>
public bool IsNewPress(Keys key)
{
return (
_lastKeyboardState.IsKeyUp(key) &&
_currentKeyboardState.IsKeyDown(key));
}
/// <summary>
/// Checks if the requested key is a current press.
/// </summary>
/// <param name="key">
/// the key to check.
/// </param>
/// <returns>
/// a bool that indicates whether the selected key is being
/// pressed in the current state and in the last state.
/// </returns>
public bool IsCurPress(Keys key)
{
return (
_lastKeyboardState.IsKeyDown(key) &&
_currentKeyboardState.IsKeyDown(key));
}
/// <summary>
/// Checks if the requested button is an old press.
/// </summary>
/// <param name="key">
/// the key to check.
/// </param>
/// <returns>
/// a bool indicating whether the selectde button is not being
/// pressed in the current state and being pressed in the last state.
/// </returns>
public bool IsOldPress(Keys key)
{
return (
_lastKeyboardState.IsKeyDown(key) &&
_currentKeyboardState.IsKeyUp(key));
}
/// <summary>
/// Checks if the requested mosue button is a new press.
/// </summary>
/// <param name="button">
/// teh mouse button to check.
/// </param>
/// <returns>
/// a bool indicating whether the selected mouse button is being
/// pressed in the current state but not in the last state.
/// </returns>
public bool IsNewPress(MouseButtons button)
{
switch (button)
{
case MouseButtons.LeftButton:
return (
_lastMouseState.LeftButton == ButtonState.Released &&
_currentMouseState.LeftButton == ButtonState.Pressed);
case MouseButtons.MiddleButton:
return (
_lastMouseState.MiddleButton == ButtonState.Released &&
_currentMouseState.MiddleButton == ButtonState.Pressed);
case MouseButtons.RightButton:
return (
_lastMouseState.RightButton == ButtonState.Released &&
_currentMouseState.RightButton == ButtonState.Pressed);
case MouseButtons.ExtraButton1:
return (
_lastMouseState.XButton1 == ButtonState.Released &&
_currentMouseState.XButton1 == ButtonState.Pressed);
case MouseButtons.ExtraButton2:
return (
_lastMouseState.XButton2 == ButtonState.Released &&
_currentMouseState.XButton2 == ButtonState.Pressed);
default:
return false;
}
}
/// <summary>
/// Checks if the requested mosue button is a current press.
/// </summary>
/// <param name="button">
/// the mouse button to be checked.
/// </param>
/// <returns>
/// a bool indicating whether the selected mouse button is being
/// pressed in the current state and in the last state.
/// </returns>
public bool IsCurPress(MouseButtons button)
{
switch (button)
{
case MouseButtons.LeftButton:
return (
_lastMouseState.LeftButton == ButtonState.Pressed &&
_currentMouseState.LeftButton == ButtonState.Pressed);
case MouseButtons.MiddleButton:
return (
_lastMouseState.MiddleButton == ButtonState.Pressed &&
_currentMouseState.MiddleButton == ButtonState.Pressed);
case MouseButtons.RightButton:
return (
_lastMouseState.RightButton == ButtonState.Pressed &&
_currentMouseState.RightButton == ButtonState.Pressed);
case MouseButtons.ExtraButton1:
return (
_lastMouseState.XButton1 == ButtonState.Pressed &&
_currentMouseState.XButton1 == ButtonState.Pressed);
case MouseButtons.ExtraButton2:
return (
_lastMouseState.XButton2 == ButtonState.Pressed &&
_currentMouseState.XButton2 == ButtonState.Pressed);
default:
return false;
}
}
/// <summary>
/// Checks if the requested mosue button is an old press.
/// </summary>
/// <param name="button">
/// the mouse button to check.
/// </param>
/// <returns>
/// a bool indicating whether the selected mouse button is not being
/// pressed in the current state and is being pressed in the old state.
/// </returns>
public bool IsOldPress(MouseButtons button)
{
switch (button)
{
case MouseButtons.LeftButton:
return (
_lastMouseState.LeftButton == ButtonState.Pressed &&
_currentMouseState.LeftButton == ButtonState.Released);
case MouseButtons.MiddleButton:
return (
_lastMouseState.MiddleButton == ButtonState.Pressed &&
_currentMouseState.MiddleButton == ButtonState.Released);
case MouseButtons.RightButton:
return (
_lastMouseState.RightButton == ButtonState.Pressed &&
_currentMouseState.RightButton == ButtonState.Released);
case MouseButtons.ExtraButton1:
return (
_lastMouseState.XButton1 == ButtonState.Pressed &&
_currentMouseState.XButton1 == ButtonState.Released);
case MouseButtons.ExtraButton2:
return (
_lastMouseState.XButton2 == ButtonState.Pressed &&
_currentMouseState.XButton2 == ButtonState.Released);
default:
return false;
}
}
#endif
}
}
Just copy it into a separate class fie and move it to your namespace, then declare one (inputHelper variable), initialize it in the initialiaze portion, and call inputHelper.Update() in your update loop before the update logic. Then whenever you need something related to input, just use the InputHelper! for instance, in your situation, you would use InputHelper.IsNewPress([type of input button/key here]) to check if you want to move the menu item down or up. For this example: inputHelper.IsNewPress(Keys.Down)
A nice way of dealing with this kind of thing is to store a counter for each key you're interested in, which you increment every frame if the key is down, and reset to 0 if it's up.
The advantage of this is that you can then test for both the absolute state of the key (if the counter is non-zero the key is down) and also easily check if it's just been pressed this frame for menus and the like (counter is 1). Plus key repeat becomes easy (counter % repeat delay is zero).
If your application is for a Windows machine, I've had great success using this event driven class that I found here: gamedev.net forum post
It handles typical key presses and the short pauses before the repeating starts, just like normal text entry in a Windows application. Also included is mouse move/wheel events.
You can subscribe to the events, for example, using the following code:
InputSystem.KeyDown += new KeyEventHandler(KeyDownFunction);
InputSystem.KeyUp += new KeyEventHandler(KeyUpFunction);
Then in the methods themselves:
void KeyDownFunction(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.F)
facepalm();
}
void KeyUpFunction(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.F)
release();
}
...and so forth. It really is a great class. I've found it's flexibility much improved over XNA's default keyboard handling. Good Luck!
I thought the previous answers were a bit over-complicated, so I'm giving this one here...
Copy the KeyPress class below in a new file, declare the KeyPress variables, initialize them in your Initialize() method. From there you can do if ([yourkey].IsPressed()) ...
Note: this answer works only for Keyboard input, but it should be easily ported to Gamepad or any other input. I think keeping the code for the different types of input separate is better.
public class KeyPress
{
public KeyPress(Keys Key)
{
key = Key;
isHeld = false;
}
public bool IsPressed { get { return isPressed(); } }
public static void Update() { state = Keyboard.GetState(); }
private Keys key;
private bool isHeld;
private static KeyboardState state;
private bool isPressed()
{
if (state.IsKeyDown(key))
{
if (isHeld) return false;
else
{
isHeld = true;
return true;
}
}
else
{
if (isHeld) isHeld = false;
return false;
}
}
}
Usage:
// Declare variable
KeyPress escape;
// Initialize()
escape = new KeyPress(Keys.Escape)
// Update()
KeyPress.Update();
if (escape.IsPressed())
...
I might be wrong, but I think my answer is easier on resources than the accepted answer and also more readable!
You could store in integer value time from last key pressed (left,right...) and if this time is bigger than some limit you could poll for new key pressed. However this could be done only for menu, because in-game you would need that information immediately.
What you can also do is make yourself a functions combining KyeUp and KeyDown that tells you when the key has been pressed once, only in 1 loop of the update, so that it only works everytime you press the key again.
Ok, I've figured it out. First, I added a
private Keys keyPressed = Keys.None;
and in my Update() method, I do the following:
KeyboardState keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyUp(keyPressed))
{
keyPressed = Keys.None;
}
if (keyboardState.IsKeyDown(keyPressed))
{
return;
}
// Some additionnal stuff is done according to direction
if (keyboardState.IsKeyDown(Keys.Up))
{
keyPressed = Keys.Up;
}
else if (keyboardState.IsKeyDown(Keys.Down))
{
keyPressed = Keys.Down;
}
It seems to be working correctly.
I save the GamePadState and KeyboardState from the previous update run. At the next update run i check for buttons that were not pressed last run, but are pressed now. Then I save the current state.
I have all of this wrapped up in a static class that I can use to query for specific buttons and/or get a list of pressed buttons since the last update. This makes it really easy to work with multiple keys simultaneously (something you certainly want in games) and it would be trivially extensible to chords.
Ranieri, what does that look like? I am having a hard time juggling these update cycles...
Hrmmm...
public static bool CheckKeyPress(Keys key)
{
return keyboardState.IsKeyUp(key) && lastKeyboardState.IsKeyDown(key);
}
SetStates() is private and it is called in the Update()
private static void SetStates()
{
lastKeyboardState = keyboardState;
keyboardState = Keyboard.GetState();
}
Here is the update...
public sealed override void Update(GameTime gameTime)
{
// Called to set the states of the input devices
SetStates();
base.Update(gameTime);
}
I've tried adding extra checks..
if (Xin.CheckKeyPress(Keys.Enter) ||
Xin.CheckButtonPress(Buttons.A))
{
if (Xin.LastKeyboardState != Xin.KeyboardState ||
Xin.LastGamePadState(PlayerIndex.One) != Xin.GamePadState(PlayerIndex.One))
{
doesn't seem to have any noticeable effects - I can't seem to slow down the menu confirmations,
Well what you could do is something like this (Also will track every key)
int[] keyVals;
TimeSpan pressWait = new TimeSpan(0, 0, 1);
Dictionary<Keys, bool> keyDowns = new Dictionary<Keys, bool>();
Dictionary<Keys, DateTime> keyTimes = new Dictionary<Keys, DateTime>();
public ConstructorNameHere
{
keyVals = Enum.GetValues(typeof(Keys)) as int[];
foreach (int k in keyVals)
{
keyDowns.Add((Keys)k, false);
keyTimes.Add((Keys)k, new DateTime()+ new TimeSpan(1,0,0));
}
}
protected override void Update(GameTime gameTime)
{
foreach (int i in keyVals)
{
Keys key = (Keys)i;
switch (key)
{
case Keys.Enter:
keyTimes[key] = (Keyboard.GetState().IsKeyUp(key)) ? ((keyDowns[key]) ? DateTime.Now + pressWait : keyTimes[key]) : keyTimes[key];
keyDowns[key] = (keyTimes[key] > DateTime.Now) ? false : Keyboard.GetState().IsKeyDown(key);
if (keyTimes[key] < DateTime.Now)
{
// Code for what happens when Keys.Enter is pressed goes here.
}
break;
}
}
By doing it this way you're able to check every key. You could also do this for just every key by create separate DateTimes and seperate bool values.
I know this is old, but how about:
Add a threadsafe dictionary:
private ConcurrentDictionary<Keys, DateTime> _keyBounceDict = new ConcurrentDictionary<Keys, DateTime>();
Then use this method to track the keys pressed and determine if there is a key bounce:
///////////////////////////////////////////////////////////////////////////////////////////
/// IsNotKeyBounce - determines if a key is bouncing and therefore not valid within
/// a certain "delay" period
///////////////////////////////////////////////////////////////////////////////////////////
private bool IsNotKeyBounce(Keys thekey, double delay)
{
bool OKtoPress = true;
if (_keyBounceDict.ContainsKey(thekey))
{
TimeSpan ts = DateTime.Now - _keyBounceDict[thekey];
if (ts.TotalMilliseconds < _tsKeyBounceTiming)
{
OKtoPress = false;
}
else
{
DateTime dummy;
_keyBounceDict.TryRemove(thekey, out dummy);
}
}
else
{
_keyBounceDict.AddOrUpdate(thekey, DateTime.Now, (key, oldValue) => oldValue);
}
return OKtoPress;
}
Here is what I put in my Update method:
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
if (IsNotKeyBounce(Keys.W, 50.0)) _targetNew.Distance *= 1.1f;
}
I use 50 ms, but you could use whatever makes sense for your app or tie it to GameTime or whatever...

Categories