AutoCompleteBox.Text property is behaving weird - c#

I'm stuck on a strange problem.
I have an AutoCompleteBox in my view
<sdk:AutoCompleteBox x:Name="txtSIA"
Grid.ColumnSpan="1" Grid.Row="1" Grid.Column="1"
SelectedItem="{Binding SIA, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"
Text="{Binding TextSIA, Mode=TwoWay}"
KeyUp="TxtSIA_KeyUp"
Populating="SIANonSIU_Populating"
Style="{StaticResource AutoCompleteStyle}"
/>
I implemented a field validator that check if its text isn't null or an empty string.
It works pretty well but the tricky part is that I have a button that Reset all my controls values, which code from my viewmodel is:
void BtnReset_OnClick(RoutedEventArgs e)
{
SIA = new SIA();
TextSIA = string.Empty;
BtnGeneralIsEnabled = false;
DataGridSource = null;
}
Whenever I click it and then write in my AutoCompleteBox, the AutoCompleteBox is never empty or null even in my code behind in a key up event listener.
Here are some picture to illustrate my point:

Register to the TextChanged event of the AutoCompleteBox instead of the KeyUp:
"Occurs when the text in the text box portion of the AutoCompleteBox changes."

I found an answer here
To fix that but we have to create a new autocompletebox and override the OnApplyTemplate method.
public class CustomAutoComplete : AutoCompleteBox
{
TextBox mytext;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
mytext = GetTemplateChild("Text") as TextBox;
mytext.TextChanged += new System.Windows.Controls.TextChangedEventHandler(mytext_TextChanged);
}
void mytext_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
this.Text = mytext.Text;
OnTextChanged(new RoutedEventArgs());
}
}

Related

Deactivate SelectionChanged in a ComboBox

With a regular ComboBox I would use the following code to deactivate the Selection Change.
<ComboBox Name="CbbTest" SelectionChanged="CbbTest_SelectionChanged"></ComboBox>
CbbTest.SelectionChanged -= new SelectionChangedEventHandler(CbbTest_SelectionChanged);
However, when my ComboBox is in a DataTemplate I am not able to access the ComboBox by name, and therefore I am not able to turn off the selection changed.
How can deactivate ComboBox CbbTestTwo like in the previous code, but from a DataTemplate in the following code?
<StackPanel>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="CbbTestTwo" SelectionChanged="CbbTestTwo_SelectionChanged"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
Any help in this matter would be appreciated
If I understand correctly, you seek to be able to manipulate your combobox in the SelectionChanged event. You can get your combobox like this:
private void cbTest_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = new ComboBox();
if(sender is ComboBox)
{
comboBox = (ComboBox)sender;
}
}
Can now handle it like that :
private void cbTest_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = new ComboBox();
if(sender is ComboBox)
{
comboBox = (ComboBox)sender;
}
//Add the treatments you want
comboBox.Items.Clear();
comboBox.ItemsSource = listTest;
}
You might want to use more MVVM like approach which means subscribing directly on events in the code behind of the view is not the best option. In this case you could use a behavior like this.
public class ComboBoxSelectionChangedBehavior : Behavior<ComboBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += OnSelectionChanged;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ....
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= OnSelectionChanged;
}
}
And then you can attach this behavior through xaml. Here is a short tutorial on behaviors https://blog.jayway.com/2013/03/20/behaviors-in-wpf-introduction/
Find control inside Listbox.ItemTemplate (WPF C#)
Please refer this link, from the above code snippet, once you will have the combobox instance, you can unsubscribe the event.

Editable ComboBox selects first entered character

I am having issues with a ComboBoxselecting the first entered character, which then causes the a problem where the second entered character overwrites the first one.
EDIT: A small explanation of what I an trying to do.
I have set up the ComboBox to act as an autocomplete control. When I enter a character, I am using CollectionView class to filter any names that match each entered character. Upon entered text the ComboBox drop down menu needs to open up, which is why I am binding to IsDropDownOpen. This is how it is supposed to look here.
This is beyond me, I can't research what I need to do to stop this behavior.
Here is a screen shot of what I mean.
This is the ComboBox XAML:
<ComboBox Style="{StaticResource ComboBoxToggleHidden}"
DisplayMemberPath="FullName" SelectedValuePath="Key"
IsTextSearchEnabled="False"
IsEditable="True"
StaysOpenOnEdit="True"
Text="{Binding Path=EnteredText, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=Employees}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=SelectedEmployee}"
IsDropDownOpen="{Binding IsDropDown}">
</ComboBox>
EDIT: I have narrowed it down to this, IsDropDown = true;, commenting this out fixes the issue. But I need the drop down when editing the ComboBox
In the EnteredText property
private string _enteredText;
public string EnteredText
{
get { return _enteredText; }
set
{
_enteredText = value;
Filter(value);
IsDropDown = true;
OnPropertyChanged();
}
}
public bool IsDropDown { get; set; }
OK, I solved this doing a hack, but it will have to do until I can figure out why this behavior is happening.
I created an KeyUpEvent Event in the constructor,
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.KeyUpEvent,
new RoutedEventHandler(DeselectText));
Then in the Handler I just deselected the text.
private void DeselectText(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox == null) return;
if (textBox.Text.Length >= 2) return;
textBox.SelectionLength = 0;
textBox.SelectionStart = 1;
}
I know this is a hack, but I have no choice until the correct solution is posted.
This is how it looks with the hack.
Consider this as another solution. It mights resove the side effect of above mentioned problem.
I am expecting the TemplateChild to have the Name (PART_EditableTextBox). If you are changing the name in the Template then please do the necessary changes here as well.
private TextBox EditableTextBox => (TextBox)GetTemplateChild("PART_EditableTextBox");
protected override void OnDropDownOpened(EventArgs e)
{
EditableTextBox.Select(Text.Length, 0);
base.OnDropDownOpened(e);
}

WPF TextBox.Text doesn't update in another control

It is simple thing that works everywhere instead of TextBox.Text without any reasons. What I need is just to get updated Text property of textbox when DataContext changed. There is a DataGrid and another control behind it to show details about row. So when user click's on row I am obtain data object from grid's row and show its details in this details control. Details control contains just propertyName and value control. I.e. textblock and textbox. ANY property of textblock or textbox are updated on changing DataContext except Text in textbox. I've break my head already.
The main problem is textbox won't updated after I've changed it. And it works fine if move textbox outside control to the details control (parent)
The property-Value control:
<DockPanel x:Class="UserControls.PropertySectionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="_propertyDockPanel"
HorizontalAlignment="Stretch" Margin="0 5 0 0"
d:DesignHeight="300" d:DesignWidth="300">
<TextBlock Text="{Binding Path=Property, ElementName=_propertyDockPanel}" TextWrapping="Wrap"/>
<TextBox
Text="{Binding Path=Value, ElementName=_propertyDockPanel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="{Binding Path=IsReadOnly, ElementName=_propertyDockPanel}"
TextAlignment="Right" Foreground="Gray" TextWrapping="Wrap" Width="120" Height="20" HorizontalAlignment="Right" Margin="0 0 15 0"
ToolTip="{Binding Value, ElementName=_propertyDockPanel}" Template="{StaticResource _inspectorValueTemplate}"
LostFocus="_textBoxValue_LostFocus" GotFocus="_textBoxValue_LostFocus" KeyDown="_textBoxValue_KeyDown" TextChanged="_textBoxValue_TextChanged"
/>
</DockPanel>
And here is how it is used in details control:
<userControls:PropertySectionControl Property="Total" Value="{Binding OrderCharges.Total}" IsReadOnly="True"/>
As you can see, in PropertySectionControl there is also binding ToolTip to Value which is works on DataContext changed! But for TextBox.Text is not. What is this?
UPD:
PropertySectionControl.cs
public partial class PropertySectionControl : DockPanel
{
public string Property
{
get { return (string)GetValue(PropertyProperty); }
set { SetValue(PropertyProperty, value); }
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(PropertySectionControl), new PropertyMetadata(""));
public static readonly DependencyProperty PropertyProperty =
DependencyProperty.Register("Property", typeof(string), typeof(PropertySectionControl), new PropertyMetadata(""));
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(PropertySectionControl), new PropertyMetadata(false));
public PropertySectionControl()
{
InitializeComponent();
}
/// <summary>
/// Static event handler is for use in Inspector control as well
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void TextBox_LostOrGotFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox.IsFocused)
{
textBox.TextAlignment = TextAlignment.Left;
textBox.SelectAll();
}
else
{
textBox.TextAlignment = TextAlignment.Right;
}
}
public static void TextBox_KeyDown(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
if (e.Key == Key.Enter)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
else if (e.Key == Key.Escape)
{
int i = 0;
do
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
} while (!(Keyboard.FocusedElement is TextBoxBase) && ++i < 5); // prevent infinite loop
Keyboard.ClearFocus();
}
}
public static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
textBox.Height = 20;
for (int i = 2; i <= textBox.LineCount; i++)
textBox.Height += 14;
}
private void _textBoxValue_LostFocus(object sender, RoutedEventArgs e)
{
TextBox_LostOrGotFocus(sender, e);
}
private void _textBoxValue_KeyDown(object sender, KeyEventArgs e)
{
TextBox_KeyDown(sender, e);
}
private void _textBoxValue_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox_TextChanged(sender, e);
}
}
Just guessing:
1) That textbox has a textchanged eventhandler set. Check if that handler works OK. Try to comment it out ans see if it changes anything. I.e. maybe it gets fired during the data update from model and maybe it throws an exception and binding is aborted in the meantime?
1a) run in VS in Debug mode and check 'Output' window. See if there is anything reported like:
first chance exceptions
binding errors
important especially if they show up when you try editing the textbox for the first time
2) Also, whenever a binding magically stops working, be sure to check if the binding is still in place. I see you are using a direct access to the controls from code-behind (like textbox.Height += ..). This is an easy way to break the bindings. If you ever anywhere ran one of these lines:
textBox.Text = ""
textBox.Text = "foo"
textBox.Text = john.name
textBox.Text += "."
these may have a high chance of unsetting your bindings on Text on that textbox. I don't see any such line in the code you provided, but maybe you have it elsewhere.
You can easily check if the binding is still inact by running:
object realvalue = textBox.ReadLocalValue(TextBox.TextProperty);
now if the realvalue is null, or string, or anything other than a Binding object - that means that something accessed the textbox and replaced the binding with a concrete constant value, and you need to find and correct that so that .Text is not assigned and instead of that the Value property of source object (customdockpanel) is changed.

Handling WPF Editable combobox when entered text is not a part of datsource

I have a Combobox in WPF, I have set Is Editable="true" which allows me enter any text in the combobox. I would like to restrict users from entering text outside datasource.
Xaml:
<ComboBox Name="service" Margin="0,0,0,4"
IsEditable="True"
Grid.Column="1"
Grid.ColumnSpan="2" Grid.Row="4"
SelectedValuePath="Id"
DisplayMemberPath="Service"
SelectedValue="{Binding Controller.Service1}"
ItemsSource="{Binding}" />
C#:
System.Data.DataView vw = tableAdapterServices.GetData().DefaultView;
service.ItemsSource = vw;
service.SelectedIndex = 0;
I do not want to allow users to enter text which is not present in the datasource, or handle it if the user enters any other text.
Update:
Thanks for the solution #Vishal, LostFocus event is handling the issue, but it gave rise to another issue. I have a button which is used to submit the combobox value along with other textbox values to the server. I am setting default value in the combobox in lostfocus event. But I need to prevent the button click event if some value other that datasource value is added in combobox.
You can check for selectedIndex in Lostfocus event :
private void ComboBox_LostFocus(object sender, EventArgs e)
{
if(((ComboBox)sender).SelectedIndex == -1)
{
//Text entered by user is not a part your ItemsSource's Item
SaveButton.IsEnabled = false;
}
else
{
//Text entered by user is a part your ItemsSource's Item
SaveButton.IsEnabled = true;
}
}
You can try handling the ComboBox's TextInput or PreviewTextInput events, doing the text search yourself, selecting the most appropriate item, and setting "e.Handled = true."
This works for a single character (i.e. if you enter the letter "j", it will select the first item that contains a "j" or "J"), but I'm sure there's a way to do this with your control. Just include a little more logic to achieve this.
private void MyComboBox_PreviewTextInput(object sender, TextCompositionEventArgs e) {
foreach (ComboBoxItem i in MyComboBox.Items) {
if (i.Content.ToString().ToUpper().Contains(e.Text.ToUpper())) {
MyComboBox.SelectedItem = i;
break;
}
}
e.Handled = true;
}
Ok, from what I understand the behaviour of the combobox should disregard an inserted character if there is no item in the datasource that contains the resulted string.
I believe you are doing it in a MVVM style since you are using binding. What you can do is to bind the ComboBox text to a property and the PreviewTextInput event to a command and do the filtering there.
XAML:
<ComboBox Name="service"
Margin="0,0,0,4"
IsEditable="True"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="4"
SelectedValuePath="Id"
DisplayMemberPath="Service"
SelectedValue="{Binding Controller.Service1}"
ItemsSource="{Binding}"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewTextInput">
<cmd:EventToCommand Command="{Binding TextInputCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Combobox>
C# ViewModel:
public RelayCommand<object> TextInputCommand { get; set; }
public bool CanExecuteTextInputCommand(object param)
{
return true;
}
public void ExecuteTextInputCommand(object param)
{
TextCompositionEventArgs e = param as TextCompositionEventArgs;
string currentText = this.Text;
string entireText = string.Format("{0}{1}", currentText, e.Text);
var item = this.Items.Where(d => d.StartsWith(entireText)).FirstOrDefault();
if (item == null)
{
e.Handled = true;
this.Text = currentText;
}
}
Where Items is the ObservableCollection containing the items (in this case it's a list of strings) and Text is the property binded to the Combobox text.
EDIT: Ok so what you need to do to make it work is to go to your project, right click on References, choose Manage NuGet Packages, search and install MVVM Light. Two dlls that start with GalaSoft will be added to your references. After this, in your xaml code add these namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
What this allows you to do is to bind an event to a ICommand object.

How do I handle a selected item of LongListMultiSelector?

I have list of items in LongListMultiSelector - how to handle a selected item?
My LongListMultiSelector xaml:
<tkit:LongListMultiSelector Name="longlist" SelectionChanged="longlist_SelectionChanged">
<tkit:LongListMultiSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="32" Tap="TextBlock_Tap"/>
</DataTemplate>
</tkit:LongListMultiSelector.ItemTemplate>
</tkit:LongListMultiSelector>
TextBlock tap event handler code:
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var itemTapped = (sender as FrameworkElement).DataContext as Book;
}
LongListMultiSelector SelectionChanged event handler code:
private void longlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
I found part of solution here, however, The problem if at least one item is selected, then textblockTap event doesn't handle - longlist_SelectionChanged event handles everything. How can i fix that?
Once you are using LongListMultiSelector, the SelectionChanged event is fired when item is added or removed. If you want to perform the action regardless item is added/removed, I've managed to do it like this (for a simle string):
private void longlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selectedItem = String.Empty;
if (e.AddedItems.Count > 0) selectedItem = e.AddedItems[0] as string;
else selectedItem = e.RemovedItems[0] as string;
MessageBox.Show(selectedItem); // do your work
}
It should run while items are selected separately by tapping, but this method will have problems when more items are added/removed at the same time - if you need it, then you should handle this also.
Your XAML DataTemplate.
<DataTemplate x:Key="listItemTemplate">
<StackPanel Orientation="Horizontal" Margin="4,4">
<TextBlock Tap="textblockTap" Margin="0,-7,0,0" Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}"/>
</StackPanel>
</DataTemplate>
In your CS page;
private void textblockTap(object sender, EventArgs e)
{
var file = (TextBlock)sender;
var ContentFile = (string)file.Text;
MessageBox.Show(ContentFile);
}
This example will show you the text of the selected item in the MessageBox.

Categories