Listbox SelectionChanged firing when setting to -1 within function, wp7 - windows-phone-7

I am running into a very odd problem in c# and I just wanted to know what is causing this. I have my theories, but not entirely sure and just want to see if it can be reproduced.
Standard Pivot Page in wp7 silverlight 4.
<Pivot>
<PivotItem>
<Listbox Width="400" Height="500" x:Name="box" SelectionChanged="myhandle">
<ListBoxItem x:Name="item1">
<TextBlock Height="40" Width="200" Text="hi everyone!"/>
</ListBoxItem>
<ListBoxItem x:Name="item2">
<TextBlock Height="40" Width="200" Text="No Wai"/>
</ListBoxItem>
<ListBoxItem x:Name="item3">
<TextBlock Height="40" Width="200" Text="Ya Rly!"/>
</ListBoxItem>
</Listbox>
</PivotItem>
</Pivot>
In my C#, I have the following:
private void myhandle(object sender, SelectionChangedEventArgs args)
{
var selection ="";
selection = (sender as Listbox).SelectedIndex.ToString();
box.SelectedIndex = -1;
}
Here is the problem: Whenever I click on one of the three listboxitems, the myhandle code makes selection equal to the proper SelectedIndex, but then it hits the box.SelectedIndex =-1; line and then refires the myhandle function. In doing so, selection is now -1.
I have no idea why it is going back up the stack. This shouldn't be a recursive function.
My goal is to select the item, but then have the SelectedIndex back to -1 so that the person is able to select the item once again if need be, instead of changing to another item and back.
Sure there is an easy fix of throwing a switch function and checking to see if it's -1 already, but that doesn't solve the problem of the recursion.
Thanks for the time.

Everytime the selection is changed, the SelectionChanged event will fire. This includes clearing the selection, which includes setting SelectedIndex = -1 and even if you are already in a SelectionChanged handler.
You can do something like this:
private bool inMyHandle = false;
private void myhandle(object sender, SelectionChangedEventArgs args)
{
if (!this.isMyHandle) {
this.isMyHandle = true;
try {
var selection ="";
selection = (sender as Listbox).SelectedIndex.ToString();
box.SelectedIndex = -1;
}
finally {
this.isMyHandle = false;
}
}
}

The standard MS samples already have this in a standard Listbox selected item event.
Simply use the following in the event handler code:
private void ListBox_SelectionChanged(object sender,System.Windows.Controls.SelectionChangedEventArgs e)
{
// If selected index is -1 (no selection) do nothing
if (ListBox.SelectedIndex == -1)
return;
//Do Something
// Reset selected index to -1 (no selection)
ListBox.SelectedIndex = -1;
}
No need to have any boolean handlers, the function just nothing if "-1" is the current index.
All this is to compensate for the way that the standard listbox opperates.
If you use MVVM and bind to the "Selecteditem" / "SelectedIndex" properties you have to keep the same thing in mind.

You can check args.AddedItems.Count too:
private void myhandle(object sender, SelectionChangedEventArgs args)
{
if (args.AddedItems.Count > 0)
{
....
box.SelectedIndex = -1;
}
}

Related

Size of the Custom popup window using Interaction request

I used a custom confirmation popup window, this the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Content}" TextWrapping="Wrap" Width="150"/>
<StackPanel Orientation="Horizontal" Margin="6" Grid.Row="1">
<Button x:Name="YesBtn" Width="100" Content="OK" Click="OnOk_Click"/>
<Button x:Name="NoBtn" Width="100" Content="No" Click="OnNo_Click"/>
</StackPanel>
</Grid>
and this is the code behide:
public partial class CustomConfirmation : IInteractionRequestAware
{
public CustomConfirmation()
{
InitializeComponent();
}
public IConfirmation Confirmation
{
get { return this.DataContext as IConfirmation; }
set { this.DataContext = value; }
}
public string Title { get; set; }
public bool Confirmed { get; set; }
public INotification Notification { get; set; }
public Action FinishInteraction { get; set; }
private void OnOk_Click(object sender, RoutedEventArgs e)
{
if (FinishInteraction != null)
{
Confirmation.Confirmed= true;
FinishInteraction();
}
}
private void OnNo_Click(object sender, RoutedEventArgs e)
{
if (FinishInteraction != null)
{
Confirmation.Confirmed = false;
FinishInteraction();
}
}
}
In view model class i have :
two commands(DispalyLongTextCommand and DispalyShortTextCommand): one
to display long message and the other to display a short message
and i have InteractionRequest ConfirmationRequest
object initialized in ctor to raise intercations.
if I display the long message first my custom window resize its content to the hole message, it is OK!
but if a want to display the short message, my window keeps the previous size!
note : even i set the window SizeToContent style to WidthAndHeight but it not working.
<ei:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=TwoWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<local:CustomConfirmation/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</ei:Interaction.Triggers>
can you guide me,
thanks in advance
SOLUTION:
I fixed the problem by adding this code in the code behind of the custom popup window, :
public CustomConfirmationView()
{
InitializeComponent();
Loaded += CustomPopupView_Loaded;
}
private void CustomPopupView_Loaded(object sender, RoutedEventArgs e)
{
var parentWindow = this.Parent as Window;
if (parentWindow != null)
{
parentWindow.Measure(parentWindow.DesiredSize);
}
}
The WindowContent property is reused each time you show a new popup. So, what happens is that when you first show a popup, the CustomPopupView is visualized and the height is set based on the current content. Now, when you close the popup, and change the content to a larger message and then show it again, the CustomPopupView.Height has already been set by the previous action and isn't updated in time for the new Window to get the correct height. So you must now resize the Window to match the new size of the CustomPopupView height. So just add a little code to handle this in your code-behind like this:
public CustomPopupView()
{
InitializeComponent();
Loaded += CustomPopupView_Loaded;
}
private void CustomPopupView_Loaded(object sender, RoutedEventArgs e)
{
var parentWindow = this.Parent as Window;
if (parentWindow != null)
parentWindow.MinHeight = _txt.ActualHeight + 75;
}
Note: '_txt' is the name of the TextBlock with the Content binding.
I think this has to do with the default confirmation window that ships with Prism. The MinWidth and MinHeight are set in the XAML to 300 and 150, respectively. So, the window width/weight will never get any smaller no matter what the window content is. Overriding the window style will not be enough to do what you need.
You could download the Prism code and remove that limitation if you are comfortable enough with that. The source path to the file you would want to start with is below.
\Source\Wpf\Prism.Wpf\Interactivity\DefaultPopupWindows\DefaultConfirmationWindow.xaml
Either that, or ask the Prism team to see if they can make this more flexible, which is probably a better suggestion. You can post this as an issue on their GitHub page. https://github.com/PrismLibrary/Prism/issues

how to use listpicker selectionChanged event in wp7

Am new to Wp7, developing the app with ListPicker and used SelectionChanged event to get selected data form listPicker but am having the problem with SelectionChanged event when am using this and getting NullReferenceException but when i used same code in button_Click that works perfectly and am getting the selected text
my c# code is :
private void listPicker1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListPickerItem lpi = (ListPickerItem)listPicker1.SelectedItem;//this code is working in click event
MessageBox.Show(lpi.Content.ToString());
}
my Xaml code is
<toolkit:ListPicker x:Name="listPicker1" Grid.Row="0" ExpansionMode="ExpansionAllowed" SelectionChanged="listPickerCountryLogin_SelectionChanged" HorizontalAlignment="Left" Margin="14,43,0,0" VerticalAlignment="Top" Width="436" FullModeHeader="Select Country" Background="White" BorderBrush="White">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Country}" Width="250" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Country}" Width="300" Margin="0,0,0,20" FontSize="44"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
but i want to get the text form SelectionChanged event?
how to achive this :)
thanks in advance
am also ran into same problem am also getting NullReferenceException
Try this works fine for me
1) If you are using static ListPickerItems means without DataBinding use this
private void listPicker1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListPickerItem lpi = (sender as ListPicker).SelectedItem as ListPickerItem;
MessageBox.Show("selected item is : " + lpi.Content);
}
2) Try this if You are using DataBinding to dispaly the listPickerItems
private void listPicker1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Countries item = (sender as ListPicker).SelectedItem as Countries;
MessageBox.Show("Selected Item is : " + item.Country);
}
here am assuming that you prepared a class Countries with country property for taht you need to typeCast to selected item to Countries class then only you get the result
I had this same problem when using SelectionChanged event in my listpicker, here is what I had:
// My listpicker is LpBluetoothPaired
private void LpBluetoothPaired_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
rcvData.Text = LpBluetoothPaired.SelectedItem.ToString();
}
But when opening the app it had an exception, so I fixed it:
private void LpBluetoothPaired_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (LpBluetoothPaired.SelectedItem != null) {
rcvData.Text = LpBluetoothPaired.SelectedItem.ToString();
}
}
it looks like the event is called when the application is opening but at that time there is still no selectedItem, so in order to avoid the exception and only fulfill the rcvData textBox I check if it isn''t null
Sometimes SelectionChanged event fires, when setting the ItemsSource from code behind. So, in such case the selectedItem may be null.
Hence, add this line in your SelectionChanged code and try.
private void listPicker1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(listPicker1.SelectedIndex == -1) //otherwise (listPicker1.SelectedItem == null) also works
return;
ListPickerItem lpi = (ListPickerItem)listPicker1.SelectedItem;//this code is working in click event
MessageBox.Show(lpi.Content.ToString());
}
If still problem persists, place a break point in the SelectionChanged handler, and observe the values
When data is loading to the listpicker it fires selectionchanged event.So for this put your code in Try-catch and For selectionchanged write this :
private void listPicker1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
Countries SelectedCountries =e.AddedItems[0] as Countries;
String SelectedCountry = SelectedCountries.Country;
}
catch
{
}
}

i want to pass list as parameters in page navigation in windows7 using silverlight and access them in target page

I'm new to Windows Phone 7 development.
In my First Application I want to Create two listboxes with checkboxes inside them in different pages and to fill the first listbox with some Data. When I select some records in first listbox, that particular records have to be added in another listbox. I done listbox selection and also pass the selecteditem via parameter to another page, when I tried to assign that list to listbox.ItemSource it throws ArgumentNullException.
Please help me to solve this problem.
Thanks.
Firstly, as you are pointing out, ArgumentNullException is being thrown. So basically when you are doing the assignment to ItemsSource you are providing a null. In other terms, the object you think you have received from the object page is null.
Secondly, it is hard to help you debug the problem if you are not providing any source code. But, generally speaking are you able to test with dummy data and from code-behind (i.e. not from the XAML) when you are in the 2nd page that the listbox there gets populated? So, for the time being ignore the passing of data. Just ensure that when the 2nd page is loaded, the listbox is populated with dummy data that you have created in the code-behind of that page. If that works then your problem is simpler.
Hopefully, this helps.
//-- Adding sample Code -- //
Firstpage.xaml
<ListBox x:Name="FirstListBox" Grid.Row="1" ItemsSource="{Binding}" SelectionChanged="HandleSelection">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="32" Margin="12"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Firstpage.xaml.cs
FirstListBox.DataContext = new String[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
...
private void HandleSelection(object sender, SelectionChangedEventArgs e)
{
String selection = (String)FirstListBox.SelectedItem;
NavigationService.Navigate(new Uri("/Secondpage.xaml?id=" + selection, UriKind.Relative));
}
Secondpage.xaml
<ListBox x:Name="OtherListBox" Grid.Row="1" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="32" Margin="12"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Secondpage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
String id = null;
NavigationContext.QueryString.TryGetValue("id", out id);
if (id != null)
{
List<String> dummyData = new List<string>();
for (int i = 0; i < 12; i++)
{
dummyData.Add(id + " - " + i);
}
OtherListBox.DataContext = dummyData;
}
}
So, as you can read from the i have a dummy list in Firstpage and when i click on any of the items it takes me to another page where there is another list which have been populated with data generated from my initial selection. As you see i am passing the selection as a querystring parameter just like i explained in my comments of this answer.
Hopefully this solves your problem and i look forward to you accepting this solution as an answer to your problem.
And in The Second Page...
<`private IList iList1;
//In The onNavigatedTo Event assign the stored list to the variable//
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
var i= PhoneApplicationService.Current.State["yourparam"];
iList1 = (IList) i ;//convert object to list//
lstpro.ItemsSource = iList1;
}'
public partial class MainPage : PhoneApplicationPage
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
//adding data to listbox on page load event//
for (int i = 0; i < 20; i++)
{
id = i + 1;
name = "productname" + i;
quantity = i + 2 / 2;
productlist.Add(new ProductList(id, name, quantity));
}
lstpro.ItemsSource = productlist;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
//using PhoneApplicationService.Current.State to store the list//
PhoneApplicationService.Current.State["yourparam"] = lstpro.SelectedItems;
NavigationService.Navigate(new Uri("/res.xaml", UriKind.Relative))
}
}

stop the stackpanel items inside scroll viewer to be at the display left side when scroll

I have added 10 images in a stackpanel horizontally which is inside a scrollviewer. When user swipe the page ,the scrollviewer stops at certain position, if the scroll stops in the middle of 2 images like the first image shown below i want to set the image with number 3 to be automatically scroll and fit with the left side of the screen like in the second image
for (int i = 0; i <= 9; i++)
{
Uri uri = new Uri("http://d1mu9ule1cy7bp.cloudfront.net//catalogues/47/pages/p_" + i + "/thump.jpg");
ImageSource img1 = new BitmapImage(uri);
Image rect = new Image { RenderTransform = new TranslateTransform() };
rect.Source = img1;
stack.Children.Add(rect);
}
XAML:
<Grid x:Name="LayoutRoot" Width="480" Background="Transparent" Margin="0,-33,0,0" Height="800">
<ScrollViewer HorizontalContentAlignment="Left" HorizontalAlignment="Left" Name="scroll" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<StackPanel Name="stack" Width="Auto" Orientation="Horizontal" HorizontalAlignment="Left" >
</StackPanel>
</ScrollViewer>
</Grid>
The first thing you need to do is detect which item is overlapping the side of the screen. To do this, iterate over each item within the StackPanel and determine their location relative to some other element that has a fixed location on screen.
To do this, I use the following extension method:
/// <summary>
/// Gets the relative position of the given UIElement to this.
/// </summary>
public static Point GetRelativePosition(this UIElement element, UIElement other)
{
return element.TransformToVisual(other)
.Transform(new Point(0, 0));
}
i.e. for each item call the following;
Point position = stackPanelItem.GetRelativePosition(someFixedElement);
Using the location of each item, you should be able to work out which one overlaps the screen.
You then need to calculate by how much you need to scroll in order to ensure that your item is fully visible, then use ScrollViewer.ScrollToVerticalOffset to scroll to that location.
This probably isn't the nicest solution and I am sure there is a better way to achieve this but you could use the following :-
XAML :-
<ListBox x:Name="MyListBox"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
C# :-
DispatcherTimer myTimer = new DispatcherTimer();
// Constructor
public MainPage()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
MyListBox.Items.Add(new Button()
{
Content = i.ToString(),
Width = 200,
Height = 100,
});
MyListBox.MouseMove += new MouseEventHandler(MyListBox_MouseMove);
}
myTimer.Interval = TimeSpan.FromSeconds(1);
myTimer.Tick += new EventHandler(myTimer_Tick);
}
private void myTimer_Tick(object sender, EventArgs e)
{
myTimer.Stop();
SnapFirstItem();
}
private void MyListBox_MouseMove(object sender, MouseEventArgs e)
{
myTimer.Stop();
myTimer.Start();
}
private void SnapFirstItem()
{
foreach (Button currentButton in MyListBox.Items)
{
bool visible = MyListBox.TestVisibility(currentButton, System.Windows.Controls.Orientation.Horizontal, true);
if (visible)
{
MyListBox.ScrollIntoView(currentButton);
break;
}
}
}
The TestVisibility extension method is from the following :-
http://blogs.msdn.com/b/ptorr/archive/2010/10/12/procrastination-ftw-lazylistbox-should-improve-your-scrolling-performance-and-responsiveness.aspx

How to Find the object of The ListItem in ListBox in Windows Phone7

I have a ListBox, here i am binding IList Like (Cities).
I want an event like OnItemDataBound in .NET in Windows Phone7.
Like when every city was binded (if 10 cities are this event will fire 10 times) this event will fire so that i have to do some more calculations on this event. In this event i have to find the object binded to the ListItem so that i can make some calculations. Is there any event in WP7 similar OnItemDataBound in .NET.
<ListBox Loaded="lstFlights_Loaded" Height="635" x:Name="lstFlights"
ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="10" Margin="10">
<Border.Background>
<ImageBrush ImageSource="../Images/rowbg.png"></ImageBrush>
</Border.Background>
//Some data here
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i am binding the data like below:
lstFlights.ItemsSource = objCities;
on Binding every city i want an event to populate some list items(like: i want to change the textblock text,etc) according to the City that binded to ListItem. To do this I need an event like OnItemDataBound in WP7. And I have list picker like below:
On SelectionChanged event also i want to change the list items.
And one more thing IList(objCities) is coming from the Service so I can't change that Object
so if i want to change any TextBlock in List Box i have do FindName and i have to assign calculated value for every city binded.
There is an CollectionChanged event in ObservableCollection collection for new elements added/removed.
ListBox provides some methods for accessing ListBoxItem. You can see them in list.ItemContainerGenerator class.
You can use it together to achieve needed results.
There is short example that shows how to make a red foreground to newly added items in some conditions (even or not):
ObservableCollection<string> items = new ObservableCollection<string>();
public MainPage()
{
InitializeComponent();
items.CollectionChanged += (s, e) =>
{
Dispatcher.BeginInvoke(() =>
{
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
ListBoxItem listitem = (ListBoxItem)list.ItemContainerGenerator.ContainerFromItem(item);
if (DateTime.Parse(listitem.Content.ToString()).Second % 2 == 0)
{
listitem.Foreground = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
}
}
}
});
};
list.ItemsSource = items;
}
private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
items.Add(DateTime.Now.ToLongTimeString());
}
private void RemoveButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
Random rnd = new Random();
if (items.Count > 0)
{
int index = rnd.Next(0, items.Count);
items.RemoveAt(index);
}
}
}
How about using the ItemsChanged event of your ListBox's ItemContainerGenerator instead?
this.myListBox.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(ItemContainerGenerator_ItemsChanged);
void ItemContainerGenerator_ItemsChanged(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
...

Resources