WPF Catch Focus Change of Children Controls - wpf

Is there a way for a UserControl to catch focus change on any of its children controls?
I've got a TabControl which has a UserControl on each tab. I am trying to maintain focus on control items when switching between tabs.

You can subscribe to the LostFocus event of each control within the usercontrol.
To automatically do this, you can subscribe on the on initialize and loop through children. However, you'll either need to know your children directly (as in they are member variables), or your usercontrol will have to be/contain an ItemsControl.
If you contain an items control from a template, you'll have to search for the template control using the name you assigned the part.
<ControlTemplate>
<Grid x:Name="PART_ChildrenContainer">
<ItemsPresenter> <!--This will contain your children-->
</Grid>
</ControlTemplate>
Then you'll have to do the following.
var grid = (Grid)this.Template.FindName("PART_ChildrenContainer",...);
foreach(var child in grid.Children)
{
child.PreviewLostKeyboardFocus +=
new System.Windows.Input.KeyboardFocusChangedEventHandler(eventHandler);
}

Related

Try to catch mouse event in HierarchicalDataTemplate

I'm fairly new to wpf and this website. Please give me some mercy if I show some mistakes.
My HierarchicalDataTemplate ,which is my treeview item, consists of multiple components: two textblocks , image , and checkbox with some stack panels for the layout. My MouseEventHandler, which is to catch when user click on textboxes, image , or checkbox , is TreeViewItem.Selected. But when I click on the tiny space between those components, it doesn't trigger TreeViewItem.Selected.
My first initial thought was that I might need to specify event handler on the stack panel which was for the layout of my HierarchicalDataTemplate . However, it didn't rise the event ,even though I made event handler on stack panel specifically.
Could you give me some guidance?
ps. I used binding for IsSelected property but it never notified to change its property
Set Background="Transparent" for topmost layout container inside your HierarchicalDataTemplate.
The following Grid doesn't raise MouseLeftButtonDown event:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200">
</Grid>
But the following does:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200" Background="Transparent">
</Grid>
This is because in the 1st case it doesn't have background and there's nothing to raise MouseLeftButtonDown event. So the event will be raised only if user clicks on some element inside that Grid.

Create Silverlight Control that can be used as the layout root

How can I create a control in silverlight that can be used as the layout root, but still have a "Template" property so I can wrap the users content inside another control using a style?
My current implementation is close, it takes the content that the user places in the control and wraps it but the user has to put a grid or panel in if there is multiple controls for the content.
--Update --
This is the code I'm using that will not work as the rootlayout for multiple children unless the user puts a grid around their content. If I inherit from Grid or Panel I get an error about the DefaultStyleKey property not being available.
public class BusyControl :ContentControl
{
public BusyControl()
{
this.DefaultStyleKey = typeof(BusyControl);
}
}
<Style TargetType="local:BusyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BusyControl">
<telerik:RadBusyIndicator DisplayAfter="0:0:0.5" IsBusy="{Binding IsBusy}" BusyContent="{Binding BusyMessage}">
<ContentPresenter Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
</telerik:RadBusyIndicator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is how I want the user to be able to use my new control with out having to wrap their content in a panel or grid.
<cdc:BusyControl x:Name="BusyControl">
<some:Control x:Name="Control1" />
<some:Control x:Name="Control2" />
</cdc:BusyControl>
Seems to me that what you want is to derive your control from an ItemsControl not a ContentControl. In any ControlTemplate that you use you can place the controls using an ItemsPresenter instead of the ContentPresenter you would have used in a ContentControl.
You can have your control inherit from Panel if you want it to be able to have multiple children. You will have to handle laying out the panel's child controls if you do this. See this MSDN article on creating custom panels.
You can then specify your Template and stick the user content into the template.
I think that if you want multiple controls, i.e. children, as content then you have to use some sort of panel, which is the base class for the .Children property.
Not sure if this is applicable to your situation. I had trouble grasping exactly what's going on in your question. Maybe you can make a custom user control that inherits from ContentControl. As you may or may not know, custom user controls need a default style key. With a custom user control you need to define a template in the default style. Now the template can have a ContentControl somewhere inside of it and its content property should be template binding to the contentcontrol.content property. Or you can override the OnContentChanged function and do whatever you want in that override function (like put a single object in the control by itself... or for multiple objects create a new grid/panel and then set the objects as the grid/panels children for the user and then do what ever it is you are doing with the grid/panel. You would have to set/bind the content property on your new control. Make sense?
I don't know about your error with the default style key, and i don't have any telerik controls, but couldn't you just inherit from your telerik busy indicator? Would something like this work for you, (or put you on the right track).
protected override void OnContentChanged( object oldContent, object newContent )
{
//I dont know how you are assigning content,
//but i would say if it's IEnumerable and count is > 1 it should use your panel
var newMultiContent = newContent as System.Collections.IEnumerable;
if ( newMultiContent!=null && newMultiContent.Cast<object>().Count()>1)
{
var myNewContentContainer = new StackPanel();//or grid or whatever
myNewContentContainer.Children.Clear();
//add children
foreach (var item in newMultiContent.OfType<UIElement>())
myNewContentContainer.Children.Add(item);
//instead of the old content that wasn't what you wanted, use the new content container
base.OnContentChanged( oldContent, myNewContentContainer );
//or maybe try this and call the base method at the beginning...
Content = myNewContentContaint
}
else
base.OnContentChanged( oldContent, newContent );
}

Initiating UserControl via MVVM in WPF / focus issue

I have a few usercontrols loaded into a tabcontrol via MVVM in WPF.
Within the XAML for the usercontrol I am setting focus to a textbox using the FocusManager, however this appears to only work when the first instance of the usercontrol is created.
Just to test I added a loaded event handler to the usercontrol - this is only called on the first instance.
I'm using data templates for the user controls as follows:
<DataTemplate DataType="{x:Type local:UserTypeViewModel}">
<local:UserTypeView />
</DataTemplate>
The textbox is focused as follows:
FocusManager.FocusedElement="{Binding ElementName=txtName}"
Additionally I'm using a global event handler (for the textbox GotFocus event) which selects all the text using a dispatcher.
If anyone has any tips on how to achieve focus with every usercontrol I'd be very grateful.
Thanks, Ben.
Remember that a visual element can only receive focus, if:
It is visible (in a TabControl only one tabitem can be visible at a time
IsFocusable must be set to true (is default false for UserControls)
It has finished loading (as you write - do it in the Loaded event))
I think the first reason is why it only works for the first element.
As for how to achieve it for all controls - you can use a style with an EventSetter for the Loaded event. You need to make a style per type of control though to avoid having to set it for each control.

WPF: Binding items added to UserControl's exposed children

I have a user control that allows items to be added to it by exposing a Grid's Children property. Any control I add shows up fine but when I try to bind a property on the added item to a control in the main window nothing happens (example):
<TextBox Name="txtTest" Text="Success!" />
<mycontrols:CustomUserControl.ExposedGridChildren>
<TextBox Text="{Binding ElementName=txtTest, Path=Text, FallbackValue=fail}"/>
</mycontrols:CustomUserControl.ExposedGridChildren>
This example always results in the TextBox's text showing "fail". Here is how I'm exposing the children in the user control:
public UIElementCollection ExposedGridChildren
{
get { return grdContainer.Children; }
}
Any thoughts? Is it a scope issue? I know I can't name the elements I add to the children because of scope errors. Thanks, Brian.
This might answer your question:
How do you bind a grid's children to a list?
Or as Dr. WPF puts it: (http://drwpf.com/blog/2007/10/15/itemscontrol-a-is-for-abundance/)
Is a Panel an ItemsControl?
No. The logical children of a panel
are UIElements, whereas the logical
children of an ItemsControl (its
Items) can be any CLR objects.
Sidebar: So what is a panel? The main
role of a panel is to provide layout
support for its children. Although a
panel does maintain a collection of
child elements, it is not technically
a WPF “control”… That is, it does not
derive from the Control base class and
it does not support the WPF notion of
templating. Instead, it is a single
element with a single purpose… namely,
to size and position (a.k.a., measure
and arrange) its children.
I was apparently going about this thing all wrong. What I needed to do was create a look-less control and template it to have one (or more) contentpresenter(s).

Passing DataContext to User Control in WPF

I have a usercontrol that acts as a container for a ContentControl.
The user control container has a listview control that I want to use to update controls in a dynamically added user control assigned to the ContentControl.
IOW, as I scroll through the listview control, the textbox's in the UC assigned to the ContentControl should update.
I've done this when everything is in one page no problem, but am having a hard time passing the ListView as a datacontext to the dynamically added UC.
How can this be done?
In XAML
<ListView x:name="lstIncidents">
</Listview>
<ContentControl x:Name="PlaceHolder"></ContentControl>
In Codebehind...
PlaceHolder.Content = new LocationView();
When adding the "LocationView" to the PlaceHolder.Content, I need to pass "lstIncidents" as the datacontext so the textboxs in "LocationView" refresh as the ListView is navigated.
Controls inherit their DataContext from their parent, so try setting the DataContext on the ContentControl:
<ContentControl
x:Name="PlaceHolder"
DataContext="{Binding SelectedItem,ElementName=lstIncidents}" />

Resources