How to pass shortcuts from MainWindow to the UserControl in ContentControl? - c#

The UserControl dynamically loaded to the ContentControl in my Window does not receive keyboard shortcuts defined inside of UserControl XAML, in case it is not focused.
I need to implement keyboard shortcuts for dynamically loaded UserControl, without focusing the UserControl.
I cannot define InputBindings on MainWindow, because the InputBindigs are changing depends on currently loaded UserControl.
1) So I tried to send all Window_KeyUp to the loaded UserControl via RaiseEvent, no luck. (StackOverflow Exception or no action called)
2) I tried also fillup the MainWindow.InputBindings by LoadedUserControl.InputBindings when the UserControl has been loaded to the ContentControl... no luck (defined command is unknown in context)
UserControl.xaml
----------------
<UserControl.InputBindings>
<KeyBinding Key="N" Command="{Binding Path=NewOrderCommand}" Modifiers="Control" />
</UserControl.InputBindings>
This is working if UserControl is focused.
So to get rid of focusing I tried this:
MainWindow.xaml.cs
-------------------
private void MainWindow_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
// ModulePanel points to the loaded UserControl
ViewModel.CurrentModule.ModulePanel.RaiseEvent(e);
e.Handled = true;
}
So this issued StackOverflowException
I tried set e.Handled = true; before RaiseEvent(e) but it does not pass the event to the UserControl - so nothing happens;
I also tried to get InputBindings from UserControl to MainWindow:
foreach(InputBinding bi in UserControl.InputBindings)
MainWindow.InputBindings.Add(bi);
But I got exception in Debug window:
Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=NewOrderCommand; DataItem=null; target element is 'KeyBinding' (HashCode=35527846); target property is 'Command' (type 'ICommand')
My expectation is, that I will dynamically change Window InputBindings depends on loaded UserControl, where the InputBindings are defined.

If you want the UserControl to be able to handle all key presses in the window, you could get a reference to the parent window using the Window.GetWindow once the UserControl has been loaded and then hook up an event handler to it:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Loaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
};
Unloaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;
};
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
//do something...
}
}

Try using ViewModels as DataContext and let them communicate with each other using a Messenger. You can send the Information needed to the ViewModel of the unfocused Usercontrol.
If you want to stick with code-behind, subscribe to the eventhandler on Mainwindow:
PreviewKeyUp += userCtrl.OnKeyUp;
and handle it in your UserControl
public void OnKeyUp(object sender, KeyEventArgs e)
{
MyTextBlock.Text = e.Key.ToString();
}

Related

How can I control the loading sequence of controls?

My English skill is poor because I'm not a native English speaker.
My application has a MainWindowView and MainWindowBehavior also MainWindowView has control(Editor) as the following code.
<MainWindowView>
<Grid>
<TabControl>
<TabItem>
<Grid>
<Editor x:Name="editor"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
<i:Interaction.Behaviors>
<behaviors:MainWindowBehavior/>
</i:Interaction.Behaviors>
</MainWindowView>
MainWindowBehavior uses the property of Editor in the LoadedEventHandler of MainWindowView.
The following code shows the above logic.
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.mainWindow = sender as MainWindow;
// run time error
this.mainWindow.editor.Parser.ParsingFailed += Parser_ParsingFailed;
}
But compiler shows run time error because of the value of the Parser property of the editor is null.
I tried to initialize the parser property of the editor at the Constructer, OnApplyTemplate function, Loaded EventHandler but 3 cases all called late than Loaded EventHandler of MainWindow.
And as a result, generate run time error.
I think that the Loaded EventHandler of the editor must be called early more Loaded EventHandler of the MainWindowBehavior. But in fact, the sequence reverse.
I don't know why the sequence reverse.
How can I change the loading sequence as I thought?
Thank you for reading.
Maybe you cannot change the sequence of events, but you can sure change the way you listen to these events. I suggest you to hook-up to an event in your mainWindow that will indicate you when its editor property is set.
Your code would become:
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.mainWindow = sender as MainWindow;
// Here we don't access mainWindow.editor anymore, we hook-up to an event instead
this.mainWindow.OnEditorPropertyChanged += MainWindow_EditorPropertyChanged;
}
private void MainWindow_EditorPropertyChanged(object sender){
{
var mainWindow = sender as MainWindow;
if (mainWindow.editor != null) {
mainWindow.editor.Parser.ParsingFailed += Parser_ParsingFailed;
}
}
And in your MainWindow, make sure to raise an event when its editor property is set, for example:
public delegate void OnEditorPropertyChangedEventHandler(object sender);
public event OnEditorPropertyChangedEventHandler OnEditorPropertyChanged;
// Backing field
private Editor _editor;
public Editor editor {
get => _editor;
set => {
_editor = value;
OnEditorPropertyChanged?.Invoke(this); // Raise the event
}
}
I found the cause of my problem while following your advice.
I think the OnApplyTemplate function of the Editor is called early than the Loaded event of the MainWindowView. But, in fact, the OnApplyTemplate function of the Editor is not called.
I have been apprehending wrong about my problem. I'm sorry...
Now I would make my problem right.
The Editor is Custom-Control. I missed adding code in the Generic.xaml file the following code.
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/WpfControls;component/Themes/Editor.xaml"/>
</ResourceDictionary.MergedDictionaries>
Now I added the above code in the Generic.xaml and the OnApplyTemplate function of Editor is called normally also it is called early than the Loaded event of the MainWindowView.
Thank you for your help and interest in my problem.

How do I set focus to a Usercontrol

I have 50 UserControls that I add to a flowlayoutPanel dynamically.
I need to set focus to a user control but it doesn't work.
I have been searching a lot but can't find any example that I understand.
The only example that I find is this
Setting Focus to a .NET UserControl...?
I tried to use userCtrl.Focus(); but it didn't work.
As I have been reading the usercontrol doesn't like to have focus.
Addition: Now that I understand more of the Control class, I
understand that if you derive from Control you should not subscribe
to its events, but use the On.. functions, like OnEnter. I've
changed my answer accordingly
To Activate any Control, including a UserControl use Control.Select().
If you do this for a TextBox, you'll see that Select ensures that it gets the input focus.
I guess you want to do something with the selected UserControl (the Control that has the focus), for instance, you want to change its appearance, or select any of the controls on it. To do this, your UserControl class has to subscribe to the events Control.Enter and Control.Leave
I have created a UserControl with a CheckBox that is automatically checked whenever the UserControl is selected (has the input focus):
Addition: If you derive from a Control, don't subscribe to events Enter and Leave. Instead override the functions that raise these events: OnEnter / OnLeave.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
protected override void OnEnter(EventArgs e)
{
this.checkBox1.Checked = true;
base.OnEnter(e); // this will raise the Enter event
}
protected override void OnLeave(EventArgs e)
{
this.checkBox1.Checked = false;
base.OnLeave(e); // this will raise the Leave event
}
}
I have a form with a button, and an event handler that is called when the button is clicked:
private void OnButton1Clicked(object sender, EventArgs e)
{
this.userControl1.Select();
}
Now whenever the button is clicked I see that the user control gets the focus because the check box is checked and whenever I click elsewhere the checkbox is unchecked.
You can set focus to a control by using the ActiveControl Property
this.ActiveControl = myUserControl;
Though you did not detail what did you mean it did not work, focusing has many aspects conventionally.
1. Explicit focusing
Calling Focus() method of a control is the same as setting ActiveControl of the container form. If CanFocus returns true (your control and all its parents are visible and enabled), it works; however, you will have no visual feedback, except some indirect hint, eg. the originally focused control (button or textbox) loses the focus.
To visualize the focused state you might want to use some custom paint:
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.Clear(Focused ? SystemColors.Highlight : SystemColors.Control);
}
If you derive directly from Control instead of UserControl, override the following two methods to force a repaint on changing the focused state:
protected override void OnGotFocus(EventArgs e)
{
Invalidate();
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
Invalidate();
}
2. Focusing by the mouse
To receive focus by clicking the control add this line to the constructor:
SetStyle(ControlStyles.Selectable, true);
If you derive directly from Control instead of UserControl, override the OnMouseDown, too:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!Focused)
Focus();
base.OnMouseDown(e);
}
3. Focusing by the keyboard
To receive focus by the TAB key just set the TabStop property to true and adjust the TabOrder property.
Example to focus on textBox1:
textBox1.Select();
you can try tab index of the user control. If you set its tab index to 1 it will be focused once the program start.

Accessing Child control event from parent level wpf

I have a user control named myControl. I have rendered another user control named dialog inside myControl as <uc:dialog x:Name="dialog"> .
I have a button named as myButton inside the dialog user control. I need to get the lostfocus event of myButton from the parent level .ie,myControl code behind.How can I get that? Which is the best way to do that?
var myButton= dialog.FindName("myButton") as Button;
if (myButton!= null)
{
myButton.LostFocus += myButton;
}
I tried like this. But it doesn't work.Why?
You could define such an Event in your UserControl.
Then just register for this event.
public event EventHandler ButtonLostFocusEvent;
private void Button_LostFocus(object sender, RoutedEventArgs e)
{
EventHandler testEvent = this.ButtonLostFocusEvent;
// Check for no subscribers
if (testEvent == null)
return;
testEvent(sender, e);
}

Set focus on TextBox in UserControl

In my program I have a user control that displays data on a window using a content presenter. I would like to simply set the cursor focus on a certain textBox in my window at startup.
Usually I would do this through the code-behind of the window, like this: textBox.Focus();
However, the textBox is defined in the user control, and doesn't seem to work the same way. So far I have tried the same method as above in the user control's code-behind.
Why doesn't this work? How do I set the focus if the textBox is defined in a user control?
What I have tried....:
User Control:
public UserControl()
{
InitializeComponent();
FocusManager.SetFocusedElement(this, textBox);
}
User Control:
public UserControl()
{
InitializeComponent();
textBox.Focusable = true;
Keyboard.Focus(textBox);
}
Give this a try: FocusManager.SetFocusedElement
FocusManager.SetFocusedElement(parentElement, textBox)
or from the msdn website:
textBox.Focusable = true;
Keyboard.Focus(textBox);
Note: You can't set focus in a constructor. If you are, UI Elements have not been created at that point. You should set focus during the Loaded event of your control.
A little bit late but what it really worked for my was
public UserControl()
{
InitializeComponent();
Dispatcher.BeginInvoke(new System.Action(() => { Keyboard.Focus(TextBox); }),
System.Windows.Threading.DispatcherPriority.Loaded);
}
You can try setting the focus in the Loaded or Initialized event of the User control. Eg:
private void MyWpfControl_Load(object sender, EventArgs e)
{
textBox.Focusable = true;
Keyboard.Focus(textBox);
}
Info: Loaded event or Initialized event

Detect if user has moved window in WPF

I have seen a couple posts on this, but they don't necessarily answer my question exactly.
I have a parent window that on its LocationChangedevent, it will grab a child window and move that along with it in a "snapped" fashion. I want to find an event and set a boolean value on the child form that would say "if the user has manually moved me, I will not re-attach to the parent."
Is there a way to detect if the user has moved the child window, rather than my parent window moving it?
I hope this makes sense.
Assuming you are using your child Window's Owner property to associate your parent Window to the child Window I would use an events-based approach.
In your child Window create an event that notifies listeners to disassociate (detach) the child Window from its parent:
public event EventHandler<EventArgs> DetachOwner;
You next need to determine when this event should be raised. For this we'll use three events in the child Window: Activated, Deactivated and LocationChanged.
LocationChanged will tell us when the child Window has moved but we'll need to filter out cases when the child Windows moves because it's following the parent Window. To do this we will need to know if the child Window is moving and it has focus. To track the focus state of the child Window create a bool field called HasFocus and set HasFocus to true in the Window's Activated event handler, and false in the Window's Deactivated handler.
Add this to your child Window:
private void Window_LocationChanged(object sender, EventArgs e) {
if (HasFocus) {
if (DetachChild != null) {
DetachChild(this, EventArgs.Empty);
}
}
}
bool HasFocus;
private void Window_Activated(object sender, EventArgs e) {
HasFocus = true;
}
private void Window_Deactivated(object sender, EventArgs e) {
HasFocus = false;
}
In the parent Window you'll subscribe to the child Window's DetachOwner event when you instantiate the child Window:
_child = new Child();
_child.Owner = this;
// Subscribe to the DetachOwner event.
_child.DetachChild += Child_DetachOwner;
This DetachOwner handler simply sets the child Window's Owner property to null:
void Child_DetachOwner(object sender, EventArgs e) {
((Child)sender).Owner = null;
}
You can expand on this approach to reattach the child Window to it's parent by creating a similar AttachOwner event in the child Window with a handler in the parent Window:
void Child_AttachOwner(object sender, EventArgs e) {
((Child)sender).Owner = this;
}

Categories