Ok so I have an issue where I have been trying to create a custom control in Silverlight. It is simply a button that when pressed opens a dropdown menu, however the dropdown menu is a child control. I have a property (isDropDownOpen) that controls whether the dropdown is open. I want that when the control loses focus that the property goes to false so implemented the following override in my class.
protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
{
base.OnLostFocus(e);
Object focusedElement = FocusManager.GetFocusedElement();
FrameworkElement element = focusedElement as FrameworkElement;
if (element != null)
{
IsDropDownOpen = false;
}
}
The problem is that the control looses focus when the child control gains focus so the menu closes as soon as I click on anything other than the button. I can’t really see how to work around this, any ideas?
EDIT: Essentially what I wish to do is check if the item is a child before actually changing the property. In wpf I would do something using 'IsChild()' or '.containsFocus()' however these do not appear to e available in silverlight...
Using silverlight 5.0.
I found the following link to be a solution to my issue.
http://icircusmonkey.wordpress.com/2012/08/26/silverlight-how-to-close-the-popup-when-user-clicks-outside-of-the-control/
The solution in my case was to find the ancestor of the control (going up to the window) and subscribing an event handler to close the menu when a click is registered on the window/ancestor. Works perfectly.
Related
I am using VS2012, C#, Winforms.
My application generates about 100 label controls at runtime. I want the user to right-click on a label control, bring up a context menu strip. I have this part working. But I am having trouble determining which control is being clicked on when I try to respond to a the context menu item click event. How can I pass on the control to the menu item click event?
I am using this code to determine the source control but it always causes a null exception:
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
Control sourceControl = new Control();
sourceControl = contextMenuStrip1.SourceControl;
MessageBox.Show(sourceControl.Text);
}
sourceControl always get a null from contextMenuStrip1.SourceControl;
The SourceControl property can be null, as this property is best used during the opening event. There are a couple of workarounds available. Check these two out on StackOverflow:
ContextMenuStrip.Owner Property null When Retrieving From Nested ToolStripMenuItem
Get the SourceControl of my ContextMenuStrip when I use the shortcut key
I have a program with a main window in which a user may alter data. The program also has a to-do-dialog, in which the user may see a list of stuff to do - and this dialog has it's own datacontext (connection to database) but needs to show let updated data.
I did not set UpdateSourceTrigger in all my fields in the main window to PropertyChanged (this wouldn't be wanted in all cases anyway because of functionality in the main window).
But I need the main window to do a updatesource on the "active" field in case the user activates the to-do-dialog so that the data may be saved to the database and re-read in the to-do-dialog.
If there were a "Deactivating" event on the window, I could use that to simply set focus to something else (forcing whatever currently focussed control to update it source). But no Deactivating event exists, only Deactivated which happens after the fact. And setting focus in the deactivated main window messes up up the activation of the to-do-dialog. Also, I cannot do this in the to-do-dialogs Actived event. A Activating event would have helped - but that does not exist.
Any good ideas how to force updatesource on my main window before the to-do-dialog gets activated (either by tabbing to it or by clicking it with the mouse)?
This kind of goes off of #AkselK's answer, but I find the keyboard focused control using the Keyboard.FocusedElement property and update the right bindings based on whether its a TextBox, Selector, or ToggleButton. I expected the Keyboard.FocusedElement to tell me the window I was swapping to is what has focus, but it actually does give me the control I was in inside the deactivating window.
void MyWindow_Deactivated(object sender, EventArgs e)
{
if(Keyboard.FocusedElement is TextBox)
{
(Keyboard.FocusedElement as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
else if(Keyboard.FocusedElement is Selector)
{
(Keyboard.FocusedElement as Selector).GetBindingExpression(Selector.SelectedItemProperty).UpdateSource();
//todo: should this also update SelectedValueProperty?
}
else if(Keyboard.FocusedElement is ToggleButton)
{
(Keyboard.FocusedElement as ToggleButton).GetBindingExpression(ToggleButton.IsCheckedProperty).UpdateSource();
}
}
I am unsure if the Selector portion should also update the binding for SelectedValue.
You do not need to set Focus to a different element to update the binding. Instead, you can use the GetBindingExpression method:
valueTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
This might work in the Deactivating event.
I have an MVVM setup with a mainwindow that contains a ContentControl.
I set this to a particular viewmodel which then maps to a view.
A view is a usercontrol.
I want to be able to set the default keyboard focus to a default element in the usercontrol(View) when it loads so the application can eventually be driven just by using up, down, left, right and enter.
Some of my failed attempts are setting
FocusManager.FocusedElement="{Binding ElementName=DefaultElement}"
in my content control tag. This sets the logical focus but not the keyboard focus
I'd rather keep the solution in xaml if possable but have tried placing the following in code behind.
Keyboard.Focus(DefaultElement);
This does not work but if I popup a message box first it does. I'm a little confused as to why.
MessageBox.Show(Keyboard.FocusedElement.ToString());
Keyboard.Focus(DefaultElement);
EDIT::::
I just placed this in my onloaded event of my user control. It seems to work but can anyone see any issues that might arrise at this priority level. I.E a circumstance when the action will never run?
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
Keyboard.Focus(DefaultElement);
}));
It seems that this wpf the you have to implement a workaround on a case by case basis. The solution that seemed to work best, most of the time for me was to insert the focus code inside the dispatcher when OnVisible was changed. This sets the focus not only when the View/Usercontrol loads but also if you a changing Views by way of Visibility. If you Hide and then Show a ContentControl that is mapped to your ViewModels then the Loaded event won't fire and you'll be forced to Mouse input, or tabbing (Not so good if you want to navigate your app with a remote control).
VisibilityChanged will always fire however. This is what I ended up with for my listbox.
private void ItemsFlowListBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
ItemsFlowListBox.Focus();
ItemsFlowListBox.ScrollIntoView(ItemsFlowListBox.SelectedItem);
}));
}
}
I had the same symptom for a WPF UserControl hosted in a Winforms application. Just wanted to note I was about to try this solution when I found a normal TabIndex in the Winforms app fixed it
Per How to set which control gets the focus on application start
"The one with the minimum tab index automatically gets the focus
(assuming the TabStop property is set to true). Just set the tab
indices appropriately."
It's a tricky one with no easy answer. I'm currently doing this, although I'm not sure I like it:
public MyView()
{
InitializeComponent();
// When DataContext changes hook the txtName.TextChanged event so we can give it initial focus
DataContextChanged +=
(sender, args) =>
{
txtName.TextChanged += OnTxtNameOnTextChanged;
};
}
private void OnTxtNameOnTextChanged(object o, TextChangedEventArgs eventArgs)
{
// Setting focus will select all text in the TextBox due to the global class handler on TextBox
txtName.Focus();
// Now unhook the event handler, since it's no longer required
txtName.TextChanged -= OnTxtNameOnTextChanged;
}
And in case you're wondering what the global class handler does, it's this:
protected override void OnStartup(StartupEventArgs e)
{
...
// Register a global handler for this app-domain to select all text in a textBox when
// the textBox receives keyboard focus.
EventManager.RegisterClassHandler(
typeof (TextBox), UIElement.GotKeyboardFocusEvent,
new RoutedEventHandler((sender, args) => ((TextBox) sender).SelectAll()));
which auto selects TextBox text when receiving keyboard focus.
I would like to create custom control that will look like standard WPF ComboBox, but instead of instead of having an ItemsPresenter in the popup there will be another custom control. So, I created a new class that derives from System.Windows.Controls.Control, added a IsDropDownOpen property and created a style that is actually a copy of default ComboBox style (main idea is that the Popup.IsOpen and ToggleButton.IsPressed properties are bound to the IsDropDownOpen property of the control).
The problem is that the Popup is not closed when I click outside of the control.
I took a look at the ComboBox class in the Reflector and found out that ComboBox used some logic to update the IsDropDownOpen property when it loses mouse capture. But that code uses some internal classes. Is there any alternative way to determine if the user clicked outside of the control and close the Popup?
UPD: I didn't find the way to attach a file to post, so I uploaded sample project here
There is a custom control that looks like ComboBox, but it has a TreeView in a popup. When you open popup and click outside of the control it closes automatically, but if you open popup, expand 'Item2' and then click outside the popup isn't closed. The question is how to fix this?
There is the Control.LostFocus event, maybe handling that would be sufficient for this.
This code solves the problem.
In the static contructor:
EventManager.RegisterClassHandler(typeof(CustomComboBox), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnMouseCaptureLost));
Event handler implementation:
private void OnMouseCaptureLost(object sender, MouseEventArgs e)
{
if (Mouse.Captured != _container)
{
if (e.OriginalSource != _container)
{
Mouse.Capture(_container, CaptureMode.SubTree);
e.Handled = true;
}
}
}
I am trying to make a nice "drag and drop zone" in WPF that is displayed in the adorner layer when something is being dragged into the main application. The problem is that I do not get any events from my adorner, even though it according to documentation should receive all input events since it is in a higher z-order.
To debug my problem I created a really simple example where I have a user control with only a button in it. This user control is displayed in the adorner layer, but I cannot click the button. Why? What have I done wrong?
My adorner class is constructed like this:
public ShellOverlayAdorner(UIElement element, AdornerLayer adornerLayer)
:base(element)
{
_adornerLayer = adornerLayer;
_overlayView = new AdornedElement();
_overlayView.AllowDrop = true;
_adornerLayer.Add(this);
}
and is created in the main window by
private void Window_Loaded(object sender, RoutedEventArgs e)
{
adornerLayer = AdornerLayer.GetAdornerLayer(MyTopGridWithButtonInIt);
ShellOverlayAdorner shell = new ShellOverlayAdorner(MyTopGridWithButtonInIt, adornerLayer);
}
I do not get any events at all from my control, i.e. no mouse clicks, mouse over, button clicks. I cannot even click the button in the adorner layer. What have I done wrong?
I don't know if you already tried that:
If you want the element added to react to events, I think that the element must be bound to the visual tree of the adorner.
The way to do it is to use a VisualCollection, intitialized to the adorner itself, or at least, this way it seems to be working:
VisualCollection visualChildren;
FrameworkElement #object;
public CustomAdorner(UIElement adornedElement) :
base(adornedElement)
{
visualChildren = new VisualCollection(this);
#object = new Button {Content = "prova"};
visualChildren.Add(#object);
}
protected override Visual GetVisualChild(int index)
{
return visualChildren[index];
}
This way the events are correctly routed.
I just had the same issue. Following the advice from MSDN sorted it for me:
Adorners receive input events just
like any other FrameworkElement.
Because an adorner always has a higher
z-order than the element it adorns,
the adorner receives input events
(such as Drop or MouseMove) that may
be intended for the underlying adorned
element. An adorner can listen for
certain input events and pass these on
to the underlying adorned element by
re-raising the event.
To enable pass-through hit testing of
elements under an adorner, set the hit
test IsHitTestVisible property to
false on the adorner.
i.e In the adorner itself, make sure IsHitTestVisible = false