WPF routed events handling in UserControl - wpf

I have a WPF UserControl that contains a button. I also have a WPF Window that contains a button.
In both the UserControl and the Window I place the following line in XAML:
UIElement.PreviewMouseLeftButtonDown="OnPreviewMouseLeftButtonDown"
and in 'OnPreviewMouseLeftButtonDown' I've place a debug print that displays args.Source.
When I click on the button that is inside the window, I get the button as the EventArgs source. However, when I click the button that inside the UserControl (which is also inside a window, so I could test it, but not the same window) I get the UserControl as the EventArgs source.
I tired to see if there is some decorator around the UserControl (using snoop) but it seems straight forward.
I can't understand what is so special about UserControl in WPF that I don't get the right sender. Can someone please explain to me what am I missing?

While the question is old, I recently ran into a similar problem myself, involving the ContextMenuOpening event. Some amount of searching yielded the source code to UserControl here, which contains the following section:
// Set the EventArgs' source to be this UserControl
internal override void AdjustBranchSource(RoutedEventArgs e)
{
e.Source=this;
}
So, apparently UserControl sets the Source of ANY routed event to itself. I have no idea why it does this, though...

Use e.OriginalSource as mentioned above. If you need to find the button, then you can use VisualTreeHelper.GetParent to find the actual Button control.

Related

WPF & MVVM, right way to do it

So I am doing my first WPF MVVM app. Just learning the right principle of MVVM, but there are some things that I don't understand ...
I already have several user controls defined.
First question is what is better to use, UserControl or DataTemplates to change content of the MainWindow?
And how to make "Binding" in the "MainWindow.xaml" to change UserControl/DataTemplates when button is pressed?
For example, When "next" button is pressed then contents of main window disappear and content of user control comes on the screen of "MainWindow.xaml".
Maybe with "" binding, to disable it and enable it?
I found some example which function on DataTemplate A Simple MVVM Example. It helped me to implement some things, but I see some discussions over "UserControl" vs. "DataTemplate" and how to do it? So now I am confused :)
I recently made a WPF application with the MVVM pattern, and I did the following:
I have one 'Window', the mainwindow, and in this window all UserControls are loaded.
Every UserControl has a different Viewmodel, for examples a "GeneralSettingsUserControl" has a GeneralSettingsViewModel for validation and databinding.
Every UserControl has its own codebehind where data is bound to its ViewModel
The following code I found on the internet (I don't know the url anymore) but for me it did the trick to change de ContentControl in the mainwindow.
Switcher.cs:
public static mainWindow mainWindow;
public static void switchPage(UserControl p_objNewPage)
{
mainWindow.navigate(p_objNewPage);
}
mainWindow.xaml.cs
public void navigate(UserControl nextPage)
{
PageContent.Children.Clear();
PageContent.Children.Add(nextPage);
PageContent.LastChildFill = true;
}
PageContent is the name of the Grid where the main content is located. In every UserControl you can call the Switcher.switchPage(new UserControl) to change the content of the window. So when you click a button you can call this method.
Hope it helps and good luck.

WPF How to handle Cut, Copy, Paste toolbar buttons with UserControls and WindowsFormsHost

I'm hoping someone out there can help me. I'm trying to convert a legacy winforms app to WPF using MVVM. I've broken up the main window of the application into 4 main UserControls. The UserControls display different types of data objects and each UserControl has it's own ViewModel. Some of the data objects are inter-changeable between the different UserControls, for instance 'User Control 1' can contain strings objects and so can 'User Control 2 and 'User Control 3' (see diagram below).
My question is how can I handle the Cut, Copy, Paste commands in the toolbar? To possibly make things more complicated, each UserControl can contain a selected object at the same time as the other UserControls contain a selected object and User Control 2 is a WindowsFormsHost wrapper around a winforms control.
So far I've tried using ApplicationCommands but I can't even get them to fire. I've pasted a snippet of the code I thought would work using the ApplicationCommands below. Any help with this would be really appreciated.
<Button Command="ApplicationCommands.Cut" />
and on the UserControls
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut" Executed="executed_Cut" CanExecute="canExecute_Cut" />
</UserControl.CommandBindings>
and finally in the UserControl's code behind (I know this isn't great)
public void executed_Cut(Object sender, ExecutedRoutedEventArgs e)
{
//execute code here
}
public void canExecute_Cut(Object sender, CanExecuteRoutedEventArgs e)
{
//can execute code here
}
I successfully use the behavior approach described in this question to avoid putting any code in my view model. Then whenever focus goes to a control which has a copy/paste behavior defined the toolbar cut/copy/paste buttons "light up" accordingly.

Usercontrol with treeview selected event that refreshed another usercontrol not working

I'm relatively new to WPF programming and can't seem to get this working correctly. This is in my mind something rather simplistic that i would like to get working so I didn't try the MVVM framework and decided to keep it simple.
I have a usercontrol treeview with report paths. I have another usercontrol with a reportviewer. Selecting a report path in the treeview should refresh the reportviewer.
I can "call" the method from the treeview usercontrol for refreshing the reportviewer in the report usercontrol but nothing happens.
I was reading other posts and some seem to suggest a button event to a delegate that will allow the other usercontrol method to be executed. I'm not using a button event so I'm a bit puzzled.
I thought I could do something like this:
private void treeViewMenu_Selected(object sender, RoutedEventArgs e)
{
//Get the selected item
....
//Call the method of another user control
ReportUserControl ruc = new ReportUserControl();
ruc.RefreshReport();
}
Obviously that didn't work...I know that it shouldn't be too difficult but i am in a brain freeze moment and can't seem to get it working.
Any suggestions would be greatly appreciated.
Thanks!

want to create a listbox with a textblock and a button itemtemplate

alt text http://www.freeimagehosting.net/uploads/abbcae9cd3.png
I want to create a listbox like the one shown in the image,
i just dont know how will i get the button(the red close button) click event for each item in the listbox
Can anyone point me to a good tutorial on this
Thanks
Create a button as usual, with a Click handler:
<Button Click="MyClickHandler">...</Button>
Now, in your click handler, you have the sender argument, which will contain the button that was clicked. You want the item that the button belongs to, though. First, you need to the ListViewItem that contains the button. There's several implementations of how to find a visual ancestor around, here's the first result I found. Add that extension method to a static class somewhere. So now your click handler looks like this:
private void MyClickHandler(object sender, RoutedEventArgs e)
{
ListViewItem parent = ((DependencyObject)sender).TryFindParent<ListViewItem>();
}
Now you need to retrieve the item that goes with this ListViewItem. This, thankfully, is much simpler (though I have no idea what it is, since your post doesn't say):
object ItemRelatedToButton = TheListBoxInQuestion.ItemContainerGenerator.ItemFromContainer(parent);
Replace object with the type that your ListBox actually contains, and the names with ones that are actually relevant. At this point, you can now interact with the item that the button is "attached" to.
You may also want to make sure parent isn't null, just in case something weird happened, and in theory you should make sure ItemRelatedToButton isn't null either.
If you don't want to write code behind, like MVVM would suggest, you have two choices:
1. if the control in your datatemplate supports ICommand, send the DataContext as the CommandParameter. Then the parameter will be passed into you command execute method.
2. If the control doesn't have an ICommand property, create an attached property, in the PropertyChangedCallback call back, your can hook a command to your clicked event. Then again, in Xaml you can pass the Datacontext as the CommandParameter.
Sorry for not elaborating further, but if you search for MVVM, Command and Attached, you should find tons of detail informations.

Can't set focus to a child of UserControl

I have a UserControl which contains a TextBox. When my main window loads I want to set the focus to this textbox so I added Focusable="True" GotFocus="UC_GotFocus" to the UserControls definition and FocusManager.FocusedElement="{Binding ElementName=login}" to my main windows definition. In the UC_GotFocus method i simply call .Focus() on the control i want to focus on but this doesn't work.
All i need to do is have a TextBox in a UserControl receive focus when the application starts.
Any help would be appreciated, thanks.
I recently fixed this problem for a login splash screen that is being displayed via a storyboard when the main window is first loaded.
I believe there were two keys to the fix. One was to make the containing element a focus scope. The other was to handle the Storyboard Completed event for the storyboard that was triggered by the window being loaded.
This storyboard makes the username and password canvas visible and then fades into being 100% opaque. The key is that the username control was not visible until the storyboard ran and therefore that control could not get keyboard focus until it was visible. What threw me off for awhile was that it had "focus" (i.e. focus was true, but as it turns out this was only logical focus) and I did not know that WPF had the concept of both logical and keyboard focus until reading Kent Boogaart's answer and looking at Microsoft's WPF link text
Once I did that the solution for my particular problem was straightforward:
1) Make the containing element a focus scope
<Canvas FocusManager.IsFocusScope="True" Visibility="Collapsed">
<TextBox x:Name="m_uxUsername" AcceptsTab="False" AcceptsReturn="False">
</TextBox>
</Canvas>
2) Attach a Completed Event Handler to the Storyboard
<Storyboard x:Key="Splash Screen" Completed="UserNamePassword_Storyboard_Completed">
...
</Storyboard>
and
3) Set my username TextBox to have the keyboard focus in the storyboard completed event handler.
void UserNamePassword_Storyboard_Completed(object sender, EventArgs e)
{
m_uxUsername.Focus();
}
Note that calling item.Focus() results in the call Keyboard.Focus(this), so you don't need to call this explicitly. See this question about the difference between Keyboard.Focus(item) and item.Focus.
Its stupid but it works:
Pop a thread that waits a while then comes back and sets the focus you want. It even works within the context of an element host.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Threading.ThreadPool.QueueUserWorkItem(
(a) =>
{
System.Threading.Thread.Sleep(100);
someUiElementThatWantsFocus.Dispatcher.Invoke(
new Action(() =>
{
someUiElementThatWantsFocus.Focus();
}));
}
);
}
Just recently I had a list-box that housed some TextBlocks. I wanted to be able to double click on the text block and have it turn into a TextBox, then focus on it and select all the text so the user could just start typing the new name (Akin to Adobe Layers)
Anyway, I was doing this with an event and it just wasn't working. The magic bullet for me here was making sure that I set the event to handled. I figure it was setting focus, but as soon as the event went down the path it was switching the logical focus.
The moral of the story is, make sure you're marking the event as handled, that might be your issue.
“When setting initial focus at application startup, the element to
receive focus must be connected to a PresentationSource and the
element must have Focusable and IsVisible set to true. The recommended
place to set initial focus is in the Loaded event handler"
(MSDN)
Simply add a "Loaded" event handler in the constructor of your Window (or Control), and in that event handler call the Focus() method on the target control.
public MyWindow() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(MyWindow_Loaded);
}
void MyWindow_Loaded(object sender, RoutedEventArgs e) {
textBox.Focus();
}
since i tried a fuzquat's solution and found it the most generic one, i thought i'd share a different version, since some complained about it looking messy. so here it is:
casted.Dispatcher.BeginInvoke(new Action<UIElement>(x =>
{
x.Focus();
}), DispatcherPriority.ApplicationIdle, casted);
no Thread.Sleep, no ThreadPool. Clean enough i hope.
UPDATE:
Since people seem to like pretty code:
public static class WpfExtensions
{
public static void BeginInvoke<T>(this T element, Action<T> action, DispatcherPriority priority = DispatcherPriority.ApplicationIdle) where T : UIElement
{
element.Dispatcher.BeginInvoke(priority, action);
}
}
now you can call it like this:
child.BeginInvoke(d => d.Focus());
WPF supports two different flavors of focus:
Keyboard focus
Logical focus
The FocusedElement property gets or sets logical focus within a focus scope. I suspect your TextBox does have logical focus, but its containing focus scope is not the active focus scope. Ergo, it does not have keyboard focus.
So the question is, do you have multiple focus scopes in your visual tree?
I found a good series of blog posts on WPF focus.
Part 1: It’s Basically Focus
Part 2: Changing WPF focus in code
Part 3: Shifting focus to the first available element in WPF
They are all good to read, but the 3rd part specifically deals with setting focus to a UI element in a UserControl.
Set your user control to Focusable="True" (XAML)
Handle the GotFocus event on your control and call yourTextBox.Focus()
Handle the Loaded event on your window and call yourControl.Focus()
I have a sample app running with this solution as I type. If this does not work for you, there must be something specific to your app or environment that causes the problem. In your original question, I think the binding is causing the problem.
I hope this helps.
After having a 'WPF Initial Focus Nightmare' and based on some answers on stack, the following proved for me to be the best solution.
First, add your App.xaml OnStartup() the followings:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
new RoutedEventHandler(WindowLoaded));
Then add the 'WindowLoaded' event also in App.xaml :
void WindowLoaded(object sender, RoutedEventArgs e)
{
var window = e.Source as Window;
System.Threading.Thread.Sleep(100);
window.Dispatcher.Invoke(
new Action(() =>
{
window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}));
}
The threading issue must be use as WPF initial focus mostly fails due to some framework race conditions.
I found the following solution best as it is used globally for the whole app.
Hope it helps...
Oran
I converted fuzquat's answer to an extension method. I'm using this instead of Focus() where Focus() did not work.
using System;
using System.Threading;
using System.Windows;
namespace YourProject.Extensions
{
public static class UIElementExtension
{
public static void WaitAndFocus(this UIElement element, int ms = 100)
{
ThreadPool.QueueUserWorkItem(f =>
{
Thread.Sleep(ms);
element.Dispatcher.Invoke(new Action(() =>
{
element.Focus();
}));
});
}
}
}
I've noticed a focus issue specifically related to hosting WPF UserControls within ElementHosts which are contained within a Form that is set as an MDI child via the MdiParent property.
I'm not sure if this is the same issue others are experiencing but you dig into the details by following the link below.
Issue with setting focus within a WPF UserControl hosted within an ElementHost in a WindowsForms child MDI form
I don't like solutions with setting another tab scope for UserControl. In that case, you will have two different carets when navigating by keyboard: on the window and the another - inside user control. My solution is simply to redirect focus from user control to inner child control. Set user control focusable (because by default its false):
<UserControl ..... Focusable="True">
and override focus events handlers in code-behind:
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
MyTextBox.Focus();
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
Keyboard.Focus(MyTextBox);
}
What did the trick for me was the FocusManager.FocusedElement attribute. I first tried to set it on the UserControl, but it didn't work.
So I tried putting it on the UserControl's first child instead:
<UserControl x:Class="WpfApplication3.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid FocusManager.FocusedElement="{Binding ElementName=MyTextBox, Mode=OneWay}">
<TextBox x:Name="MyTextBox"/>
</Grid>
... and it worked! :)
I have user control - stack panel with two text boxes.The text boxes were added in contructor, not in the xaml. When i try to focus first text box, nothing happend.
The siggestion with Loaded event fix my problem. Just called control.Focus() in Loaded event and everthing.
Assuming you want to set focus for Username textbox, thus user can type in directly every time it shows up.
In Constructor of your control:
this.Loaded += (sender, e) => Keyboard.Focus(txtUsername);
After trying combinations of the suggestions above, I was able to reliably assign focus to a desired text box on a child UserControl with the following. Basically, give focus to the child control and have the child UserControl give focus to its TextBox. The TextBox's focus statement returned true by itself, however did not yield the desired result until the UserControl was given focus as well. I should also note that the UserControl was unable to request focus for itself and had to be given by the Window.
For brevity I left out registering the Loaded events on the Window and UserControl.
Window
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
ControlXYZ.Focus();
}
UserControl
private void OnControlLoaded(object sender, RoutedEventArgs e)
{
TextBoxXYZ.Focus();
}
I set it in the PageLoaded() or control loaded, but then I'm calling WCF async service and doing stuff that seems to lose the focus. I have to to set it at the end of all the stuff I do. That's fine and all, but sometimes I make changes to the code and then I forget that I'm also setting the cursor.
I had same problem with setting keyboard focus to canvas in WPF user control.
My solution
In XAML set element to Focusable="True"
In element_mousemove event create simple check:
if(!element.IsKeyBoardFocused)
element.Focus();
In my case it works fine.

Resources