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);
}
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>
I have the following popup where a user is able to enter three fields and when they click 'ok'. The popup should simply disappear. As of now, when I click on the ok button, nothing happens, the popup just stays there, the same goes for when I click the 'Cancel' button, nothing happens either. What am I missing that when I press the 'OK' button, it does not disappear? Why are the buttons not doing anything when I click on them?
EDIT:
After placing a breakpoint on my ok button logic, I get a hit on that, however, I think my main question here is if there is a way for the popup to close after clicking on ok, hence, the user is done entering data.
As for the cancel button, it does not trigger when the breakpoint when clicking on the button.
XAML CODE
<Border Grid.Column="0" Margin="5"
Grid.Row="2">
<WrapPanel HorizontalAlignment="Right">
<Button x:Name="btnCancel" Command="{Binding Path=CloseCommand}"
Content="_Cancel" Margin="4,2" MinWidth="60"/>
<Button x:Name="btnOk" Command="{Binding Path=OKCommand}"
Content="_OK" Margin="4,2" MinWidth="60"/>
</WrapPanel>
</Border>
ViewModel Code
public event CancelHandler OnCancel;
public delegate void CancelHandler();
public ICommand CancelCommand
{
get { return new RelayCommand(c => OnCancelLock()); }
}
public ICommand OKCommand
{
get { return new RelayCommand(c => OnOKLock()); }
}
protected void OnOKLock()
{
var currentSetting = AppSession.Repository.Settings.Find(SettingQuery.ID == new ID("LockedOutDate"));
currentSetting[0].Value = LockedOutDate;
AppSession.Repository.Settings.Save(currentSetting[0]);
currentSetting = AppSession.Repository.Settings.Find(SettingQuery.ID == new ID("LockedOutBy"));
currentSetting[0].Value = LockedOutBy;
AppSession.Repository.Settings.Save(currentSetting[0]);
currentSetting = AppSession.Repository.Settings.Find(SettingQuery.ID == new ID("LockedOutFor"));
currentSetting[0].Value = LockedOutFor;
AppSession.Repository.Settings.Save(currentSetting[0]);
currentSetting = AppSession.Repository.Settings.Find(SettingQuery.ID == new ID("IsUsersLockedOut"));
currentSetting[0].Value = "1";
AppSession.Repository.Settings.Save(currentSetting[0]);
}
protected void OnCancelLock()
{
OnCancel();
}
The problem you have is right here:
<Button x:Name="btnCancel" Command="{Binding Path=CloseCommand}"
Content="_Cancel" Margin="4,2" MinWidth="60"/>
You defined your binding to be CloseCommand, but in your ViewModel code you wrote:
public ICommand CancelCommand
{
get { return new RelayCommand(c => OnCancelLock()); }
}
So either change it to CloseCommand in your ViewModel or CancelCommand in your XAML file.
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;
}
I have an existing solution of my WPF UI but it's ViewModel implementation is clunky and I'm looking to improve.
Below is a gif of how my current system works:
There's a Current Task (note: only ever one item)
There's a Task List for Tasks (note: possibly many) that need to run in the future
When the user selects one list box, the other selection is removed
The problem is, I'm implementing Current Task as a Listbox with only one item. This means I have to lug around a backing IList for the ItemSource and another property for the SelectedItem.
Is there another control I can use to behave like ListBoxItem, but I can bind my CurrentTask directly to it and not have to muck around with an List for ItemSource as well?
EDIT: To get the selection to go away when one listbox is selected, I have a trigger set up on the SelectionChanged event.
(deleted my previous answer)
It occurs to me that at least part of the functionality you're looking for is implemented by the RadioButton class. Multiple RadioButtons in the same scope guarantee that only one of them is selected. You'll probably have to do a little work to make sure that your RadioButtons can be scoped correctly in your UI, and you'll probably need to retemplate some things to get exactly the UI you need. Additionally, RadioButton does not have a SelectedItem/SelectValue property to which it can write to, because WPF provides no built-in mechanism for multiple controls to safely bind to a "SelectedWhatever" property. But you could roll this yourself pretty easily with codebehind or triggers.
Here's the implementation I went with:
XAML View
<!-- The Current Task box -->
<ListBox x:Name="CurrentTaskBox" FlowDirection="RightToLeft" Background="{StaticResource WhiteBrush}">
<ListBoxItem IsSelected="{Binding CurrentTaskSelected, Mode=TwoWay}" Content="{Binding CurrentTask.TaskId}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Selected">
<command:EventToCommand Command="{Binding SetTaskDetailsFromCurrentTaskCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBoxItem>
</ListBox>
<!-- The Task List box -->
<ListBox x:Name="TaskListBox" SelectedIndex="{Binding TaskListIndex}" SelectedValue="{Binding TaskListSelection}" IsSynchronizedWithCurrentItem="True" HorizontalContentAlignment="Stretch" ItemsSource="{Binding TaskList}" FlowDirection="RightToLeft" DisplayMemberPath="TaskId" Margin="3">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding SetTaskDetailsFromTaskListCommand}" CommandParameter="{Binding SelectedItem, ElementName=TaskListBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
ViewModel
/* Omitted most INPC property declarations...kinda boring */
public ICommand SetTaskDetailsFromCurrentTaskCommand { get { return new RelayCommand(SetTaskDetailsFromCurrentTask); } }
public ICommand SetTaskDetailsFromTaskListCommand { get { return new RelayCommand<TaskScheduleSequenceDto>(async taskSelection => await SetTaskDetailsFromTaskList(taskSelection)); } }
private bool _currentTaskSelected;
public bool CurrentTaskSelected
{
get
{
return _currentTaskSelected;
}
set
{
Set(() => CurrentTaskSelected, ref _currentTaskSelected, value);
}
}
private async Task SetTaskDetailsFromTaskList(TaskScheduleSequenceDto taskListSelection)
{
if (taskListSelection == null)
{
return;
}
var taskDetails = await _broker.RetrieveTaskDetails(taskListSelection.TaskId);
TaskDetails = taskDetails;
CurrentTaskSelected = false;
}
private void SetTaskDetailsFromCurrentTask()
{
TaskDetails = CurrentTask;
TaskListSelection = null;
CurrentTaskSelected = true;
}
This works fine and only requires that I have a single CurrentTask property in my VM, which I think is much cleaner.
Some of the checkboxes on my form should not be able to be checked/unchecked by users. Is there a way for me to cancel the event before the checbox's Check event is triggered?
in winForms it was easy, just
public void cb_BeforeChecked(object sender, EventArgs e){
e.Handled = true;
}
but I cannot find anything like this in WPF...I figure you can probably do it, just need to do something fancy..
Thanks!
Why not just set IsEnabled="False"?
You can set IsHitTestVisible="False" to make it not respond to user clicks. Otherwise you can bind it to a command if viewmodel logic determines whether it is clickable.
<Grid>
<CheckBox IsHitTestVisible="False" Content="I cannot be clicked at all"/>
<CheckBox Command="{Binding DoSomethingCommand}" Content="I can be clicked if DoSomethingCanExecute returns true."/>
</Grid>
In your DataContext (Viewmodel or otherwise):
RelayCommand _DoSomethingCommand = null;
public ICommand DoSomethingCommand
{
get
{
if (_DoSomethingCommand== null)
{
_DoSomethingCommand= new RelayCommand(
param => DoSomething(),
param => DoSomethingCanExecute
);
}
return _DoSomethingCommand;
}
}
public bool DoSomethingCanExecute
{
get
{
return CheckboxShouldBeEnabled();
}
}
public void DoSomething()
{
//Checkbox has been clicked
}
This might be a bit of an overkill, but you could sub-class CheckBox and then override the OnClick() method.
Only setting IsHitTestVisible="False" just takes care of the Mouse, the users can still use the KeyBoard to tab to the CheckBox and change the value.
You should set both IsHitTestVisible="False" and Focusable="False" to disable the KeyBoard as well
You can have the check box disabled, and associate a style with the disabled check box, if the disabled look is a problem. As its already pointed in the previous posts, its good to have different looks for different states.