Preserve grid focus state even when button left-clicked - wpf

I have a grid and a button. I want to be able to press down-arrow to select a row, then click on the button, then press down-arrow to select the next row, then click on the button.
However, when I click on the button, the grid keyboard focus is no longer on the row.
Here's an example. In this picture I have the third row selected. The behavior I want is: I mouse-click on Press Me, then press the keyboard down arrow once to select the fourth row. Instead, when I press down-arrow the top cell has a frame around it, and when I press it again the top row is selected.
How should I go about creating the behavior I need? I've tried editing the code behind to explicitly focus on the grid, but that didn't work.
Here is the complete code for the example.
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="DcCharacters">
<x:XData>
<Characters xmlns="">
<Character HeroName="Catwoman" Identity="Selina Kyle" />
<Character HeroName="Batman" Identity="Bruce Wayne" />
<Character HeroName="Starman" Identity="Jack Knight" />
<Character HeroName="BlueBeetle" Identity="Jaime Reyes" />
</Characters>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid
Grid.Row="0"
ItemsSource="{Binding Source={StaticResource DcCharacters},
XPath=//Characters/Character}"
AutoGenerateColumns="False"
IsReadOnly="True"
>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding XPath=#HeroName}" />
<DataGridTextColumn Binding="{Binding XPath=#Identity}" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="1">
<Button Margin="3,3,3,3" Width="Auto" Height="Auto" HorizontalAlignment="Left">_Press Me!</Button>
</StackPanel>
</Grid>
</Window>

I think you're looking for the Attached Property FocusManager.IsFocusScope.
By setting it to true on the StackPanel, the Button's (or other Controls) within it won't steal focus if they are clicked with the mouse. They will work like a Toolbar Button or Menu. You'll still be able to tab to them though.
Here is an example if you have three Buttons instead of one, Cut, Copy and Paste
<StackPanel Grid.Row="1"
FocusManager.IsFocusScope="True" >
<Button Width="Auto" Height="Auto" HorizontalAlignment="Left">Cut</Button>
<Button Width="Auto" Height="Auto" HorizontalAlignment="Left">Copy</Button>
<Button Width="Auto" Height="Auto" HorizontalAlignment="Left">Paste</Button>
</StackPanel>
Or in your case, just
<StackPanel Grid.Row="1"
FocusManager.IsFocusScope="True">
<Button Margin="3,3,3,3" Width="Auto" Height="Auto" HorizontalAlignment="Left">_Press Me!</Button>
</StackPanel>

Just Focusable = "False" for your Button. It works for me.
<Button Margin="3,3,3,3" Width="Auto" Height="Auto" HorizontalAlignment="Left" Focusable="False">_Press Me!</Button>

Kind of a Hack but it works
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (myDataGrid.SelectedItem != null && e.Key == Key.Down)
{
DataGridRow dgrow = (DataGridRow)myDataGrid.ItemContainerGenerator.ContainerFromItem(myDataGrid.SelectedItem);
dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}

Related

Capturing name of StackPanel from Drop event

Within WPF I have the following XAML code:
<Page x:Class="com.MyCo.MyProj.Pages.Configuration.ManageLinkage.MasterLinkage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:com.MyCo.MyProj.Pages.Configuration.ManageLinkage"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="MasterLinkage">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TabControl TabStripPlacement="Top" Background="Transparent">
<TabItem Header="Import">
<ListBox Margin="0,5,0,0" Name="lbxImportItems" HorizontalAlignment="Left" VerticalAlignment="Top" Width="110" Background="Transparent"
PreviewMouseLeftButtonDown="lbxImportItems_PreviewMouseLeftButtonDown" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Name="DBImport">
<Image Source="/Images/DBImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="Database" Foreground="AntiqueWhite"/>
</StackPanel>
<StackPanel Orientation="Vertical" Name="CSVImport">
<Image Source="/Images/CSVImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="CSV Import" Foreground="AntiqueWhite"/>
</StackPanel>
</ListBox>
</TabItem>
</TabControl>
<Canvas x:Name="cnvsLinkScreen" AllowDrop="True" Grid.Column="1" Background="Transparent" Drop="cnvsLinkScreen_Drop" DragOver="cnvsLinkScreen_DragOver" ></Canvas>
</Grid>
The code for capturing the event is here:
private void cnvsLinkScreen_Drop(object sender, DragEventArgs e)
{
Canvas parent = (Canvas)sender;
object data = e.Data.GetData(typeof(string));
StackPanel objIn = (StackPanel)e.Data;
...
}
The drag and drop work great, the event method created the image in the canvas. However, I want to capture the Name="" from the StackPanels which are dropped.
I found the Name buried super deep in the "DragEventArgs e" object. I was think that there should be a way to cast the object (or the object within that object) as a StackPanel to easily work with it. The above code does not convert the StackPanel object( it's not at the root or the child object; I tried both) so it exceptions on "StackPanel objIn = (StackPanel)e.data;"
How do I either translate the incoming object to a StackPanel or how do I access the Name attribute from the Stackpanel?
I got it. I was close with the translation. To translate / typecast the object to what you are working with I needed to use the following line:
StackPanel objIn = (StackPanel)(e.Data.GetData(typeof(StackPanel)));
Which is slightly different than above.

WPF : ListBox.ItemTemplate doesn't react when click on text or image wpf xaml

I'm new to Xaml and WPF . I'm using the following code to extract botton title and image from RSS feeds .
The problem that the botton react only when user click on the border ... it dosen't react when user click on text or image .
<ListBox.ItemTemplate >
<DataTemplate >
<Button Background="{Binding C:AccentColors}" Width="400" Height="100" HorizontalAlignment="Left" >
<Grid Width="400" Height="100" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding XPath=enclosure/#url}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock TextWrapping="Wrap" Text="{Binding XPath=title}" FontWeight="Bold" Grid.Column="2"/>
</Grid>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You have to add the Click event in your button.
<Button Background="{Binding C:AccentColors}" Width="400" Height="100" HorizontalAlignment="Left" Click="MethodNameHere".....
and also you have to create and implement the method in the Window C# file
The common mistake is that somebody placed TextBlock on top of button without realizig it, because TextBlock has transparent Background.
Visually, the Text of TextBlock may be outside Button, but since TextBlock alignment is set to strech by default, it fills entire area.
hard to say if this is your case...
<Grid>
<Button Content="I'm not working" Margin="0,100,0,0" />
<TextBlock Text="I'm in top left corner" />
<Grid />
if you set TextBlock.Background to Red, you entire grid would be red.
The button is taking the clicks. You need a handler, either a command binding or if you're not using MVVM - a code behind method:
<Button Click="ButtonBase_OnClick"
Handler will look like this:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
}

scrollviewer the property 'content' is set more than once

Here my code:
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer
Grid.RowSpan="2" Style="{StaticResource HorizontalScrollViewerStyle}"
HorizontalScrollBarVisibility="Visible" >
<!-- The elements you want to be horizontally scrollable goes here -->
<!-- Horizontal scrolling grid used in most view states -->
<GridView
Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Padding="100,136,116,46"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"/>
<Button Hello /> <!-- From here come the Error -->
</ScrollViewer>
</Grid>
When I add any thing after my GridView, it give me that error (the property content is set more than once).
The answer is right there in the error message. A ScrollViewer can only have one child (you have two, a GridView and a Button). If you want to add multiple things, you will have to wrap them in a panel that allows multiple children (e.g. Grid).

Image selection canvas

Acutally i'm doing a WPF application where user can select Image from a panel. There are two Button inside the panel (right arrow and left arrow). I'm not sure what is the name of the panel but i attach an image in my question. So, i want to ask how do i create the panel? Using a canvas and put the image inside the canvas? hm... and the buttons, I totally don't have any ideal how to do it.
p/s: i'm wpf newbie
Image:
Here is one way to do it, using the standard ListBox control:
First, we need a pair of Previous/Next buttons and a ListBox. We make the ListBox lay out its items horizontally by changing its ItemsPanel:
<DockPanel Width="200" >
<Button x:Name="_prev" Content="<<" />
<Button x:Name="_next" Content=">>" DockPanel.Dock="Right" />
<ListBox x:Name="_myList" Loaded="OnMyListLoaded" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<TextBlock Text="Image1 " />
<TextBlock Text="Image2 " />
<TextBlock Text="Image3 " />
<TextBlock Text="Image4 " />
<TextBlock Text="Image5 " />
<TextBlock Text="Image6 " />
<TextBlock Text="Image7 " />
</ListBox>
</DockPanel>
In the ListBox' Loaded event, we use VisualTreeHelper to search through its Template and find its built-in ScrollViewer. Once we find it, we hook it up to the two buttons we created:
private void OnMyListLoaded(object sender, RoutedEventArgs e)
{
var scroller = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(_myList, 0), 0) as ScrollViewer;
if (scroller != null)
{
_prev.Command = ScrollBar.LineLeftCommand;
_prev.CommandTarget = scroller;
_next.Command = ScrollBar.LineRightCommand;
_next.CommandTarget = scroller;
scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
}
}
This might help you:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Name="LeftB" Grid.Column="0" />
<Button Name="RightB" Grid.Column="2" />
<StackPanel Orientation="Horizontal" Name="Images" Grid.Column="1" />
</Grid>
The images can be the Children of the StackPanel.
Of course you should set the size of the the images as a function of their number and StackPanel's Width. The best way might be to handle Width change event of the StackPanel.

Silverlight/WP7: .NET calls the wrong event

I'm developping a Windows Phone 7 app with the Pivot template. This app has a menu toolbar (at the bottom of course).
When I click on the search button (MenusButtonsApplication_Click event to go to the "One" tab), a search page with a ListBox containing some elements is displayed. When I click on one of these elements (ListBoxFoobarSelectionChanged event), I get redirected to another page to show me some details. I use this method.
To go back to the search page again, I must click twice on the search button (MenusButtonsApplication_Click event). Which is not normal.
After a debug, I found that the first click is related to the ListBoxFoobarSelectionChanged while calling the MenusButtonsApplication_Click.
This is the XAML code:
<phone:PhoneApplicationPage
x:Class="Test.Soft.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Phone.Controls.Samples;assembly=Phone.Controls.Samples"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480">
<!--Ressources application-->
<phone:PhoneApplicationPage.FontFamily>
<StaticResource ResourceKey="PhoneFontFamilyNormal"/>
</phone:PhoneApplicationPage.FontFamily>
<phone:PhoneApplicationPage.FontSize>
<StaticResource ResourceKey="PhoneFontSizeNormal"/>
</phone:PhoneApplicationPage.FontSize>
<phone:PhoneApplicationPage.Foreground>
<StaticResource ResourceKey="PhoneForegroundBrush"/>
</phone:PhoneApplicationPage.Foreground>
<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:PivotControl x:Name="pvPrincipal" SelectedIndex="0" TitleTemplate="{StaticResource titleTemplate}" HorizontalAlignment="Left" Width="480" FontSize="22" DefaultItemWidth="480" FontStretch="Normal" Height="697" VerticalAlignment="Top" Margin="0,27,0,0" HorizontalContentAlignment="Left" IsEnabled="True" Visibility="Visible">
<!-- Recherche de comptes -->
<controls:PivotItem Name="pivotOne" Header="One" Loaded="PivotOne_Loaded">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="192*" />
<RowDefinition Height="423*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<ListBox Name="lbSearch" Width="462" Height="377" HorizontalAlignment="Left" BorderThickness="1"
VerticalAlignment="Bottom" SelectionChanged="ListBoxCompteSelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="25,0,0,10" Text="{Binding FoobarCode}" Width="80" HorizontalAlignment="Right" />
<TextBlock Text="- " />
<TextBlock Text="{Binding FoobarDescription}" Padding="5,3,5,5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</controls:PivotItem>
<!-- Details -->
<controls:PivotItem x:Name="pivotDetails" Header="Details" >
<Grid Name="grDetail" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="398*" />
<RowDefinition Height="167*" />
</Grid.RowDefinitions>
<ListBox>
<StackPanel Margin="5,0,12,20" Grid.ColumnSpan="2">
<TextBlock FontSize="26" FontWeight="Bold"
FontStyle="Normal" Foreground="White" HorizontalAlignment="Left">Détail d'un compte</TextBlock>
</StackPanel>
<StackPanel Grid.Row="1" >
<TextBlock Text="{Binding Path= FoobarCode}" Name="tbCode" HorizontalAlignment="Left" FontWeight="Bold"/>
<TextBlock Text="{Binding Path= Description}" Name="tbDescription" FontWeight="Bold" HorizontalAlignment="Left" />
</StackPanel>
<StackPanel Grid.Row="2" >
</StackPanel>
</ListBox>
</Grid>
</controls:PivotItem>
</Grid>
<!-- ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar >
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton x:Name="btnToPivotOne" IconUri="/Icons/appbar.plan.rest.png" Text="One" Click="MenusButtonsApplication_Click"></shell:ApplicationBarIconButton>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem x:Name="menuToPivotOne" Text="To pivot One" Click="MenusButtonsApplication_Click"></shell:ApplicationBarMenuItem>
</shell:ApplicationBarMenuItem>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
This interface has:
A pivot (pcPrincipal) containing two tabs (One and Details)
An application bottom bar
For the two buggy functions, here they are:
private void ListBoxFoobarSelectionChanged(object sender, SelectionChangedEventArgs e)
{
grDetail.DataContext = lbSearch.SelectedItem;
lblDescriptionType.Text = mainVM.RetourneDescriptionTypeEncours();
pvPrincipalSelectedItem = pivotDetail;
}
private void MenusButtonsApplication_Click(object sender, EventArgs e)
{
if (sender is ApplicationBarIconButton)
{
switch ((sender as ApplicationBarIconButton).Text)
{
case "One":
// Affichage pivot plan
pvAccueil.Dispatcher.BeginInvoke(() =>
{
pvPrincipal.SelectedItem = pivotOne;
});
break;
default:
break;
}
}
else
{
if (sender is ApplicationBarMenuItem)
{
switch ((sender as ApplicationBarMenuItem).Text)
{
case "To pivot One":
pvAccueil.Dispatcher.BeginInvoke(() =>
{
pvPrincipal.SelectedItem = pivotOne;
});
break;
default:
break;
}
}
}
}
Is it a .NET bug? How can I resolve it? May be should I fire it manually?
Thank you.
It sounds like the button you are using to navigtate back is in the listbox so the first time you "click" it you are actually just selecting the item in the list and the second "click" actually triggers the navigation.
If this is the case, the solution is to move the "Back/search" button outside of hte listbox.
Alternatively, have the navigation take place in the handler for the SelectionChanged event.
Edit.
What is Phone.Controls.Samples.PivotControl?
And why are you using this rather than the standard one?

Resources