Silverlight/WP7: .NET calls the wrong event - silverlight

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?

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.

TextBlock1 is not declared. It may be inaccessible due to its protection level

As silly as this sounds I'm a little stumped at this one. Here's my XAML in a Win Phone 8 App:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Page" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
I've searched around but I don't know why I can't write code against the TextBlock1 control in code behind. When I type TextBlock1.Text= .... I get the error TextBlock1 is not declared. It may be inaccessible due to its protection level. But I can't see how it is private?
All I'm trying to do is add a textblock, assign some content to it, and then that selected value is passed across another page to perform relevant action.
In addition as soon as I remove it outside of the PhoneListSelector I can access it.
TextBlock1 is defined inside an ItemTemplate, anything defined a Template cannot be access directly as it will be created on runtime by the control.
You probably need to do binding on the TextBlock if you want to manipulate anything that the LongListSelector's DataContext has.
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" Text="{Binding Content"} HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
MainLongListSelector.DataContext = new List<TestViewModel>();
public class TestViewModel : INotifyPropertyChanged
{
//Assuming you've implemented the interface
private string _content;
public string Content { get { return _content; } { set { _content = value; NotifyOfPropertyChanged("Content"); } }
}
From here, you can try to access the selected value content and pass that to the next page.
var selectedItem = MainLongListSelector.SelectedItem as TestViewModel;
GoToNextPage(selectedItem.Content);
I strongly suggest to read MVVM design pattern and everything should be easy for you to implement, always remember UI is not DATA it's responsibility is only to show something that is passed through the ViewModel.

ListBox with "load more" option

I would like to know how to construct the ListBox in WP7 that only load 20 items at a single time, and have a footer that show "load more" if there is any.
When user press the "load more", it will load another 20 in the list without loading the previous loaded data?
I am using LINQ at the behind source.
my code for XMAL as follow:
<Grid>
<ListBox name="newsIndexListBoxEN">
<ListBoxItem>
<DataTemplate>
<StackPanel Width="410" Orientation="Horizontal" VerticalAlignment="Top" Margin="0,5,0,5">
<StackPanel Background="DarkBlue" Margin="10,0,0,0" Height="100" Width="100" VerticalAlignment="Top">
<TextBlock Name="columnsTypeTB" Text="{Binding pType}" Margin="0,0,0,0" Foreground="White" FontSize="23" HorizontalAlignment="Center" />
<Image Width="100" Height="100" VerticalAlignment="Top" HorizontalAlignment="Center" Source="Background.png" />
</StackPanel>
<StackPanel Width="300" Height="100" Margin="0,0,0,0">
<Path Margin="0,0,0,0" Data="M39,8 L389,8" Fill="DarkBlue" Height="1" Stretch="Fill" Stroke="DarkBlue" UseLayoutRounding="False" Width="400"/>
<TextBlock Margin="8,0,0,0" Text="{Binding pTitle}" Tag="{Binding pID}" Style="{StaticResource PhoneTextNormalStyle}" TextWrapping="Wrap" Width="292" Height="66" />
<TextBlock Margin="8,5,0,0" Text="{Binding pDate}" Tag="{Binding pID}" MouseEnter="NewsViewContent_mouseEnter" Style="{StaticResource PhoneTextSmallStyle}" VerticalAlignment="Bottom" TextWrapping="Wrap" Width="292" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBoxItem>
</ListBox>
</Grid>
C# Code as follow:
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fs = storage.OpenFile(fileName, FileMode.Open))
{
XDocument menuIndex = XDocument.Load(fs);
var menuIndexList = from query in menuIndex.Descendants("news")
orderby (int)query.Element("newsID") descending
select new mkmenu
{
pID = query.Element("newsID").Value,
pTitle = query.Element("newsTitle").Value,
pDate = query.Element("newspDate").Value,
pType = newsType
};
newsIndexListBoxEN = menuIndexList.Count();
}
}
any ideas? sample code?
You can edit your listbox template to show a "Load more" button at the end of the list. In Blend, right click on your listbox, choose Edit Template, Edit a copy. By default, your listbox has a template like this:
ScrollViewer
ItemPresenter
Wrap your ItemPresenter into a StackPanel, then add a button at the end:
ScrollViewer
StackPanel
ItemPresenter
Button
That button will always be displayed at the end of the listbox. Handle the Clicked event of that button to add items to your ObservableCollection.
You can bind your listbox to ObservableCollection and add first 20 items on your page(app) load. Than after pressing "load more" get next 20 items and add to collection. Items will automatically be added to listbox.

Preserve grid focus state even when button left-clicked

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));
}
}

WPF numeric up down custom control

I've been needing to use a numeric up-down control for my WPF app. I read a similar question posted here and tried using the one available here > http://bot.codeplex.com/.
I added the references and referenced it in my XAML window
xmlns:lib="clr-namespace:PixelLab.Wpf;assembly=PixelLab.Wpf"
and did this.
<lib:NumericUpDown Name="year"></lib:NumericUpDown>
and keep getting the error: 'nud' is an undeclared namepsace.
I'm very new to WPF so any help would be appreciated.
The Extented WPF Toolkit has one: NumericUpDown
Just combine a TextBox with a veritical fixed-height ScrollBar like this:
<Grid Height="80">
<TextBox x:Name="part_TextBox" Text="{Binding Value,ElementName=part_Scrollbar,StringFormat={}{0:F6},Mode=TwoWay}" MaxLength="11" VerticalAlignment="Center" VerticalContentAlignment="Center" FontSize="24" Height="40"/>
<ScrollBar x:Name="part_Scrollbar" Orientation="Vertical" Minimum="0" Maximum="100" BorderBrush="{x:Null}" SmallChange="0.1" Height="32" Margin="8 4" VerticalAlignment="Stretch" Grid.Column="1" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Right">
<ScrollBar.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="180"/>
<TranslateTransform/>
</TransformGroup>
</ScrollBar.RenderTransform>
</ScrollBar>
</Grid>
Bindings for Maximum & Minimum & SmallChange/Increment are directly avaliable.
Vanilla XAML (no additions or packages) implementation:
<Window x:Class="Spinner.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Spinner"
mc:Ignorable="d"
ResizeMode="CanMinimize" SizeToContent="WidthAndHeight" Title="Scroll Spinner">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- The button exists just to have something other than the spinner be the object of focus. -->
<Button Content="Reset" TabIndex="0"/>
<!-- The spinner is just a scroll bar overlaying a text box (same tab indices). -->
<!-- Only the scroll bar is named (in order to get its value); the text box just relfects the scroll bar's value. -->
<TextBox GotFocus="TextBox_GotFocus" Grid.Row="1" Height="{Binding ElementName=SpinnerScr, Path=ActualHeight}" HorizontalAlignment="Stretch" IsReadOnly="True" TabIndex="1" Text="{Binding ElementName=SpinnerScr, Path=Value, StringFormat={}{0:####0}}" TextAlignment="Center"/>
<ScrollBar x:Name="SpinnerScr" Background="Transparent" Focusable="True" Grid.Row="1" Height="20" LostFocus="SpinnerScr_LostFocus" Margin="0,3" Maximum="999" Orientation="Horizontal" SmallChange="1" TabIndex="1" Visibility="Hidden"/>
<x:Code>
<![CDATA[
void SpinnerScr_LostFocus(object sender, RoutedEventArgs e) {
SpinnerScr.Visibility = Visibility.Hidden;
}
void TextBox_GotFocus(object sender, RoutedEventArgs e) {
SpinnerScr.Visibility = Visibility.Visible;
SpinnerScr.Focus();
}
]]>
</x:Code>
</Grid>
</Window>
using System.Windows;
namespace Spinner {
public partial class MainWindow : System.Windows.Window {
public MainWindow() {
InitializeComponent();
}
}
}
When the scroll bar (or text box) has focus, the scroll elements are visible. On loss of focus, only the text box is visible. Only the scroll bar may be accessed in any code-behind.

Resources