How to check if AutomationElement has focus? - c#

I'm doing integration tests with help of UI Automation. I need to check if a user control has focus (IsFocused = true). Is it possible ? I tried using AutomationElement.FocusedElement, but as far as I checked it returns different control (probably outside of my app).
My control:
<UserControl GotFocus="UserControl_GotFocus" Focusable="True">
<DockPanel>
<Button DockPanel.Dock="Right" Content=">" IsTabStop="False" Focusable="False" Click="TextButton_Click" />
<TextBox Text="{Binding Text}" x:Name="textBox" />
</DockPanel>
code behind:
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
{
textBox.Focus();
Keyboard.Focus(textBox);
}

You can get the AutomationElement associated with the control you care about, then get the AutomationElement.FocusedElement and compare them. If they are the same, then it is focused.
Also, AutomationElement.Current should have a HasKeyboardFocus property. You can see if that gives you the information you need.

Related

WPF Copied Button Click not firing

I'm quite new to c# wpf and have a problem.
I have used the answer from this post to duplicate a Grid control. The grid control contains a button. It looks like it is being duplicated correctly.
When the original control's button is pressed, the click event is handled which calls a method in the window's code.
When the copy of the control's button is pressed, the click event is not fired and the method is not called. This is confusing me as I want it to call that same method.
Maybe the event handling data is not being copied properly? Is there a way around this?
Both the origional grid and copied grid (containing the buttons) are children of another grid.
Edit:
This is the xaml for the origional grid which contains a button:
<Grid Name="TempTab" DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5,5,5,0">
<Rectangle Fill="Black" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke ="White" Margin="0,0,-2,0">
</Rectangle>
<Grid>
<DockPanel LastChildFill="False">
<TextBlock Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="3,0,3,3">Some Text</TextBlock>
<Button Width="50" BorderBrush="{x:Null}" Foreground="{x:Null}" BorderThickness="0" Margin="3,0,0,0" Click="tabdowntest">
<Button.Background>
<ImageBrush ImageSource="TopMenuBar_Close.png" Stretch="Uniform"/>
</Button.Background>
</Button>
</DockPanel>
</Grid>
</Grid>
This grid is a child of a DockPanel with name 'TabsDock'.
It is being copied with the following code:
string gridXaml = XamlWriter.Save(TempTab);
StringReader stringReader = new StringReader(gridXaml);
XmlReader xmlReader = XmlReader.Create(stringReader);
Grid newTab = (Grid)XamlReader.Load(xmlReader);
TabsDock.Children.Add(newTab);
This is the code for the 'Click' event handler which should be called when the either the origional or the copied button's are pressed. But it is only called for the origional:
private void tabdowntest(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Button Pressed");
}
The bindigs are not set, you need to set them (comment in the orig post):
To be clear, this is only half the solution (as it stood back in 08). This will cause bindings to be evaluated and the results be serialized. If you wish to preserve bindings (as the question asked) you must either add a ExpressionConverter to the Binding type at runtime (see the second part of my question for the relevant link) or see my own answer below for how to do it in 4.0.

ToolBar OverflowPanel remains open

I have a ToolBar with a ItemsTemplate which works fine until the OverflowPanel is Available.
The OverflowPanel does not close if i select one of the context actions.
The Problem only occurs if the Items are added via the ItemsSource binding:
<ToolBar ItemsSource="{Binding ContextActionViewModels}"
Background="Transparent"
ToolBarTray.IsLocked="True"
FocusManager.IsFocusScope="False">
<ToolBar.ItemTemplateSelector>
<views:ContextActionTemplateSelector>
<views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<DataTemplate DataType="{x:Type viewModels:SimpleContextActionViewModel}">
<Button Name="Button"
Command="{Binding ActionCommand}"
Style="{StaticResource ToolBarButtonStyle}"
ToolTip="{userInterface:Translation Binding={Binding ToolTip}}">
<ContentControl Template="{Binding Icon,Converter={StaticResource NameToResourceConverter}}"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</DataTemplate>
</views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<!-- Multiple DataTemplates follow!-->
Why is the DataTemplate / ItemTemplteSelector not working properly. While hardcoded Buttons in XAML work properly?
I uploaded a full sample that illustrates what is not working here:
Just Resize the window and try invoking one off the buttons in the OverflowPanel. While the 'ICommand' is executed properly the Popup stays open.
In the .NET framework source you can find the method that handles the closing behavior of OverflowPanel for ToolBar class:
private static void _OnClick(object e, RoutedEventArgs args)
{
ToolBar toolBar = (ToolBar)e;
ButtonBase bb = args.OriginalSource as ButtonBase;
if (toolBar.IsOverflowOpen && bb != null && bb.Parent == toolBar)
toolBar.Close();
}
When you define a DataTemplate and use ItemsSource property, the Parent property of the created button becomes null and the if check fails. This is the expected behavior of DataTemplate as described here:
For templates, the Parent of the template eventually will be null. To get past this point and extend into the logical tree where the template is actually applied, use TemplatedParent.
As a solution you can set the IsOverflowOpen property to false when you click any of the buttons:
<ToolBar Name="SomeToolBar" ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate DataType="local:ItemViewModel">
<Button Command="{Binding Command}" Content="{Binding Name}" Click="ButtonBase_OnClick"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SomeToolBar.IsOverflowOpen = false;
}

Tagging in Windows 8

As hard as I try I can not find how to reference my tag in my CS file.
In my XAML file I have the following
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="User Id" Margin="30" Foreground="White" FontSize="25" />
<TextBox x:Name="logInUserIdText" IsSpellCheckEnabled="True" Height="40" Margin="13,1" Width="408" InputScope="EmailSmtpAddress" FontFamily="Global User Interface" KeyDown="logInUserIdKeyDown" Tag="1" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="Password" Margin="20" Foreground="White" FontSize="25" />
<PasswordBox x:Name="logInPasswordText" IsPasswordRevealButtonEnabled="True" Height="40" Margin="5,1" Width="408" KeyDown="logInPasswordKeyDown" Tag="2"/>
</StackPanel>
Now in my CS file I have the following
private void logInUserIdKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
{
logInUserIdString = logInUserIdText.Text;
Debug.WriteLine("aa");
logInPasswordText.Focus(FocusState.Programmatic);
}
}
private void logInPasswordKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
{
logInPasswordString = logInPasswordText.Password;
Debug.WriteLine("bb");
}
}
And the issue is that regardless of which box is in focus, if I hit ENTER on the keyboard both methods get fired and the output is aa bb which are the debug statements in each method.
So I assume that since I set the tag value in XAML I should be able to do something like
if (e.Key == Virtual.Enter)&&(e.Tag == 1)
Debug.WriteLine("loginuserid method only called");
But I can not, it wont let me reference the tag in the CS file. Why not?
Any help would be much appreciated.
What happen to you seems impossible. To inspect more closely, try to run your application in debug mode, put breakpoint at the beginning of both method. Then, when execution hit breakpoint, you can see which control trigger that event from sender parameter (I prefer to use visual studio watch window to see content of variable).
The same concept can be used to get Tag value :
var s = (FrameworkElement)sender;
var tagValue = (string)s.Tag;
But I suggest to fix the original problem rather than workaround it with using Tag value.

Silverlight: Layout changes when I call NavigationService.Navigate

This is a really weird bug. I have no idea why it could be happening. I know that posting it here is a bit of a long-shot, but I'm out of other ideas.
I have two ListBoxs that act as menus.
<ListBox Margin="56,8,15,0" FontSize="64"
ItemsSource="{Binding FavoriteSections}"
SelectionChanged="MenuList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Remove" Click="FavoritesContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="sectionList" Margin="56,8,15,0" FontSize="64"
SelectionChanged="MenuList_SelectionChanged"
ItemsSource="{Binding SectionViewModels}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Add to favorites" Click="SectionContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The bug exists across both of them.
When the selection changes on either menu, this method is called:
void MenuList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 0)
{
return;
}
Uri page = null;
object selected = e.AddedItems[0];
if (selected is NavigableItem)
{
NavigableItem selectedItem = (NavigableItem)selected;
page = selectedItem.Page;
}
else if (selected is SectionViewModel)
{
SectionViewModel selectedVM = (SectionViewModel)selected;
page = selectedVM.Section.Page;
}
Debug.Assert(page != null, "What is the type of `selected`?");
// if I comment out this line, the problem goes away:
NavigationService.Navigate(page);
ListBox selectedBox = (ListBox)sender;
selectedBox.SelectedIndex = -1;
}
If I comment out the NavigationService.Navigate() line, the problem goes away. If I replace the line with a different URI, the problem remains.
About 70% of the time, when I click on a menu item, the content jumps all over the page. (The remaining 30%, no bug occurs.) It happens too quickly to see what's really going on, but different UI elements overlap each other.
This only occurs the first time I click on something in those menus during the app's lifetime. If I hit "back" then select a menu item again, the problem will not occur.
What could be happening here? I really have no idea. The code-behind doesn't have a OnNavigatedFrom method, so I don't think it's a problem there.
I'm using Silverlight for Windows Phone 7
Update: Mysteriously, I can't seem to reproduce this in the debugger - only after deploying the app and running it in the emulator unattached. ???
Update 2: The bug appears when NavigationService.Navigate() is called from the Click event handler of a button, as well:
<Button Content="Foo" Click="Button_Click" Grid.Row="0"/>
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/Views/sections.xaml?section=43", UriKind.Relative));
}
Looks like the bug has to do with the navigation, not the UI element used to trigger the call.
Update 3: More weirdness. Still not able to reproduce the app while the debugger is attached. If I make the loading progress bar always collapsed, the bug disappears:
<ProgressBar x:Name="LoadingProgressBar"
IsIndeterminate="True"
Visibility="Collapsed"
Style="{StaticResource PerformanceProgressBar}"
VerticalAlignment="Top"/>
Alternatively, commenting out this line in code-behind makes the bug disappear:
LoadingProgressBar.Visibility = Visibility.Collapsed;
I really don't understand what's going on here. That line of code is not executed when the page is navigated from.
Here is the full XAML of the control that's getting messed up:
<ProgressBar x:Name="LoadingProgressBar"
IsIndeterminate="True"
Visibility="Collapsed"
Style="{StaticResource PerformanceProgressBar}"
VerticalAlignment="Top"/>
<TextBlock x:Name="DownloadFailed"
Visibility="Collapsed"
Style="{StaticResource disabledText}"
Margin="56,8,8,-8" >
FooBar.com could not be reached. Do you have a network connection?
</TextBlock>
<ListBox x:Name="sectionList" Margin="56,8,15,0" FontSize="64"
SelectionChanged="MenuList_SelectionChanged"
ItemsSource="{Binding SectionViewModels}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Add to favorites" Click="SectionContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</controls:PivotItem>
The problem lies in your usage of the Indeterminate ProgressBar. All its animations are done on the UI thread, and not the Compositor thread, as is the usual practise. Since you are already using the Windows Phone Toolkit, you can easily replace your ProgressBar with the PerformanceProgressBar offered by the toolkit. This should solve your problems.
Before I begin, let me say that I don't have a lot of experience with Windows Phone, so my answers are based on more generic WPF knowledge, so forgive me if I'm overlooking specifics of the platform, or am referencing features not available.
Some diagnostic questions (sorry this isn't a direct answer):
Firstly, it does seem like Navigate is calling a lot of layoutUpdates. I haven't yet seen what type of container is containing the pages you're updating, but it is worth asking, is that also being disrupted or only the menus?
Secondly, could you try specifying your itemPanel explicitly? You're expecting them to be virtualizingStackPanels, but you may find that some parent object in your visual hierarchy is creating a different inheritance scenario.
You have these in a grid, which is meant to size to its content, or take the default size (100x100 in normal WPF) or take sizing from its parent, which without knowing how you've specified the grid, or the grid's parent, it's difficult to know its behaviour. Furthermore, Grids automatically z-order their children according to the order in which they were added. Can you determine whether it is just the layout of the lisboxes that is being disturbed, or whether it is the entire grid? Or, is it larger than that?
If you attach to the layoutUpdated() event of the listboxes, grid, or grid's parent, you should be able to look at the stacktraces that lead you there - it sounds to me that you'll find that layoutUpdated() is firing more than you'd like it to. Further, you'll be able to output the heights and widths (ActualHeight etcetera of course) during those steps so that you can see when exactly those changes happen.
I hope that some of these diagnostic steps might help you reach an answer.

How do I handle click events on multiple similar buttons in WPF?

I'm a C# beginner and am trying to implement a numeric pad in WPF. It consists of 10 similar buttons:
<Button Content="0" Name="button0" Click="anyButtonClicked" />
<Button Content="1" Name="button1" Click="anyButtonClicked" />
<Button Content="2" Name="button2" Click="anyButtonClicked" />
...
<Button Content="9" Name="button9" Click="anyButtonClicked" />
What is the best practise to handle all those buttons? Do I build a function in the code behind for each one (Which would be mostly boring and repeating myself), or do I build one to handle any button clicked?
In the second case, how do I identify which button was clicked? What property of the sender object do I need to access?
If you want to use code behind then you can hook it up to a single event handler, you can then cast sender to a Button (or a FrameworkElement) and check its Name property.
Expanding on Goblin's answer below; if you want to stick with code behind and events you can define the event on a parent panel:
<StackPanel Button.Click="anyButtonClicked">
<Button Content="0" Name="button0"/>
<Button Content="1" Name="button1"/>
<Button Content="2" Name="button2"/>
...
<Button Content="9" Name="button9"/>
</StackPanel>
Then use e.OriginalSource, cast as a Button or FrameworElement, to retrieve the name.
private void anyButtonClicked(object sender, RoutedEventArgs e)
{
var source = e.OriginalSource as FrameworkElement;
if (source == null)
return;
MessageBox.Show(source.Name);
}
Alternatively you could take the MVVM approach, have a single command that all of your buttons are bound to, and pass a CommandParameter to differentiate them.
You handle the Button.Click event in the Parent control:
<StackPanel Button.Click="anyButtonClicked">
<Button Content="0" Name="button0"/>
<Button Content="1" Name="button1"/>
<Button Content="2" Name="button2"/>
...
<Button Content="9" Name="button9"/>
</StackPanel>
Then in your eventhandler - you can check e.OriginalSource for the button pressed.
EDIT: As for your question as to how to handle it - you could use the Content-property of the button pressed to figure out the key and then use that to perform your logic.
You really need to go by the Command approach because you may need it for key presses also.

Categories

Resources