I have implemented an undo system based on the Memento pattern. I disable the built in Undo on TextBox and was wondering how to do this on a ComboBox. The Combobox I have is editable, so it contains a TextBox, how do I access this to disable the Undo on it as well.
I know I can derive from ComboBox add a property and override the control template and set the property on the TextBox, but I would like a way to do this on the standard ComboBox from the xaml.
You can look it up from the template like this:
public Window1()
{
this.InitializeComponent();
comboBox1.Loaded += new RoutedEventHandler(comboBox1_Loaded);
}
void comboBox1_Loaded(object sender, RoutedEventArgs e)
{
var textBox = comboBox1.Template.FindName("PART_EditableTextBox", comboBox1) as TextBox;
}
I know this is 3+ years old but maybe it'll help someone. It is basically Rick's answer as a Behavoir that decyclone mentioned:
public class ComboBoxDisableUndoBehavoir : Behavior<ComboBox>
{
public ComboBoxDisableUndoBehavoir()
{
}
protected override void OnAttached()
{
if (AssociatedObject != null)
{
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
base.OnAttached();
}
void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var tb = AssociatedObject.Template.FindName("PART_EditableTextBox", AssociatedObject) as TextBox;
if (tb != null)
{
tb.IsUndoEnabled = false;
}
}
}
Related
I have a custom WPF TextBox User Control. Im writing some content, but it always detects the same data that was preset on the XAML, new written text is not shown by doing textbox.Text get call.
public partial class SearchBox : UserControl
{
public SearchBox()
{
InitializeComponent();
this.DataContext = this;
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox textBox && textBox.Tag.ToString() == textBox.Text)
textBox.Clear();
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox textBox && string.IsNullOrEmpty(textBox.Text))
textBox.Text = textBox.Tag.ToString();
}
public string Text { get; set; }
public event TextChangedEventHandler TextChanged;
private void TextBox_TextChanged(object sender, TextChangedEventArgs args)
{
TextChangedEventHandler h = TextChanged;
if (h != null)
{
h(this, args);
}
}
}
How it needs to work:
<v:SearchBox x:Name="searchBox" TextChanged="searchBox_TextChanged" Text="Input a keyword to filter..." Tag="Input a keyword to filter..." HorizontalAlignment="Left"/>
User writes on textbox "test" --> textbox.Text returns "Input a keyword to filter..."
Thanks for the help!
Your class inherits from UserControl and not TextBox.
Though you haven't shared your XAML for SearchBox, I assume you have a TextBox in it, which you are hoping to get the text from. Problem is, the property SearchBox.Text is different from SearchBox.TextBox.Text. As a result, even if you change the text in the TextBox, SearchBox.Text remains the same.
Solution: use SearchBox.TextBox.Text for your result.
i try to use the new Behavior-Feature in UAP. I use this Behavior:
public sealed class AutoScrollToLastItemBehavior : Behavior<ListView>
{
private bool _collectionChangedSubscribed;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += SelectionChanged;
AssociatedObject.DataContextChanged += DataContextChanged;
}
private void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ScrollToBottom();
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ScrollToBottom();
}
private void DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (collection == null || _collectionChangedSubscribed) return;
collection.CollectionChanged += CollectionChanged;
_collectionChangedSubscribed = true;
}
private void ScrollToBottom()
{
var selectedIndex = AssociatedObject.Items?.Count - 1;
if (!selectedIndex.HasValue || selectedIndex < 0)
return;
AssociatedObject.SelectedIndex = selectedIndex.Value;
AssociatedObject.UpdateLayout();
AssociatedObject.ScrollIntoView(AssociatedObject.SelectedItem);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= SelectionChanged;
AssociatedObject.DataContextChanged -= DataContextChanged;
var collection = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (collection == null || !_collectionChangedSubscribed) return;
collection.CollectionChanged -= CollectionChanged;
_collectionChangedSubscribed = false;
}
}
This code works well. But i would like that the AutoScrolling stoped when user interact with the ListView. I found some sampels but most of them based on WPF and some of the functions or properties are not present in UWP.
So actual i dont finde a way to implementate a AutoScroll that stop working if the User scrolls by it self. Have someone maybe an idear for this?
Greetings
It really depends on what you mean by "user interacting with a ListView". If you mean someone scrolling manually - you can check the ViewChanged event or VerticalOffset of the ScrollViewer to see if it's scrolled to the bottom before collection change. Other interactions are likely things that are custom to your app, so you would have to detect them yourself. You need to access the ScrollViewer from the ListView template first before you look at these details.
I extended the button control to have also LabelName. When I press the button I need to write the name of the button in the label.
My first idea was using events - easy and simple.
The question is: Is there more elegant way to do it? (I've been asked to bind the button and the label)...
I think that the best way to do it would be to use an action listener and the best way to use the action listener would be to build it into your class that extends the button control so that the user doesn't have to do this on their own. It would look like this.
class Button2 : Button
{
public string LabelName = "";
public Button2()
{
this.Click += this.SetLabelName;
}
private void SetLabelName(object sender, EventArgs e)
{
this.LabelName = "Something?";
}
//You could also do this instead.
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
}
}
If you're talking about changing the Text property of an external Label control, then simply create a property in your Button to hold a reference to a Label. You can set this via the IDE like any other property:
Here's the Button class:
public class MyButton : Button
{
private Label _Label = null;
public Label Label
{
get { return _Label; }
set { _Label = value; }
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if (this.Label != null)
{
this.Label.Text = this.Name;
}
}
}
Here's the Label after I clicked the Button:
I have a TextChanged event on my ComboBox like;
private void comboBox1_TextChanged(object sender, EventArgs e)
{
foreach (var item in comboBox1.Items.Cast<string>().ToList())
{
comboBox1.Items.Remove(item);
}
foreach (string item in InputBox.AutoCompleteCustomSource.Cast<string>().Where(s => s.Contains(comboBox1.Text)).ToList())
{
comboBox1.Items.Add(item);
}
}
As an explanation, when I change the text of combobox, I want to get string values contains in AutoCompleteCustomSource on InputBox (which is TextBox).
It works fine when I search them but when I select the item, obviously TextChanged event triggered again and Text property of Combobox will reset.
How to solve this?
If I understood correctly then i think you want to hide the TextChange event of the combobox. If it is then you can create a custom control inherited by ComboBox and override the TextChange event.
public partial class MyCombo : ComboBox
{
public MyCombo()
{
InitializeComponent();
}
bool bFalse = false;
protected override void OnTextChanged(EventArgs e)
{
//Here you can handle the TextChange event if want to suppress it
//just place the base.OnTextChanged(e); line inside the condition
if (!bFalse)
base.OnTextChanged(e);
}
protected override void OnSelectionChangeCommitted(EventArgs e)
{
bFalse = true;
base.OnSelectionChangeCommitted(e);
}
protected override void OnTextUpdate(EventArgs e)
{
base.OnTextUpdate(e);
bFalse = false; //this event will be fire when user types anything. but, not when user selects item from the list.
}
}
EDITED:
Another simple soution is use TextUpdate event instead of TextChange and keep your combobox as it is without creating another custom control.
private void myCombo1_TextUpdate(object sender, EventArgs e)
{
foreach (var item in myCombo1.Items.Cast<string>().ToList())
{
myCombo1.Items.Remove(item);
}
foreach (string item in myCombo1.AutoCompleteCustomSource.Cast<string>().Where(s => s.Contains(myCombo1.Text)).ToList())
{
myCombo1.Items.Add(item);
}
}
TextUpdate event will call only when user types anything in combobox. But, not when user selects item from the drop down list. So, this will not resent the added items.
You can also change the where condition if you wish to return all matched items in both cases(Upper and Lower). Suppose you have a two items in the list 1. Microsoft Sql Server, 2. microsoft office then what would be the result if i type microsoft only.
Where(s => s.ToLower().Contains(comboBox1.Text.ToLower()))
Sample Code
As #Sayse already said:
Add a boolean:
private bool codeCalled = new bool();
In your textChanged:
if(codeCalled == true)
{
codeCalled = false;
return;
}
else
{
codeCalled = true;
//your foreachcode here
}
That should do the trick.
Tested and is working.
Also tested and working, also not elegant:
private void textBox_TextChanged(object sender, EventArgs e)
{
textBox.TextChanged -= textBox_TextChanged;
//yourcode
textBox.TextChanged += textBox_TextChanged;
}
I created some textboxes and I want user to enter decimal values into them. In every application I have ever used, when I type something into the textbox and hit enter, the value is accepted and textbox lose focus. How can I do it in my app?
I know it should be relatively easy to do it with a key event, but maybe there is a command or something.
I searched the stackoverflow but I only found questions about how to keep focus after hitting enter...
You can also create a generic behavior which can be easily applied to any textbox within your application. Here is a sample behavior class:-
public class TextBoxEnterKeyUpdateBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
if (this.AssociatedObject != null)
{
base.OnAttached();
this.AssociatedObject.KeyDown += AssociatedObject_KeyDown;
}
}
protected override void OnDetaching()
{
if (this.AssociatedObject != null)
{
this.AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
base.OnDetaching();
}
}
private void AssociatedObject_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
if (e.Key == Key.Return)
{
if (e.Key == Key.Enter)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
}
To use this class in your xaml, just include it in textbox behaviors collection like this :-
<TextBox>
<i:Interaction.Behaviors>
<TextBoxEnterKeyUpdateBehavior />
</i:Interaction.Behaviors>
</TextBox>
Here "i" refers to System.Windows.Interactivity namespace.