I'm messing around with some windows phone development as I'm new to it coming from an Android background.
I am using "theCatAPI" to load a random picture of a cat and show it, then when the picture is clicked, or the button at the bottom of the screen, the image refreshes to a new one.
I have the following so far:
XAML:
<Page
x:Class="CatFactsPics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CatFactsPics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- TitlePanel -->
<StackPanel Grid.Row="0" Margin="24,17,0,28">
<TextBlock Text="My Application" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/>
<TextBlock Text="page title" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot">
<Image HorizontalAlignment="Center" Stretch="UniformToFill" VerticalAlignment="Center" x:Name="KittyPic" Tapped="KittyPic_Tapped"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
</Grid>
</Grid>
</Page>
and in the page.cs:
...
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
KittyPic.Source = new BitmapImage(myUri);
}
...
private void newPic_Click(object sender, RoutedEventArgs e)
{
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = myUri;
KittyPic.Source = bmi;
}
I have a couple of questions:
1) is this the correct way of doing things? In Android I'd try and do things asynchronously to avoid stalling the UI thread. That being said, I don't appear to be having any issues with things the way they are now. I'm not familiar with the Windows way of doing things, and haven't found any resources giving any explanation or advice on doing so.
2) There is a delay in displaying the new picture causing a short (couple of second) period where the image view turns black, before the new image reappears. Is there a way of setting it up so either the old picture remains until the new one is physically ready to be displayed, or alternatively display a placeholder "loading" image until the new one can replace it.
Any other advice or tips on how to do things would be great, thanks.
1) With your current code you do not block the UI thread as yes you are setting the URI on the UI thread, but the actually loading of the image is done on another thread automatically. (For doing manually downloading of images, strings etc, you will probably use async/await to avoid locking the UI thread).
2) The image goes black because you change the ImageSource before the new image has loaded. There are as you mention several ways to deal with this. Common for most of them though is that you will want to use the ImageOpened and ImageFailed events on the Image control, which triggers whenever the image is done loading (or an error occurred, for example no internet connection). Here is an example of displaying a loading bar while it is loading, which just hides/shows the loading progress:
In the page.xaml file:
<Grid Grid.Row="1" x:Name="ContentRoot">
<Image x:Name="KittyPic" Tapped="KittyPic_Tapped" ImageOpened="KittyPic_ImageOpened" ImageFailed="KittyPic_ImageFailed" />
<StackPanel x:Name="LoadingPanel" VerticalAlignment="Center">
<ProgressBar IsIndeterminate="True" IsEnabled="True" />
<TextBlock Text="Loading image..." HorizontalAlignment="Center" />
</StackPanel>
</Grid>
And in the page.xaml.cs file
private void KittyPic_Tapped(object sender, TappedRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Visible;
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = myUri;
KittyPic.Source = bmi;
}
private void KittyPic_ImageOpened(object sender, RoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
}
private async void KittyPic_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
await new MessageDialog("Failed to load the image").ShowAsync();
}
Instead of the TextBlock and ProgressBar you could of course show whatever you want, or for example swapping between two images to keep showing the old one.
For other advice I think when you got used to the basics is to take a look at data bindings which is very helpful and powerful. Also take a look at this article about MVVM pattern.
1) is this the correct way of doing things?
No. It's not. You should definitely adopt the MVVM pattern and detach your business logic from your view - meaning you shouldn't create bitmap images or request/assign such remote image URL's in your view's code-behind. You should be doing such stuff in your ViewModel and bind them to your View.
So in your case there will be an Uri (or a string where you will assign the remote URL) property in your ViewModel (which implements INotifyPropertyChanged) then in your View you will be binding it like this:
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot">
<BitmapImage UriSource="{Binding BackGroundImage}" CreateOptions="BackgroundCreation" />
<Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
</Grid>
Whenever you set the BackGroundImage property you will be raising an event called;
RaisePropertyChanged("BackGroundImage") -> This is the classical MVVM approach.
So that your view will be aware of the fact that the BackGroundImage is changed and it will load it automatically. (But note that if you just provide a string for this BackGroundImage - you will have to use a converter, a string to Uri converter, since it only accepts Uri's for remote images)
2) "...Is there a way of setting it up so either the old picture remains until the new one is physically ready to be displayed, or alternatively display a placeholder "loading" image until the new one can replace it."
I suggest going with displaying a 'loading' image. I experienced the exact same problem as you do here and my workaround for this was inserting a loading image and setting it's opacity value to 0.1 - along with the actual image. While you are switching between remote URL's, when the previous image disappears the opaque loading image appears and when the next actual image is loaded the loading image is not displayed because new image is overwriting it.
Related
How can I let a user of a Windows Universal App swipe from one page to another? (I thought this would be easy to find, but searching hasn’t uncovered the answer.)
And if this is possible within one page - that's fine too. (To swipe one grid out and another in.)
Pivot control behaves like you discribed.
See guidelines for tabs and pivots.
Example:
<Page x:Class="App1.MainPage"
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"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot>
<PivotItem Header="Item 1" Background="Black" />
<PivotItem Header="Item 2" Background="Red" />
<PivotItem Header="Item 3" Background="Blue" />
</Pivot>
</Grid>
You can use GestureRecognizer and manipulate what you wanna do. Create animation for the FX.
I want to say use a FlipView control, but that could be dangerous if your views are too complex. FlipView will keep your pages rendered and ready to be flipped to at all times. I think you can try to implement your own thing to keep memory usage low. Maybe use a GestureRecognizer so that you have control over where the user can swipe and only render what you need and discard anything obsolete or off the screen.
Pivot will also create this effect, but the difference is that it must completely slide one element off the screen and then slide the next one in. It keeps from having two or three views rendered at once, which is good for memory. However, you won't be able to see both pages sliding in/out at the same time.
Try them both, see which is best for you.
I have something similiar to what you're asking:
How to "swipe" from one page to another:
On page 1 (The page where you will swipe FROM)
Make a grid, and put these values in:
XAML:
<Grid Padding="15,15,15,15"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
ManipulationMode="TranslateX,TranslateInertia,System"
ManipulationDelta="SwipeablePage_ManipulationDelta"
ManipulationCompleted="SwipeablePage_ManipulationCompleted">
Code Behind:
private bool _isSwiped;
private void SwipeablePage_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (e.IsInertial && !_isSwiped)
{
var swipedDistance = e.Cumulative.Translation.X;
if (Math.Abs(swipedDistance) <= 2) return;
if (swipedDistance > 0)
{
// go to next page
this.Frame.Navigate(typeof(Page2));
}
else
{
// do nothing
}
_isSwiped = true;
}
}
Ultimate Goal
When a user clicks the Expand button, I want the WebContainerControl to be full screen, be focused, not allow scrolling in the ScrollViewer, and overlap the title grid (with the back button, page title, etc.)
Basically, it should be like clicking on a photo in a nice photo viewing app. Exapnd to full screen, have an X button in the top right corner and when you click it, it goes back to the regular view.
Problem
Since it's a WebView, I can't simply pass the view to a popup (It gives me an invalid args exception since the current browsing session can't be passed by reference... they logged in on a site, so it would be insecure I assume)
I have a XAML control with a webview in it:
<UserControl x:Class="App.WebContainerControl">
<Grid x:Name="grdWebContainer">
<StackPanel>
<Button Click="btnExpandView_Click"/>
<WebView x:Name="wvSite"/>
</StackPanel>
</Grid>
</UserControl>
Here is an example view it would be loaded into:
<Grid x:Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title Grid -->
<Grid x:Name="grdTitleBar" Grid.Row="0">
<TextBlock Text="App Title"/>
</Grid>
<!-- Web Views -->
<Grid Grid.Row="1">
<ScrollViewer>
<StackPanel>
<controls:WebContainerControl x:Name="First Site"/>
<controls:WebContainerControl x:Name="Second Site"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
What I have so far
So far, when they press the Expand button, it makes the control full screen (using Current.Window.Bounds)
Then, I pass the event that the button is pressed to the main view:
private void OnAccount_Expanded(object sender, ExpandedEventArgs args) {
// Expanded button is pressed and control is made full screen
if (args.IsExpanded) {
// Hide titlebar
grdMain.RowDefinitions[0].Height = GridLength.Auto;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else {
// show titlebar again
GridLength gl = new GridLength(140);
grdMain.RowDefinitions[0].Height = gl;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
The Question
Right now, it makes it full screen, but I can still scroll. Any idea how to set the ScrollViewer to horizontally center on the control? If anyone has a better idea on how to achieve my Ultimate Goal, you would make me one happy camper! (Remember, it won't allow me to pass my control around, only manipulate it)
If I understand well, Put name on you Scroll Viewer
<ScrollViewer Name="uiScroll" >
............
</ScrollViewer >
When you doing full screen set visibility of you scrollBar
uiScroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
I am working on a windows store 8.1 app, I have added Grids in MainPage.xaml using List in MainPage.xaml.cs
MainPage.xaml
<GridView Margin="20" x:Name="main" SelectionMode="None" IsItemClickEnabled="True" ItemClick="main_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Red" Width="250" Height="200">
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Stretch="UniformToFill" Source="{Binding ImageLocation}"/>
<TextBlock Text="{Binding Title}" Grid.Row="1" FontSize="28" />
<TextBlock Text="{Binding SubTitle}" Grid.Row="2" FontSize="16" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
List<data> myList = new List<data>();
myList.Add(new data()
{
ImageLocation = #"Assets/network.png",
iName = "NetWork",
SubTitle ="Network",
Title = "Network"
});
myList.Add(new data()
{
ImageLocation = #"Assets/fb.png",
iName = "Facebook",
SubTitle = "Facebook",
Title = "Facebook"
});
main.ItemsSource = myList;
}
private void main_ItemClick(object sender, ItemClickEventArgs e)
{
Frame.Navigate(typeof(ListView));
}
I want that when someone click on any of the grids, a TextBlock in ListView page show which grid was clicked in MainPage .
This will be a challenge to explain without showing you in code, but here goes...
Hopefully you have created two pages so far. MainPage.xaml that holds your GridView. And a DetailsPage.xaml that will have the layout to show one item.
In the code-behind of MainPage.xaml, like you have in your sample code, you handle the ItemCLick of the GridView, but you want to get the Id of the item clicked, not the item itself. The reason for this is that you want to pass a string, and not a complex object.
In your handler, the event args (e) has a property called ClickedItem that will be the item you are binding to. Let's pretend it's a UserObject you are binding to. In your handler do something like this:
var user = e.ClickedItem as UserObject;
this.Frame.Navigate(typeof(DetailPage), user.Id.ToString());
So, what's happening here? Almost the same code you had before. Except you are navigating to the type of the second page instead of anything else. You are also passing in (the second argument in the Navigate method) the exact record you want to show.
Then in your DetailPage.xaml code-behind you ned to override the OnNavigatedTo method. This method is what is invoked when the Navigation framework directs to the page. It's has a NavigationPararmeter passed to it that you can use to extract the key you passed.
I think it's actually args.Parameter you want to use. You can parse it to an integer and use that to fetch the individual record you have somehow in memory in your application.
var id = int.Parse(args.Parameter);
var user = YourFactory.GetUser(id);
The reason I shifted from this is how you do it to "I think this is how it works" is because although the basic framework operates like this, most developers do not use it like this. Most developers implement something like Prism.StoreApps which introduces not only a lightweight MVVM framework, but also a sophisticated NavigationService that lets you inject parameters directly into an auto-associated view model.
But based on the simplicity of your question, try not to pay attention to that last bit. I explained the basic workflow using the in-box framework. It works just fine, and it will get the job done. When you are ready to write a more advanced implementation you can investigate Prism.StoreApps
More info: http://msdn.microsoft.com/en-us/library/windows/apps/xx130655.aspx
Best of luck!
In my app there is a canvas with a small image (not taking up entire space of canvas).
when user clicks on image and drags inside the canvas, it traces the path with a black line. Till here everything works. Now, I want the path to be deleted as soon as the user releases the hold on the mouse. I am using MouseLeftButtonUp on the canvas to detect the mouse release. Now problem is this is working randomly. sometimes it fires and sometimes it does not. Kindly help.
XAML:
<Canvas Height="400" HorizontalAlignment="Center" Margin="10,10,0,0" Name="canvas1" VerticalAlignment="Center" Background="Aqua" Width="400">
<Image Canvas.ZIndex="30" Canvas.Left="10" Canvas.Top="10" Height="20" Name="dot1" Stretch="Uniform" Width="20" Source="/BoxIt;component/Images/dot.png" MouseLeftButtonDown="dot_MouseLeftButtonDown" />
</Canvas>
C#:
this.canvas1.MouseMove += new MouseEventHandler(canvas1_MouseMove);
this.canvas1.MouseLeftButtonUp += new MouseButtonEventHandler(canvas1_MouseLeftButtonUp);
Eventhandler in C# :
void canvas1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// code to delete the path
}
In Windows Phone we are advised to use the Tap, Hold and Manipulation* methods instead of the Mouse related events.
Please read this for more information.
How to make a button let a picture show up ?
what i Need to do, is creating 300 buttons each of these buttons will represent a unique path and pic every pic is defferent than the other? Unfortunately, WPF SketchFlow or Silverlight SketchFlow (I don't know the difference) can not be running as a program on computer. so what left is WFP app. Furthermore, i find it so difficult to just create one button linked to a pic's path!!!!.
I watched all of the Microsoft training Videos(the 5 days ones, if u dont know what i mean)http://www.microsoft.com/expression/resources/blendtraining/ . And still cant do it. I was searching for almost 4 weeks, and i still do.
Any idea how to create this essay app which require lots of work.
summery: 1-I don't know which app to use Silverlight or WPF app?(I don't know the difference)
2-i need to create a button that when i press on it, (New sorry) there will be a pic showing up in certain place that ill choose).
so in the end i need the code that will let the button work. direction for further info that ill need(websites that include vids or training by any type)
My regards SHeeDeED :)
Maybe you should start with a simple program: 1 button, showing 1 picture.
Handcraft it in XAML (no blend) and when you're stuck post a specific question here with the code.
I use Blend for most of my XAML/WPF UI design, and it works pretty well for me. I did, however, learn WPF and XAML by writing it longhand in the Visual Studio designer first. For any coding other than XAML (VB.NET, C#, C++, etc) I would highly suggest using Visual Studio as there are no bells and whistles in the Blend code window. It is essentially a colorful text editor.
As for buttons displaying images I have a couple of questions. I imagine your buttons are going to be loaded from a collection of some objects or something. By far, this is better than specifying each button in the XAML code.
I will assume your collection of objects has an ImagePath (or similarly named) property. The following should work out alright.
Load your ListBox with the Items, (see either http://www.WindowsClient.net for the old [Windows Forms] way, or read up on MVVM for the newer better way). Below is my listbox; ItemsSource is bound to the ImageCollection property on my ViewModel in this case. The items in my Imagecollection have an ImagePath property that is just a string file path to the image.
<ListBox ItemTemplate="{DynamicResource MyImageButton}" ItemsSource="{Binding ImageCollection}"/>
In Blend, you can create a DataTemplate by right clicking on the listBox, and going to Edit Additional Templates > Edit Generated Items (ItemTemplate).
From there you are just editing the layout of the items that will be created in the listbox. Below is my example of an item button with an image bound to the ImagePath property.
<DataTemplate x:Key="MyImageButton">
<Button Width="75">
<Button.Content>
<StackPanel>
<Image Source="{Binding ImagePath}" HorizontalAlignment="Left" Height="64" Width="64"/>
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
Let me know if you need more info and I can post some more resource links.
EDIT
Alright, so here is a simple little application that I made to illustrate how this can be done probably the easiest as possible. In this application, I have 3 classes:
ImageButton - It just consists of two string properties, ImagePath and ImageName.
ImageButtonCollection - Inherited from ObservableCollection, creates and adds 300 buttons (iterates 1 to 300 and sets ImagePath to "C:\Images\image{i}.png" and ImageName to "image{i}".
The MainWindow class - I will post the contents below.
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ImageButtons"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="ImageButtonsCVS"/>
</Window.Resources>
<Grid>
<ListBox Height="311" HorizontalAlignment="Left" x:Name="ListBox1" VerticalAlignment="Top" Width="268" HorizontalContentAlignment="Stretch">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:ImageButton}">
<Border Background="#5A000000" CornerRadius="5">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.ColumnSpan="1" Height="30" Width="30" BorderBrush="#38FFFFFF" BorderThickness="1" Margin="5" Padding="0">
<Image Source="{Binding ImagePath}"/>
</Button>
<TextBlock Margin="0" TextWrapping="Wrap" Text="{Binding ImageName}" d:LayoutOverrides="Width, Height" Grid.Column="1" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemsSource>
<Binding Source="{StaticResource ImageButtonsCVS}"/>
</ListBox.ItemsSource>
</ListBox>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="322,54,0,0" x:Name="Button1" VerticalAlignment="Top" Width="75" Click="CreateButtons" />
</Grid>
MainWindow.xaml.vb - I am more familiar with VB, but this can be easily ported to C# or whatever.
Class MainWindow
Private Sub CreateButtons(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim ImageButtonCVS As CollectionViewSource = Me.FindResource("ImageButtonsCVS")
ImageButtonCVS.Source = New ImageButtonCollection
End Sub
End Class
So, the ListBoxes ItemsSource property is bound to the CollectionViewSource created in the Window.Resources. The ListBox also has in it's resources a DataTemplate for the ImageButton class. Anytime an ImageButton is displayed in that listbox, that template will be used. The buttons click event is set to the CreateButtons method in the codebehind.
That method finds the CollectionViewSource resource, and sets it's source to a new instance of the ImageButtonCollection class. Once that is done, the UI is notified of an update (since the ObservableCollection has built in UI notification) and changes accordingly displaying 300 buttons with a little textblock next to them with the image name.
If you run this, create a folder on your C drive with some images named "imageX.png" with x being a number 1 to 300. Below are the other two classes so you can create/compile/run it.
ImageButton class
Public Class ImageButton
Public Property ImagePath As String
Public Property ImageName As String
Public Sub New()
End Sub
Public Sub New(ByVal Path As String, ByVal Name As String)
Me.ImagePath = Path
Me.ImageName = Name
End Sub
End Class
ImageButtonCollection class
Imports System.Collections.ObjectModel
Public Class ImageButtonCollection
Inherits ObservableCollection(Of ImageButton)
Public Sub New()
For i As Integer = 1 To 300
Me.Add(New ImageButton(String.Format("C:\Images\image{0}.png", i), String.Format("Image{0}", i)))
Next
End Sub
End Class
You never specified how you wanted the image displayed. You may not want them to be in a listbox. The beauty of WPF is that once you get it working in a listBox or whatever ItemsControl, you can easily change to another ItemsControl, or even customize one. Go into the ListBox template and change the ItemsHost to use a uniform grid or to stack items horizontally instead of vertically.