WP7 Itemtemplate click event fires on collapsed ExpanderView - windows-phone-7

I am using the ExpanderView control available in the Silverlight Toolkit with some custom templates. It all works well, but when the ExpanderView is collapsed, and I click on the area below the Header where an item resides when the ExpanderView is expanded. The click event of that item fires.
How can i fix this? Should I somehow remove the tap commands or remove the ItemPanel when the ExpanderView is collapsed and add it again when it's being expanded?
<DataTemplate x:Key="CustomItemTemplate">
<Image delay:LowProfileImageLoader.UriSource="{Binding}" Width="156" Height="95" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding Storage.ImageTapCommand, Source={StaticResource Locator}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</DataTemplate>
<toolkit:ExpanderView Grid.Column="1" Header="{Binding}"
Expander="{Binding}" IsExpanded="{Binding IsExpanded, Mode=TwoWay}"
ItemsSource="{Binding Files}" HeaderTemplate="{StaticResource CustomHeaderTemplate}"
ExpanderTemplate="{StaticResource CustomExpanderTemplate}"
ItemTemplate="{StaticResource CustomItemTemplate}" >
<toolkit:ExpanderView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</toolkit:ExpanderView.ItemsPanel>
</toolkit:ExpanderView>

You can change the IsHitTestVisible property of the root UIElement for each of your expander items every time the ExpanderView is expanded/collapsed, and also just after initially binding the ExpanderView (hooking up to ExpanderView.LayoutUpdated works fine for that purpose). Here's an example that fixed the issue for me:
private void FixExpanderItemsInteractivity(ExpanderView expanderView)
{
foreach (var item in expanderView.Items)
{
ContentPresenter contentPresenter = expanderView.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
if (contentPresenter != null)
{
UIElement expanderItemRootElement = VisualTreeHelper.GetChild(contentPresenter, 0) as UIElement;
if(expanderItemRootElement != null)
{
expanderItemRootElement.IsHitTestVisible = expanderView.IsExpanded;
}
}
}
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}
private void Expander_LayoutUpdated(object sender, EventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}

Related

How to update Preferences again after removing item to list in Xamarin

I have a list of CartUsers. I execute the command remove item from the list. I managed to remove each item in the list. However how does the list Preferences update.
My code:
cart.xaml
<StackLayout Padding="15,13" BindableLayout.ItemsSource="{Binding CartUsers}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Margin="0,2.5" x:DataType="model:CartUser">
<Label Text="{Binding NumberProduct}" />
<ImageButton x:Name="removeitem" Clicked="removeitem_Clicked" >
<ImageButton.Source>
<FontImageSource Color="red" Size="10" FontFamily="FontAwesome" Glyph="{x:Static local:FontAwesomeClass.TrashAlt}"/>
</ImageButton.Source>
</ImageButton>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
2.cart.xaml.cs
CartUserViewModel cartUserViewModel;
private void removeitem_Clicked(object sender, EventArgs e)
{
var mycart = Preferences.Get("CartUser", "defaultcart");
var getcart = JsonConvert.DeserializeObject<List<CartUser>>(mycart);
var button = sender as ImageButton;
var itemcart = button.BindingContext as CartUser;
cartUserViewModel.RemoveItemCart.Execute(itemcart);
//Preferences.Set("CartUser", JsonConvert.SerializeObject(getcart));
}
Update...
private void removeitem_Clicked(object sender, EventArgs e)
{
var mycart = Preferences.Get("CartUser", "defaultcart");
var getcart = JsonConvert.DeserializeObject<List<CartUser>>(mycart);
var button = sender as ImageButton;
var itemcart = button.BindingContext as CartUser;
cartUserViewModel.RemoveItemCart.Execute(itemcart);
getcart.Remove(itemcart);
int count = getcart.Count();
Preferences.Set("CartUser", JsonConvert.SerializeObject(getcart));
}
You just deleted a CartUser inside the ViewModel, and the Preferences have not changed.
According to your code, getcart is the List obtained using Preferences. You need to use getcart.Remove(itemcart); to delete an item. Then use your commented code to store it.

Passing data from list to new view xamarin

Ive created a picker with data inside, when selected the data is added to a list. I want to the values that are added to the list to follow to the next page when calculate button is pressed.. tried a few different approaches but i cant get it right..
.cs
// add from picker to listview function
ObservableCollection<LayersClass> listProducts = new ObservableCollection<LayersClass>();
private void MainPicker_SelectedIndexChanged(object sender, EventArgs e)
{
// feedback popup box
var product = MainPicker.Items[MainPicker.SelectedIndex];
DisplayAlert(product, "Layer added to calculation list", "OK");
// if selected add to list
if (null != product)
{
LayersClass layer = new LayersClass();
layer.Product = product;
listProducts.Add(layer);
}
}
//calculate button
private async void Button_Clicked(object sender, EventArgs e)
{
// send selected values to CalculationPage ??
await Navigation.PushAsync(new CalculationPage());
}
xaml:
<ListView
x:Name="productsListView"
HasUnevenRows="False"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Clicked="MenuItem_Clicked" Text="Delete" IsDestructive="true" CommandParameter="{Binding .}" />
</ViewCell.ContextActions>
<StackLayout>
<Label Text="{Binding Product}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Margin="0,0,0,90" Text="Calculate" Clicked="Button_Clicked" FontSize="Medium" TextColor="#00AB8E" HorizontalOptions="Center" BackgroundColor="Transparent"/>
You should use parameter to pass data to the another page.
private async void Button_Clicked(object sender, EventArgs e)
{
// send selected values to CalculationPage ??
//Also you should write there an event.
var item = sender as Button;
var selectedItem = item as LayersClass;
await Navigation.PushAsync(new CalculationPage(item));
}
public CalculationPage(string item){
//when you type here dot after typing item like this -> item. you can see the layerclass items.
}

wp7 listbox selection

I have a listBox with 1000+ items.
in xaml
<ListBox
//some code here
SelectionChanged="OnSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MyCustomItem/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In cs
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var item = lb.SelectedItem as MyCustomItem;
var vm = DataContext as ViewModel;
if (vm == null) return;
foreach (var it in vm.ItemsForBinding)
{
it.IsSelected = false;
}
item .IsSelected = true;
}
in MyCustomItem xaml
<UserControl
//Some code here
Style="{Binding Path=IsSelected, Converter = {StaticResource BoolToStyle}}">
<Border
</Border>
</UserControl>
Where IsSelected - one of property of my ViewModel. Converter return one of two style (first if item selected, second - if no)
Always work, but I know - it's very resource-intensive, and the wrong decision.
How to do it right?
This post help me!
Just override default ItemContainerStyle for my ListBox.
I removed:
metod OnSelectionChanged, UserControl MyCustomItem, converter for styles and
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MyCustomItem/>
</DataTemplate>
</ListBox.ItemTemplate>
from xaml.

Images inside a Pivot get downloaded and decoded more than once?

I set my Pivot's ItemsSource property to a list of 3 URL strings.
Odd things that happen:
With the template below, the ImageOpened event gets called 6 times instead of 3.
If I set the BitmapImage's CreateOptions to 'BackgroundCreation', the ImageOpened event can be called hundreds of times for no apparent reason
If I place the Pivot inside a UserControl and use the 'BackgroundCreation' option for the BitmapImage, then the Images don't render at all inside the Pivot, despite the ImageOpened event getting called (hundreds of times).
The same behaviour happens on WP7 and WP8. I am quite surprised as I was just creating an ImageGallery UserControl that is using a Pivot underneath and I wanted to make use of BackgroundCreation option to offload decoding work to the background thread but with such symptoms it seems quite pointless :(.
1) Pivot
<phone:PhoneApplicationPage
x:Class="WP7.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
shell:SystemTray.IsVisible="True">
<!--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 x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<Button Tap="UIElement_OnTap">press</Button>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<controls:Pivot x:Name="gallery">
<controls:Pivot.ItemTemplate>
<DataTemplate>
<Image>
<Image.Source>
<BitmapImage ImageOpened="BitmapImage_OnImageOpened" ImageFailed="BitmapImage_OnImageFailed" UriSource="{Binding}" CreateOptions="DelayCreation"></BitmapImage>
</Image.Source>
</Image>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
</Grid>
</Grid>
2) Codebehind
public partial class MainPage : PhoneApplicationPage
{
int c = 0;
List<string> list = new List<string>()
{
"http://d24w6bsrhbeh9d.cloudfront.net/photo/6298284_460s.jpg", "http://d24w6bsrhbeh9d.cloudfront.net/photo/6291760_460s.jpg", "http://d24w6bsrhbeh9d.cloudfront.net/photo/6298282_460s.jpg",
};
// Constructor
public MainPage()
{
InitializeComponent();
}
private void UIElement_OnTap(object sender, GestureEventArgs e)
{
c = 0;
gallery.ItemsSource = list;
}
private void BitmapImage_OnImageOpened(object sender, RoutedEventArgs e)
{
c++;
Debug.WriteLine("opened - {0}", c);
}
private void BitmapImage_OnImageFailed(object sender, ExceptionRoutedEventArgs e)
{
Debug.WriteLine("failed");
}
}
To give you a better idea of what is happening, you can check this snippet:
private void BitmapImage_OnImageOpened(object sender, RoutedEventArgs e)
{
BitmapImage image = (BitmapImage)sender;
Debug.WriteLine("opened - {0}", image.UriSource.ToString());
}
At the very beginning, when you press the button, you will notice what it seems like the first image from your list is loaded twice:
Even if you remove the DelayCreation flag, you will still get the same result because this flag is set by default.
Now, what happens if we inspect this with WireShark, the phone connected to an ad-hoc network:
(NOTE: I am using a different image URL here)
The image is only downloaded once, so per-se you do not need to worry about extra data. The reason why you see it triggered twice seems to be the fact that it is once triggered for the source, and another one for the Image.
Technically, the same if you would do this:
List<BitmapImage> list = new List<BitmapImage>();
// Constructor
public MainPage()
{
InitializeComponent();
BitmapImage image = new BitmapImage();
image.UriSource = new Uri("http://timenerdworld.files.wordpress.com/2012/08/new-microsoft-logo.jpg");
image.ImageOpened += image_ImageOpened;
list.Add(image);
}
void image_ImageOpened(object sender, RoutedEventArgs e)
{
Debug.WriteLine("opened image from bitmap");
}
private void UIElement_OnTap(object sender, GestureEventArgs e)
{
gallery.ItemsSource = list;
}
private void BitmapImage_OnImageOpened(object sender, RoutedEventArgs e)
{
Image image = (Image)sender;
Debug.WriteLine("opened - {0}", ((BitmapImage)image.Source).UriSource.ToString());
}
So no, the image is not downloaded twice.

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
{
}
}

Resources