I have a user control that has a TextBox on it. If I set focus on the TextBox in the constructor, then the TextBox works as expected. Sometimes though, I don't want the TextBox to have focus when the user control is first shown, and so I added a property to the user control which sets focus to the TextBox. This works, although I get the problem that I can't then reset focus on the TextBox after it has lost focus.
Doesn't anyone have any ideas why this might be happening?
public ucQueryBox()
{
InitializeComponent();
// Set default values for properties
CodePrompt = "Barcode";
TextBoxFontSize = 20;
TextBoxMaxWidth = 0;
Label = "";
LabelFontSize = 20;
LabelForeground = Colors.White.ToString();
KeyboardButtonVisibility = Visibility.Visible;
txtSelection.Focus();
}
/// <summary>
/// Allows user to decide whether or the user control should have focus when it loads
/// Focus puts the green boarder around the textbox
/// </summary>
[Browsable(true)]
public Boolean SetFocusOnLoad
{
get { return _bSetFocusOnLoad; }
set
{
_bSetFocusOnLoad = value;
if (_bSetFocusOnLoad)
txtSelection.Focus();
}
}
Focus in WPF is a complex topic. I think you'll find that the proper way to do what you want is to use FocusManager in your XAML:
<UserControl ... FocusManager.FocusedElement="{Binding ElementName=myTextBox}">
<TextBox x:Name="myTextBox" />
</UserControl>
If you use FocusManager like this to establish all focus requirements (that is, you use FocusManager on all Windows and UserControls that have any sort of focus requirements), then you'll probably find that all the focusing works exactly like you expect.
Related
I am using MVVM. On my View I have a control that by default is hidden, it's Visibility property is Binded to ViewModels property.
<Grid>
<TextBox Visibility={Binding IsVisible, Mode=OneWay, Converter={StaticResource MyVisibilityConverter}}/>
<Grid>
In the ViewModel I have a property
private bool _isVisible;
bool IsVisible
{
get {return _isVisible;}
set {_isVisible = value; NotifyOfPropetyChanged(() => IsVisible);}
}
pretty much straighforward, to show the control I just do
IsVisible = true;
in my ViewModel and the TextBox becomes visible, works fine as intended.
What I want to do is to set Focus on the TextBox just after it becomes visible. The problem is that I can't find any good solution how to determine that this particular control just got visible and it is the moment I can set the focus.
The solution would be to test the visibility inside LayoutUpdated event, but it is definitely not the nicest thing to have in code.
Any better solution?
edit:
To clarify - I don't want to set the focus via MVVM from the ViewModel. There is no problem in setting the focus from the code-behind as it is the UI behaviour. The only problem is how to determine WHEN to do that. There is a some period of time beetween the ViewModel property is set and the layout being updated to match its state. After that perdiod of time I want to be able to catch anything that can notify me "my visibility has changed, now you can change focus"
You could use RegisterPropertyChangedCallback to register a change callback for the Visibility property of the textbox. then in the changed call back method you can set the focus is the visibility is visible.
Put this in the constructor of the code behind:
TextBox1.RegisterPropertyChangedCallback(UIElement.VisibilityProperty, VisibilityChanged);
and add the CallBack method:
private void VisibilityChanged(DependencyObject sender, DependencyProperty dp)
{
if (((UIElement)sender).Visibility == Visibility.Visible)
{
TextBox1.Focus(FocusState.Keyboard);
}
}
Problem Overview:
Any KeyBinding's defined at a level higher than a TextBox (with no modifier keys assigned), prevents the user from typing those keys inside the TextBox.
Minimal XAML Hierarchy:
<Window>
<UserControl>
<Border>
<UserControl>
<TextBox>
Minimal Command/KeyBinding:
<UserControl.Resources>
<RoutedUICommand x:Key="Commands.SomeCommand" />
</UserControl.Resources>
<UserControl.InputBindings>
<KeyBinding Key="A" Command="{StaticResource Commands.SomeCommand}" />
</UserControl.InputBindings>
<UserControl.CommandBindings>
<CommandBinding Command="{StaticResource Commands.SomeCommand}" Executed="..." />
</UserControl.CommandBindings>
The Command and KeyBinding, are defined at the first UserControl level. So in this example, in the textbox, the user can type freely until they press the A key, and then it just does not insert the letter into the textbox. I can clearly see that the TextBox.KeyDown and TextBox.PreviewKeyDown are firing when you press the A key (and Handled = false) , but the letter will not get added to the text of the textbox and TextBox.PreviewTextInput does not fire.
I'm looking for any suggestions that may indicate what is swallowing the keypress and stopping it from getting processed by the TextBox, or anything related to how I can debug this issue.
EDIT: Thanks to Snoop, I was able to clearly see the problem.
TextBox.PreviewKeyDown tunnels down and fires through the visual tree, starting at the Window, and ending at the TextBox
TextBox.KeyDown bubbles back up starting at the TextBox and heading towards the window
TextBox.KeyDown gets Handled set to true by the first UserControl that has the KeyBinding set.
TextBox.PreviewTextInput never fires, nor does the textbox process the input, because the KeyDown event was set as handled.
This still leaves the problem, how do you prevent the UserControl from handling the input if a textbox has focus? Within the Command execution, I can check if a textbox has keyboard focus, but by this time it's too late.
TextInput and PreviewTextInput only fires when the Text actually changes / might change.
As you updated your question to reflect, the Command intercepts the event and the (Preview)TextInput events are never raised.
The nicest solution would be to add a modifier key to your KeyBinding, but I suspect that is not your preferred way to go.
Another option would be to e.Handle the PreviewKeyDown event on the TextBox and raise the TextComposition events yourself, using something like:
target.RaiseEvent(new TextCompositionEventArgs(InputManager.Current.PrimaryKeyboardDevice,
new TextComposition(InputManager.Current, target, "A"))
{
RoutedEvent = TextCompositionManager.TextInputEvent
});
(Alternatively, insert into textBox.Text at the correct CaretIndex)
Truth be told, it would still be a hack.
I have the same problem.
I took a look to documentation for key bindind, and there is described, that the key on which you bind shouldn't be just key, but key gesture, so it shall be
Modifier key + normal key
Numeric keypad key
Functional key.
Of course, it works with just A, but it's bad practice overall. You should consider to implement some of the posibilities mentioned behind. More at https://msdn.microsoft.com/cs-cz/library/system.windows.input.keybinding(v=vs.110).aspx
I had used the TextComposition RaiseEvent approach for years, however this seems to break typing for non-latin keyboard layouts (eg. cyrillic).
The proper way to do this is to derive from InputBinding and return false in the Matches? check if the event originated from a text-box.
/// <summary>
/// This gesture doesn't handle keys originating in a text control. This allows key bindings without modifier keys
/// that don't break normal typing. A standard KeyGesture doesn't have such logic; this allows the parent of a
/// text box to handle such bare keypresses before the textbox gets to see it as normal text input, thus breaking
/// normal typing.
/// </summary>
public class BareKeyGesture : InputGesture
{
public Key Key { get; set; }
public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
{
var keyEventArgs = inputEventArgs as KeyEventArgs;
if (keyEventArgs == null)
return false;
if (inputEventArgs.OriginalSource is TextBoxBase)
return false;
return (int)Key == (int)keyEventArgs.Key && Keyboard.Modifiers == ModifierKeys.None;
}
}
/// <summary>
/// This only exists because the InputBinding constructor is protected, but since we have to have it anyway
/// we also use this opportunity to simplify adding a BareKeyGesture to it.
/// </summary>
public class BareKeyBinding : InputBinding
{
private BareKeyGesture _gesture = new();
public BareKeyBinding()
{
Gesture = _gesture;
}
public Key Key
{
get => _gesture.Key;
set { _gesture.Key = value; }
}
}
And now that you have an InputGesture which will ignore events originating from textboxes, you can use it in XAML like normal:
<UserControl.InputBindings>
<nsp:BareKeyBinding
Key="D"
Command="{StaticResource Commands.YourCommand}"
CommandParameter="None" />
</UserControl.InputBindings>
As long as you use KeyBinding this not going to work without major hacks. A solution I implemented for this is:
Use the KeyDown event to capture those keys being pressed (instead of KeyBindings). This will be on your code-behind and from there you'll need to switch on the pressed Key to call the required command (SomeCommand in your case).
Now you have a different problem. The TextBox is getting the input but your key-bound commands are also firing. On the code behind, check the type of keyEventArgs.InputSource and ignore the key stroke if it's a TextBox.
It should look like this:
private void OnKeyDown(object sender, KeyEventArgs e)
{
ICommand command = null;
switch (e.Key)
{
case Key.A:
command = Commands.SomeCommand;
break;
case Key.B:
command = Commands.SomeOtherCommand;
break;
}
bool isSourceATextBox = e.InputSource.GetType() == typeof(TextBox);
if (command != null && !isSourceATextBox)
{
command.Execute(parameter:null);
}
}
I am using MVVM Light WPF 4.
I have a ContentPresenter in my Home.xaml.
<ContentPresenter Name="MDI" Content="{Binding WindowName, Mode=OneWay}">
I am binding user control to this in viewmodel like
public UserControl WindowName { get; set; }
void ShowSalesEntry()
{
WindowName = null;
WindowName = new SalesEntry();
RaisePropertyChanged("WindowName");
}
by using command in a menu click and it is binding fine.
Now in the user control i have a button which i used to close (but to close i change the visibility
to collapsed) by this way..
Visibility="{Binding visibility, Mode=OneWay}"
in the user control view model,
public SalesEntryViewModel()
{
visibility = Visibility.Visible;
cmdExitWindow = new RelayCommand(ExitWindow);
RaisePropertyChanged("visibility");
}
and the following to close (visibility to collapsed)
public RelayCommand cmdExitWindow { get; set; }
void ExitWindow()
{
visibility = Visibility.Hidden;
RaisePropertyChanged("visibility");
}
To exit (means visibility collapsed)..
This is working fine upto this.
Problem is when i click the same page i mean to show the same user control,
now this time the visibility is still collapsed. Even though i changed to visible in the
load event.
How to solve this..
I am new to MVVM WPF.. Please help me..
Problem is when i click the same page i mean to show the same user
control, now this time the visibility is still collapsed. Even though
i changed to visible in the load event.
Based on this comment and the code provided, you've either omitted code, or you've confused the purpose of the constructor.
In your constructor, you have set the Visibility to Visible. You then have a method that sets the Visibility to Hidden, but there is nothing to ever set it back to Visible once this has occurred. The constructor only fires when the object is created. You need something to set the Visibility back at the appropriate time (ie. your comment "when i click the same page").
//Add these lines to the method/event that will show the control again
visibility = Visibility.Visible;
RaisePropertyChanged("visibility");
That's the best answer I can give based on what you've provided.
I have a ComboBox in a WPF app that has recently been refactored to use the MVVM pattern. An apparent side effect to this change is that changing focus to another application while the combobox dropdown is visible completely prevents the dropdown from being visible again, until the app has been restarted.
The ComboBox DataContext is set to my ViewModel, with its ItemsSource bound to an ObservableCollection<String> SearchSuggestions, and IsDropdownOpen bound to a property SuggestionsVisible in the ViewModel.
The desired effect is a search box with autocomplete suggestions. It should close if there are no suggestions in the ObservableCollection, if the user cancels the search, if the user runs the search, or if the user clicks away from the text field - either inside the app or outside it.
The ViewModel explicitly sets the SuggestionsVisible property to true or false based on whether SearchSuggesions contains any items after user input. This process continues to take place after this bug manifests itself, just with no visible change to the UI. Any idea why losing focus while the dropdown is open renders the dropdown un-openable for the rest of the app's session?
Here's how I have things wired together:
<ComboBox DataContext="{Binding SearchBoxVm}" Name="cmboSearchField" Height="0.667"
VerticalAlignment="Top" IsEditable="True" StaysOpenOnEdit="True"
PreviewKeyUp="cmboSearchField_OnKeyUp"
PreviewMouseLeftButtonUp="cmboSearchField_OnPreviewMouseLeftButtonUp"
Background="White" ItemsSource="{Binding SearchTopics}"
IsDropDownOpen="{Binding SuggestionsVisible,
UpdateSourceTrigger=PropertyChanged}"
Margin="50.997,15.333,120.44,0"
RenderTransformOrigin="0.5,0.5" Grid.Row="1" >
<!-- SNIP STYLING -->
</ComboBox>
ViewModel:
public class SearchBoxViewModel : INotifyPropertyChanged
{
public void ResetSearchField(bool preserveContents = false)
{
if (!preserveContents || string.IsNullOrEmpty(Query))
{
Foreground = Brushes.Gray;
QueryFont = FontStyles.Italic;
Query = DEFAULT_TEXT;
}
}
public bool OnKeyUp(Key key)
{
bool showDropdown = SuggestionsVisible;
bool changeFocusToCombobox = false;
if (keyInValidRange(key))
{
SearchSuggestions = GetSearchSuggestions(Query);
if (SearchSuggestions.Count > 0)
{
SuggestionsVisible = true;
}
}
return changeFocusToCombobox;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
bool _suggestionsVisible = false;
public bool SuggestionsVisible
{
get { return _suggestionsVisible; }
set
{
// this section is still called after this issue manifests,
// but no visible change to the UI state is made
_suggestionsVisible = value;
NotifyPropertyChanged("SuggestionsVisible");
}
}
public ObservableCollection<String> SearchTopics = new ObservableCollection<String>();
}
The OnKeyUp() method is called by the MainWindow class ( haven't gotten as far as binding events to handlers specified in the ViewModel ), while but there's also a call to ResetSearechField from the MainWindow:
// Note: removing references to this event handler does not have any effect
// on the issue at hand... only including here for completeness
void window_Deactivated(object sender, EventArgs e)
{
SearchBoxVm.SuggestionsVisible = false;
SearchBoxVm.ResetSearchField(true);
}
I've spent quite a bit of time trying to debug this, and haven't seen any internal state changes that might account for this. The NotifyPropertyChanged event is otherwise behaving as it did before, and the stack trace window isn't showing any exceptions having been encountered.
Setting the binding mode on the IsDropdownOpen property to 'TwoWay' in the XAML hasn't had any effect either. Lastly, wrapping the assignment to SuggestionsVisible in a Dispatcher call on the main thread has had no effect on the issue either.
Any assistance would be appreciated.
#BrMcMullin, since you have stated that:
The desired effect is a search box with autocomplete suggestions.
may I ask, why do you choose to use standard ComboBox instead of specialized AutoCompleteBox that is available in the WPF Toolkit - February 2010 Release and seems like was especially designed for your case?
You may have noticed that first link points to documentation for its Silverlight predecessor, but don't worry - WPF Toolkit library include fully functional official WPF port of AutoCompleteBox from Silverlight. There is more info about this "event": AutoCompleteBox: Now with 100% more WPF.
With that control your auto complete popup could looks as simple as:
or as complex as:
So, if you will not manage to solve your issue with ComboBox's popup visibility, feel free to give a try to AutoCompleteBox. With it you could even leverage dynamic sorting of your suggestions if needed (just use answer from #adabyron).
Hai
am having a WPF user control in my WPF Form, when i click my button in my form , i just want to pass some value to the textbox which is in the usercontrol, tell me how to do this.
There are several ways you can do this. The easiest way is to use a String property and implement INotifyPropertyChanged in your UserControl.
To illustrate, you will have your UserControl like so:
/// <summary>
/// Interaction logic for TextBoxUsercontrol.xaml
/// </summary>
public partial class TextBoxUsercontrol : UserControl, INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
public TextBoxUsercontrol()
{
DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now the TextBox in your UserControl must bind itself to your Text property like so:
<TextBox Text="{Binding Text}" />
Then in your WPF form, you will have your UserControl declared and a button to handle the click like so:
<local:TextBoxUsercontrol x:Name="textBox" />
<Button Click="ButtonBase_OnClick" >Add Text</Button>
And finally, in your Click handler:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
textBox.Text = "Hello!";
}
Having shown you the solution, I give your question asking skill a 1 out of 5. You can be a lot more specific with your question and give sample codes snippets as I have done without asking us to download your whole solution from a site that we must wait download it (not to mention most of us are security conscious about downloading unknown files).
Good luck.
Standard WPF? NO WAY.
Why? You dont pass values around. You get a click event on the item that is clicked (the button) with elements defined on the button (only), then in the code you access the other elements, which thus also have to be either defined in code (the standard way) and expose their values through something called "properties", OR you get the control by name and extract the value. But you dont pass any additional data around.
Look at the tutorials ;)
If you want to PASS values around to METHODS on a click, you need to use something like caliburn (http://www.codeplex.com/caliburn) which allows you to map the click to a method and grab the values passed into the method from other controls.
Just Create a Dependency property and Bind the Porperty to the UserControl's TextBox. While creating the object itself assign the value to the Usercontrol's dependency property.