This question already has answers here:
How to clear text box on click in MVVM
(2 answers)
Closed 7 years ago.
I have a textbox in which I am handling its text changed event.Now when I click button I want to clear the text from the textbox.
Now when I have text in the textbox and when I call my command the text is not cleared.
xaml
<TextBox Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Name="mytxtBox">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=SearchCommand}" CommandParameter="{Binding ElementName=mytxtBox, Path=Text}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
ViewModel
public string SearchText
{
get
{
return TypedText;
}
set
{
TypedText=value;
if (string.IsNullOrEmpty(TypedText.ToString()))// This is called when the text is empty
{
Some Logic//
}
SetProperty(ref TypedText, value);
}
}
private void MyCommandExecuted(string text)
{
SearchText= string.Empty;
}
You seem not to understand the framework you are using
public string SearchText
{
set
{
TypedText = value;
SetProperty(ref TypedText, value);
}
}
These two lines of code should/could NEVER ever be in the same block of code EVER.
What is happening is this.
The first line sets TypedText to value. OKAY...
Second line, check if TypedText is equal to value (spoiler alert, it is), and set them to be equal if not AND THEN TELL WPF that you changed to value.
The problem is, the second line never runs its logic (of tell WPF that I've changed). The reason this never runs is the first line.
Remove TypedText = value; from your code and it might just work.
set
{
if (string.IsNullOrEmpty(value))// This is called when the text is empty
{
Some Logic//
}
SetProperty(ref TypedText, value);
}
However, one last thing. I really really really hate code where the setter DOES stuff. Why is there logic here? From an external user, it might do something unexpected.
I have a textbox in which I am handling its text changed event
No you don't, or at least not in the code excerpt that you have shown in your question:
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="mytxtBox">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=SearchCommand}" CommandParameter="{Binding ElementName=mytxtBox, Path=Text}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
In this example, you have a string property data bound to the TextBox.Text property, which is similar, but not the same as handling its text changed event.
Either way, in order to clear this data bound value, you just need to set your data bound string property to an empty string (after removing that extraneous code from the setter):
public string SearchText
{
get { return TypedText; }
set { TypedText = value; SetProperty(ref TypedText, value); }
}
...
private void MyCommandExecuted(string text)
{
SearchText = string.Empty;
}
Related
I need to validate the value in the text box when the users finish their input.
When they go to the next text box, the previous text box will validate the range of input (100~100000 steps 100 and format is "###,###"). And my opinion is Lost Focus Event is the good way to solve this.
I have this property which I want to use Lost Focus
private string resultCommandNote02;
public string ResultCommandNote02
{
get
{
return resultCommandNote02;
}
set
{
resultCommandNote02 = value;
NotifyOfPropertyChange(() => ResultCommandNote02);
}
}
And here is the code of View.XAML
<TextBox Grid.Column="1" Margin="0 0 0 4" Text="{Binding ResultCommandNote01}"
cal:Message.Attach="[Event LostFocus] = [Action TxtCmdNote01_LostFocus]"></TextBox>
Background: In my View I have a TextBlock and a TextBox. As soon as the text changes in the TextBox the TextChanged event gets fired and after filtering a list, I want to update the property which is bound to the TextBlock.
In my case it's a counter that shows the number of contacts in the current list.
Problem: When I debug the property (ContactsCount) gets always updated correctly, but only in Code and not in the UI. Strangely enough the UI only updates after I delete the text from the TextBox, to the last list count, but not the actual one.
Code
View:
<TextBlock Text="{Binding ContactsCount, UpdateSourceTrigger=PropertyChanged}"
d:Text="4 Contacts"/>
<xctk:WatermarkTextBox Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Watermark="Search Contact"
Margin="20,10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding Path=SearchBoxTextChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</xctk:WatermarkTextBox>
ViewModel:
public string ContactsCount
{
get => contactsCount;
set
{
contactsCount = value;
OnPropertyChanged(ContactsCount);
}
}
public string SearchText
{
get => searchText;
set
{
searchText = value;
OnPropertyChanged(SearchText);
}
}
public CommandHandler SearchBoxTextChanged { get; set; }
SearchBoxTextChanged = new CommandHandler(TextChanged);
private void TextChanged()
{
var filteredList = contactsList.Where(c => c.FirstName != null && c.FirstName.Contains(searchText.ToLower(), StringComparison.OrdinalIgnoreCase) ||
c.SecondName != null && c.SecondName.Contains(searchText.ToLower(), StringComparison.OrdinalIgnoreCase));
Contacts = new ObservableCollection<Contact>(filteredList);
// Bug: Doesn't update the UI after ContactsCount gets changed
ContactsCount = $"{Contacts.Count} Contacts";
}
You didn't post the code of your OnPropertyChanged() method, but I suspect if should be
OnPropertyChanged("SearchText");
or better
OnPropertyChanged(nameof(SearchText));
i.e. pass in the name of the updated property, not its value.
I have added a placeholder to the Textbox using below link,
http://www.techken.in/coding/c-wpf-create-textbox-placeholder-using-xaml-code/
Below is the textbox syntax i used,
<TextBox Style="{StaticResource placeHolder}" Tag="Input text" Text="
{Binding Path=Token,Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged}" cal:Message.Attach="[Event
TextChanged] = [Action OnChangeEvent()]" />
When placeHolder is kept event is firing twice. If I remove UpdateSourceTrigger for Textbox which is inside of placeHolder xaml I could not get the changed property value. Anyone please let me know how I can make this to hit OnChangeEvent once only with placeholder.
Try getting the value in the property setter so the property is not set twice causing the OnChangeEvent to fire twice. try below way
if (value == _token) return;
public string Token
{
get => _token;
set
{
if (value == _token)
{
return;
}
_token = value;
OnPropertyChanged();
}
}
I am trying to make a small modal window with a search box and a list of results.
My goal is for the focus to land in the first element of the List after searching.
Problem
Unfortunately, I stuck in a situation where my focus lands on the window itself after searching.
My TextBox has an InputBinding that triggers on enter to perform the search:
<TextBox Grid.Column="1" Grid.Row="1"
Text="{Binding Supplier.LeverandorNavn, UpdateSourceTrigger=PropertyChanged}"
x:Name="txtBoxSupplierName" Width="150"
HorizontalAlignment="Left" VerticalAlignment="Top"
TabIndex="1">
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding SearchCommand}" />
</TextBox.InputBindings>
</TextBox>
I have been trying to use Snoop to understand the flow of events.
As seen here, the KeyDown is handled by my txtBoxSupplerName Textbox fine.
But I cannot understand why the levWindow gets focus after my command executes.
I have attempted to manually set the focus element, but it has no effect.
Questions
Can someone explain to me, why the focus lands on the window by default?
Can someone suggest an approach, on how I can take control of the focus myself and avoid this default behavior?
I am challenged by the fact that the DataGrid needs some time to redraw itself before the row I want to focus is visible.
Properties
public ClientLeverandor ValgtLeverandør
{
get { return _valgtLeverandør; }
set { SetProperty(ref _valgtLeverandør, value, nameof(ValgtLeverandør)); }
}
public ObservableCollection<ClientLeverandor> Leverandører
{
get { return _leverandører; }
private set { SetProperty(ref _leverandører, value, nameof(Leverandører)); }
}
public ListCollectionView LeverandørView
{
get { return _leverandørView; }
set { SetProperty(ref _leverandørView, value, nameof(LeverandørView)); }
}
Command Implementation
using (BusyIndicator.ShowInScope(Strings.HenterData_BusyText))
{
var leverandører = await SøgLeverandører(Supplier);
if (leverandører.Any())
{
Leverandører.Clear();
foreach (var lev in leverandører) Leverandører.Add(lev);
ValgtLeverandør = Leverandører[1];
SuppliersAdded?.Invoke();
}
}
I was unable to find an explanation for why KeyboardFocus land on my window or why my attempts to set the focus did not have effect.
But i was finally able to find a solution, where my attempt worked.
I used a Dispatcher with BackgroundPriority to invoke my method to set focus after whatever was setting focus to the window. This worked for both Keyboard and Mouse input.
private void ViewModel_SuppliersAdded()
{
Dispatcher.BeginInvoke(new Action(SetFocusToFirstRow), DispatcherPriority.Background);
}
This is for a Windows 10 Universal App.
XAML:
<RelativePanel Padding="4" Margin="4,12,0,0">
<TextBlock x:Name="Label" Text="Class Name" Margin="12,0,0,4"/>
<ListView x:Name="ClassTextBoxes"
ItemsSource="{Binding TextBoxList}"
SelectionMode="None" RelativePanel.Below="Label">
<ListView.ItemTemplate>
<DataTemplate >
<RelativePanel>
<TextBox x:Name="tbox"
PlaceholderText="{Binding PlaceHolder}"
Text="{Binding BoxText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Padding="4" Width="200" MaxLength="25"/>
<TextBlock x:Name="errorLabel"
RelativePanel.Below="tbox"
Text="{Binding Error, Mode=TwoWay}"
Padding="0,0,0,4"
FontSize="10"
Foreground="Red"/>
<Button Content="Delete" Margin="12,0,0,0" RelativePanel.RightOf="tbox"/>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RelativePanel>
Model:
public class TextBoxStrings : BaseModel
{
private string _placeholder;
public string PlaceHolder
{
get { return _placeholder; }
set
{
if (_placeholder != value)
{
_placeholder = value;
NotifyPropertyChanged();
}
}
}
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
}
}
public string CheckBoxText(string val)
{
var r = new Regex("[^a-zA-Z0-9]+");
return r.Replace(val, "");
}
}
ViewModel:
private TrulyObservableCollection<TextBoxStrings> _textBoxList;
public TrulyObservableCollection<TextBoxStrings> TextBoxList
{
get { return _textBoxList; }
set
{
if (_textBoxList != value)
{
_textBoxList = value;
RaisePropertyChanged();
}
}
}
and I add new TextBoxString objects to my TextBoxList collection from within my view-model.
I want to make it that users can't type in certain characters (or rather, they get deleted whenever they
are typed in.
This works...in the model. Setting breakpoints and looking at the values, everything in the Model is working: value goes into the setter and gets changed, _boxText holds the new value that is set from CheckBoxText();
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
So if I type in "abc*()" into "tbox", the value in the model will be "abc". The value of the textbox, however, will still be "abc*()".
I have a feeling it has something to do with the fact that I'm editing items that are inside of a collection and I don't have anything implemented to handle changing items within a collection. I was under the impression that using INotifyPropertyChanged and ObservableCollection<T> would take care of that for me.
Does anyone have any suggestions?
Thank you!
Edit: So, now I'm trying to use TrulyObservableCollection because I thought this was the problem, but it hasn't helped. Here it is: https://gist.github.com/itajaja/7507120
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
As you've seen, the TextBox do reflect changes to your model. When you type in "abc*()" in the TextBox, the value in the model will be changed to "abc". The problem here is that the binding system in UWP is "intelligent". For TwoWay bindings, changes to the target will automatically propagate to the source and in this scenario, binding system assumes that the PropertyChanged event will fire for corresponding property in source and it ignores these events. So even you have RaisePropertyChanged or NotifyPropertyChanged in you source, the TextBox still won't update.
In WPF, we can call BindingExpression.UpdateTarget Method to force the update. But this method is not available in UWP.
As a workaround, you should be able to use TextBox.TextChanged event to check the input like following:
private void tbox_TextChanged(object sender, TextChangedEventArgs e)
{
var tb = sender as TextBox;
if (tb != null)
{
var originalText = tb.Text;
var r = new Regex("[^a-zA-Z0-9]+");
if (originalText != r.Replace(originalText, ""))
{
var index = (tb.SelectionStart - 1) < 0 ? 0 : (tb.SelectionStart - 1);
tb.Text = r.Replace(originalText, "");
tb.SelectionStart = index;
}
}
}
However it may break your MVVM model, you can use data validation to avoid this and here is a blog: Let’s Code! Handling validation in your Windows Store app (WinRT-XAML) you can refer to. And for my personal opinion, data validation is a better direction for this scenario.
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
Try changing this to:
var tmp = CheckBoxText(value);
if (_boxText != tmp)
{
_boxText = tmp;
NotifyPropertyChanged();
}
I hope, in your XAML, the binding to property BoxText is two-way, right?
You should edit BoxText and then send checked value to UI. Just send value to CheckBoxText and already edited should be assigned to _boxText. And then you should send BoxText to UI by calling RaisePropertyChanged("BoxTest"). Please, see the following code snippet:
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText=CheckBoxText(value);
RaisePropertyChanged("BoxText");
}
}
}
There is no difference where you use INotifyPropertyChanged for one property of for properties placed in collection. The complete example with collections and ListView can be seen here