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.
Related
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.
}
I need help understanding how I can bind a ContentView's Content's to my Xamarin page. I have tried maybe 20 different methods and I can only get the binded contentview to render when I don't use bindings and set the property directly.
<ContentPage.Content>
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="GridLayout" RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<nav:NavView Grid.Row="0" Grid.Column="0" Padding="0,0,0,0" Margin="0,0,0,0" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"
HeightRequest="60" x:Name="nav"/>
<ContentView BindingContext="{Binding MainView}" Grid.Row="1" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
and then below is my code behind
public partial class DetailLayoutView : ContentPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ContentView mainView;
public ContentView MainView
{
get { return mainView; }
set
{
mainView = value;
OnPropertyChanged("MainView");
}
}
public DetailLayoutView()
{
InitializeComponent ();
MainView = new PyxusChatView();
BindingContext = MainView;
}
}
Can someone help guide me to the right direction? I dont have the bandwidth to implement an entire MVVM refactor at this time I would just like to know how I can achieve this w/ minimal code.
DETECT WHEN A UI IS RENDERED IOS & ANDROID BELOW...
public DetailLayoutView()
{
InitializeComponent ();
MainContentArea.LayoutChanged += MainContentArea_LayoutChanged;
}
private void MainContentArea_LayoutChanged(object sender, EventArgs e)
{
Device.BeginInvokeOnMainThread(() => {
//UI Is now visible, send a msg to let everyone subscribed to "UpdateDetailView" event now that the
//detail page was changed and it is now okay to make API calls!
MessagingCenter.Send<ContentView, string>(MainView, "UpdateDetailView", "From BlePairingViewModel");
});
}
YOU HAVE SENT MSG NOW YOU CAN MAKE API CALLS & YOUR UI IS VISIBLE W/O MVVM... ALTHOUGH MVVM WOULD BE A BETTER PRACTICE TO INDIVIDUALS W/ MORE TIME TO CODE..
MessagingCenter.Subscribe<ContentView, string>("Pyx", "UpdateDetailView", (sender, arg) =>
{
if (sender == this)
{
Task.Run(async () =>
{
await OnAppearing();
});
}
});
I AM NOT ADVISING THIS AS BEST PRACTICE BUT IT DOES ANSWER THE QUESTION
I have a XAML UserControl which defines a fairly basic button - I want to define a Bindable property HasMeasurements which will display an overlay image when HasMeasurements is false . However when I include it in my project and bind it to the ViewModel it does not update consistently.
I am sure the ViewModel is properly notifying the bindings since I have simultenously bound the same ViewModel property to another separate element and it updates as expected. Also it works in Blend when I update the mock data.
I have tried this solution which defines a callback where I change the Visibility programatically, however this callback is not called every time the ViewModel property changes, only sometimes. I have also tried binding the Visibility in the XAML using this solution and a non dependency property which also did not work. I have also tried implementing NotifyPropertyChanged out of desperation but no luck there either ...
Here is my XAML,
<UserControl x:Class="MyApp.View.Controls.ConversionBtn"
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:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Grid x:Name="btnGrid" toolkit:TiltEffect.IsTiltEnabled="True" Height="115">
<Border Background="{StaticResource ImgOverlayColor}" BorderThickness="0" Padding="0" VerticalAlignment="Top" >
<TextBlock x:Name="titleTxtBlock" FontSize="{StaticResource PhoneFontSizeMedium}" Foreground="{StaticResource TileTxtColor}" Margin="6,0,0,0"/>
</Border>
<Image x:Name="notAvailableImg" Source="/Images/ConversionNotAvailableOverlay.png" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" />
</Grid>
</Grid>
</UserControl>
Here is the code behind,
// usings here ...
namespace MyApp.View.Controls
{
public partial class ConversionBtn : UserControl
{
public ConversionBtn()
{
InitializeComponent();
if (!TiltEffect.TiltableItems.Contains(typeof(ConversionBtn)))
TiltEffect.TiltableItems.Add(typeof(ConversionBtn));
//this.DataContext = this;
}
public string Title
{
get { return this.titleTxtBlock.Text; }
set { this.titleTxtBlock.Text = value; }
}
public static readonly DependencyProperty HasMeasurementsProperty =
DependencyProperty.Register("HasMeasurements", typeof(bool), typeof(ConversionBtn),
new PropertyMetadata(false, new PropertyChangedCallback(HasMeasurementsPropertyChanged)));
private static void HasMeasurementsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ConversionBtn cBtn = (ConversionBtn)d;
bool val = (bool)e.NewValue;
if (val)
{
cBtn.notAvailableImg.Visibility = Visibility.Collapsed;
}
else
{
cBtn.notAvailableImg.Visibility = Visibility.Visible;
}
cBtn.HasMeasurements = val;
}
public bool HasMeasurements
{
get { return (bool)GetValue(HasMeasurementsProperty); }
set { SetValue(HasMeasurementsProperty, value); }
}
}
}
You have a callback, that is called after HasMeasurment propetry was changed.
And in a callback you change it again. So, you have a logical misstake.
If you need to do something with this value - just save it in private field.
private static void HasMeasurementsPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ConversionBtncBtn = (ConversionBtn)d;
bool val = (bool)e.NewValue;
if (val)
{
cBtn.notAvailableImg.Visibility = Visibility.Collapsed;
}
else
{
cBtn.notAvailableImg.Visibility = Visibility.Visible;
}
cBtn.SetMeasurments(val);
}
private bool measurmentsState;
public void SetMeasurments(bool value)
{
measurmentsState = value;
}
Here you can get free e-Book by Charls Petzold about Windows Phone Development, there is a nice chapter about Dependency Properties.
Ah damnit, it was a combination of Anton's answer and the fact that I hadn't set my image as 'Content', hence it loaded in Blend but was not present in the deployed app.
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);
}
I have a .wav music file.
When I tried to play using SoundEffect.Play();
mus1.Play(); I am getting noise rather than music.
The same is played well in PCs music player.
How to resolve this issue?
How to make sure that it will play good in Windows Phone 7 too?
It sounds like there's content in the .wav file that WP7 doesn't understand. Why don't you try converting it to .wav (yes I know it's already in .wav) as this may remove the unknown data.
Audacity (http://audacity.sourceforge.net/) is free and will do this for you. Just drag the .wav file into it and click file --> export...
Try the following Code
MainPage.xaml.cs
namespace MediaSample
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
progress.DataContext = this;
this.Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
mediaplayer.BufferingProgressChanged += new System.Windows.RoutedEventHandler(mediaplayer_BufferingProgressChanged);
mediaplayer.CurrentStateChanged += new RoutedEventHandler(mediaplayer_CurrentStateChanged);
}
public double Duration { get; set; }
void mediaplayer_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if (mediaplayer.CurrentState == MediaElementState.Playing)
{
Duration = mediaplayer.NaturalDuration.TimeSpan.TotalSeconds;
ThreadPool.QueueUserWorkItem(o =>
{
while (true)
{
Dispatcher.BeginInvoke(new Action(() => Progress = mediaplayer.Position.TotalSeconds * 100 / Duration));
Thread.Sleep(0);
}
});
}
}
void mediaplayer_BufferingProgressChanged(object sender, System.Windows.RoutedEventArgs e)
{
MediaElement mediaplayer = sender as MediaElement;
if (mediaplayer.BufferingProgress == 1)
Debug.WriteLine(mediaplayer.NaturalDuration);
}
void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
mediaplayer.Source = new Uri("/Oru nalum..mp3", UriKind.Relative);
mediaplayer.Play();
}
public double Progress
{
get { return (double)GetValue(ProgressProperty); }
set { SetValue(ProgressProperty, value); }
}
// Using a DependencyProperty as the backing store for Progress. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(PhoneApplicationPage), new PropertyMetadata(0.0));
}
}
MainPage.xaml
<phone:PhoneApplicationPage
x:Class="MediaSample.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"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="Media Application" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<MediaElement x:Name="mediaplayer"/>
<ProgressBar Height="40" x:Name="progress" Value="{Binding Path=Progress}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Add Mp3 file as follows,
Like that you can add .wav files. Working good. Tested codes above.
Thank you.
Use MeadiaElement Rather than using SoundEffect Class.
try this one
MediaElement