I'm developping a silly application that:
- opens a pictures from media library,
- puts the chosen image in a grid element (which contains a TextBlok element)
- save the pictures in the "saved pictures" album.
My XAML code is:
<controls:PanoramaItem Header="Image Selection" Height="652">
<Grid Name="markedImage" Margin="0,0,4,89">
<Image x:Name="img" Stretch="Fill" Margin="0,0,0,10"></Image>
<TextBlock x:Name="text" Text="Hello!">
<i:Interaction.Behaviors>
<el:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
</TextBlock>
</Grid>
and the code to open and save the chosen picture is:
private void photoChooserTask_Completed(object sender, PhotoResult e)
{
try
{
BitmapImage image = new BitmapImage();
image.SetSource(e.ChosenPhoto);
WriteableBitmap wbp = new WriteableBitmap(image);
this.img.Source = image;
height = image.PixelHeight;
width = image.PixelWidth;
MessageBox.Show("H: " + height + "\t" + "W: " + width);
}
catch
{
MessageBox.Show("Disconnect your device from Zune");
}
}
private void save_Click(object sender, System.EventArgs e)
{
WriteableBitmap marked = new WriteableBitmap(this.markedImage, null);
ThreadPool.QueueUserWorkItem(callback =>
{
MemoryStream ms = new MemoryStream();
marked.SaveJpeg(ms, (width * 2), (height * 2), 0, 100);
using (MediaLibrary lib = new MediaLibrary())
lib.SavePicture("Test", ms.ToArray());
});
MessageBox.Show("H: " + marked.PixelHeight + "\t" + "W: " + marked.PixelWidth);
// wbm.SaveToMediaLibrary("SavedPicture.jpg", true);
MessageBox.Show("Picture saved successfully");
}
I can't post pictures because I'm a new user, anyway pictures (orignal and saved pic) have the same height and width but they look different
I think that the problem is the grid dimension and Stretch property. I tried different combo but results were not good.
Some suggestion?
edit: i earned reputation point
original pics is
saved pics is
if you open both in another window, you can see the difference
The problem is quite simple to understand: basically, you're doing a 'screenshot' of your grid. Therefore, the size of the generated picture is the same as the size of the grid. Hence the down-sampled picture.
Fixing that however can be tricky. I can suggest two ways:
Recreate programmatically the grid and its contents in the code-behind, then create the WriteableBitmap from this new grid
Remove the grid from its parent control just before executing your image-generation code (then the grid will be able to use as much space as needed), and add it back afterwards:
<Grid x:Name="ParentGrid" Grid.Row="1" Width="200" Height="150">
<Grid Name="markedImage" Margin="0,0,4,89">
<Image x:Name="img" Source="Images/Test.jpg" Stretch="Fill" Margin="0,0,0,10"></Image>
<TextBlock x:Name="text" Text="Hello!">
<i:Interaction.Behaviors>
<el:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
</TextBlock>
</Grid>
</Grid>
And the code-behind:
this.ParentGrid.Children.Remove(this.markedImage);
WriteableBitmap marked = new WriteableBitmap(this.markedImage, null);
MemoryStream ms = new MemoryStream();
marked.SaveJpeg(ms, 1349, 1437, 0, 100);
using (MediaLibrary lib = new MediaLibrary())
lib.SavePicture("Test", ms.ToArray());
MessageBox.Show("H: " + marked.PixelHeight + "\t" + "W: " + marked.PixelWidth);
MessageBox.Show("Picture saved successfully");
this.ParentGrid.Children.Add(this.markedImage);
Related
I'm having an issue in my UWP Win2D app. I have an image and text being displayed in a CanvasAnimatedControl, and when I resize the window, it stretches both image and text instead of maintaining their proportions:
This is a portion of the code which loads and draws the image:
private void Draw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args)
{
if (!IsLoadInProgress())
{
args.DrawingSession.DrawImage(_bitmap);
DrawText(args, "...TESTING TEXT...");
}
}
private void DrawText(CanvasAnimatedDrawEventArgs args, string text)
{
using (var textFormat = new CanvasTextFormat()
{
FontSize = 96,
FontFamily = "Segoe UI",
FontWeight = FontWeights.Medium
})
{
args.DrawingSession.DrawText(
text,
new Vector2(20, (float)_bitmap.Size.Height / 2),
Colors.White,
textFormat);
}
}
private async Task SetImageAndSize()
{
if (_imagePath != null)
{
_bitmap = await CanvasBitmap.LoadAsync(AnimatedControl, new Uri(_imagePath, UriKind.RelativeOrAbsolute));
// Set the controls size according to image pixels to display for image
AnimatedControl.Height = _bitmap.SizeInPixels.Height;
AnimatedControl.Width = _bitmap.SizeInPixels.Width;
}
}
The sample project can be downloaded here:
PROJECT LINK
What do I need to change so that the Image and Text don't stretch on resize??
Found my solution, I use a ViewBox:
<Viewbox Stretch="Fill">
<canvas:CanvasAnimatedControl x:Name="AnimatedControl" />
</Viewbox>
I had to change the Stretch on it:
<Viewbox Stretch="UniformToFill">
<canvas:CanvasAnimatedControl x:Name="AnimatedControl" />
</Viewbox>
How can I enlarge image in C# (exactly in UWP) after clicking on it?
I tried few things:
1) I tried to add button, with image content, what I want to enlarge, and then I added event Click. But I don't know what I should to add into that code.
2) i also tried to add image directly to my XAML page, and I wanted to create Tapped event, but again, I don't know what I should to add into that code.
I just want to create a small photogallery, so after clicking on image thumbnail will be opened larger image.
Or if there is any possibility to add pdf files, you can write it too. That's another solution of my problem.
You could enlarge the Image by settings its RenderTransform property to a ScaleTransform:
private void Image_Tapped(object sender, TappedRoutedEventArgs e)
{
Image image = sender as Image;
image.RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 };
}
<Image Source="ms-appx:///Assets/pic.png" Tapped="Image_Tapped" Stretch="None" />
The ScaleX and ScaleY properties gets or sets the scaling factor. Please refer to the MSDN documentation for more information: https://msdn.microsoft.com/library/windows/apps/br242940?f=255&MSPPError=-2147217396
Looks good, but there is a problem. When I add more images, like into GridView, they are overlapping, and highlighted. Images can overlap, but image, which I click should be always on top...
You could put the tapped Image in a Popup then and then for example add it back to its original Panel when it is tapped again. I put together an example that should give you the idea and something to build on:
private void Image_Tapped(object sender, TappedRoutedEventArgs e)
{
Image image = sender as Image;
Panel parent = image.Parent as Panel;
if (parent != null)
{
image.RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 };
parent.Children.Remove(image);
parent.Children.Add(new Popup() { Child = image, IsOpen = true, Tag = parent });
}
else
{
Popup popup = image.Parent as Popup;
popup.Child = null;
Panel panel = popup.Tag as Panel;
image.RenderTransform = null;
panel.Children.Add(image);
}
}
<GridView SelectionMode="None" isItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="ms-appx:///Assets/pic.png" Tapped="Image_Tapped" Stretch="None" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Please, help is needed. In this code have to be stupid mistake but what it is? If I run this very simple code I can load and change bitmap image only once. At first time it runs ok but if I like to change the image again (press the button) the first image remains. Why?
XAML
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button x:Name="AddProfilePicture" HorizontalAlignment="Center" Click="AddProfilePicture_Click">
<Grid Width="200" Height="200">
<Ellipse Width="200" Height="200">
<Ellipse.Fill>
<ImageBrush x:Name="ImageBrush_ProfilePicture" Stretch="UniformToFill"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
</Button>
</StackPanel>
</Grid>
CODE
private async void AddProfilePicture_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker profilePictureFilePicker = new FileOpenPicker();
profilePictureFilePicker.ViewMode = PickerViewMode.Thumbnail;
profilePictureFilePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
profilePictureFilePicker.FileTypeFilter.Add(".jpg");
StorageFile userSelectedProfilePicture = await profilePictureFilePicker.PickSingleFileAsync();
//MAKE SURE U HAVE THIS FILE (ProfilePicture.jpg) IN FOLDER ALREADY....
StorageFile destinationFileImage = await StorageFile.GetFileFromPathAsync(ApplicationData.Current.LocalFolder.Path + #"\" + "ProfilePicture.jpg");
await userSelectedProfilePicture.CopyAndReplaceAsync(destinationFileImage);
Uri profilePictureBitmapURI = new Uri(ApplicationData.Current.LocalFolder.Path + #"\" + "ProfilePicture.jpg");
BitmapImage profilePictureBitmap = new BitmapImage(profilePictureBitmapURI);
ImageBrush_ProfilePicture.ImageSource = profilePictureBitmap;
}
This code doesn't have any extra check. It means the image 'ProfilePicture.jpg' have to be in a folder before running app. Code works well at first run but on the second run (press the button again and selecting new picture) cannot change output on screen even the source change in folder.
While creating BitmapImage set CreateOptions to IgnoreImageCache.
var profilePictureBitmap = new BitmapImage(profilePictureBitmapURI) {CreateOptions = BitmapCreateOptions.IgnoreImageCache};
Try to add the Load() method and see if that helps:
private async void AddProfilePicture_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker profilePictureFilePicker = new FileOpenPicker();
profilePictureFilePicker.ViewMode = PickerViewMode.Thumbnail;
profilePictureFilePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
profilePictureFilePicker.FileTypeFilter.Add(".jpg");
StorageFile userSelectedProfilePicture = await profilePictureFilePicker.PickSingleFileAsync();
//MAKE SURE U HAVE THIS FILE (ProfilePicture.jpg) IN FOLDER ALREADY....
StorageFile destinationFileImage = await StorageFile.GetFileFromPathAsync(ApplicationData.Current.LocalFolder.Path + #"\" + "ProfilePicture.jpg");
await userSelectedProfilePicture.CopyAndReplaceAsync(destinationFileImage);
Uri profilePictureBitmapURI = new Uri(ApplicationData.Current.LocalFolder.Path + #"\" + "ProfilePicture.jpg");
BitmapImage profilePictureBitmap = new BitmapImage(profilePictureBitmapURI);
ImageBrush_ProfilePicture.ImageSource = profilePictureBitmap;
ImageBrush_ProfilePicture.Load()
}
I have the follow XAML:
<ContentControl HorizontalAlignment="Left" HorizontalContentAlignment="Left" Content="{Binding TotalReviewWordBlock}" Width="465" Margin="5,10,0,5" Foreground="#FF2D2D2D" Background="White"/>
and its binded to the following property:-
public StackPanel TotalReviewWordBlock
{
get
{
StackPanel st = new StackPanel();
st.Orientation = Orientation.Horizontal;
st.Background = new SolidColorBrush(Colors.White);
Paragraph pgf = new Paragraph();
Run r = new Run();
r.Text = App.Convert("Blah ");
r.FontWeight = FontWeights.Bold;
r.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(r);
int Rating = (int)(this.userrating * 2);
string ratingReplacement;
(some more code in the property itself...)
Run run = new Run();
run.Text = " " + this.myText;
run.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(run);
RichTextBox rtb = new RichTextBox();
rtb.TextWrapping = TextWrapping.Wrap;
rtb.Width = 450;
rtb.Blocks.Add(pgf);
st.Children.Add(rtb);
st.Background = new SolidColorBrush(Colors.White);
return st;
}
}
The problem is when the text is too much(say more that a 1000 character), or the height of the stackpanel is a lot, Its background becomes black. Its as if the stackpanel breaks) I noticed this earlier but at that time it was in a listbox and had multiple items to i simply made the width of each item 480, used blank grids instead of margins and it was "covered". But this time its just one big chunk of text(in a Paragraph). Let me know if you need ay other info. Please help!!
I worked around a similar "black stackpanel" problem by splitting the text into paragraphs to form a List<String>. And then that list of strings would be the ItemsSource of a ListBox.
So instead of a very large StackPanel, I ended up with a long ListBox.
I also prevented user interaction in the ListBox and vertical scroll by using IsHitTestVisible="False" and ScrollViewer.VerticalScrollBarVisibility="Disabled"
So, the ListBoxended up as follows:
<ListBox x:Name="listBox" IsHitTestVisible="False" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="White">
<TextBlock TextWrapping="Wrap" Text="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in code behind:
textSplitInParagraphs = new List<String>();
// add paragraphs to the list...
listBox.ItemsSource = textSplitInParagraphs;
Don't know if it is the correct workaround, but I helped me, after some time of banging my head against the table.
Hope this helps.
I have the following xaml
<StackPanel x:Name="StackPanelBanner" Grid.Row="1"></StackPanel>
I had to write this much messy code to add picture to it.
var toplogoBitmap = new BitmapImage();
toplogoBitmap.BeginInit();
toplogoBitmap.UriSource = new Uri(#"" + _appPath + "images/toplogo.png", UriKind.RelativeOrAbsolute);
toplogoBitmap.EndInit();
var toplogoImage = new Image
{
Source = toplogoBitmap,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Stretch = Stretch.None
};
StackPanelBanner.Children.Add(toplogoImage);
Because i have to get the images from folder, which will be changing on time to time, i can't put them in resources.
Is there any short way of adding picture in XAML control. i.e
<StackPanel source="toplogo" x:Name="StackPanelBanner" Grid.Row="1"></StackPanel>
in Codebehind
var toplogo = #"" + _appPath + "images/toplogo.png";
Your "messy" code is pretty much the code you would need to add an item to a content control. You can't really shorten it, but if you're doing this regularly, refactoring to a method could help reduce the extra code required:
public void AddImageToContainer(string path, Panel parent)
{
var bmap = new BitmapImage(new Uri(_appPath + path, UriKind.RelativeOrAbsolute));
var img = new Image
{
Source = bmap,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Stretch = Stretch.None
};
parent.Children.Add(img);
}
You could then just call this as needed, ie:
AddImageToContainer("images/toplogo.png", StackPanelBanner);
You could use an ItemsControl that uses a StackPanel as its ItemsPanel.
<ItemsControl x:Name="itemsControl" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You might now add images as easy as shown below, because WPF provides built-in type conversion from string (or Uri) to ImageSource:
itemsControl.Items.Add(_appPath + "images/toplogo.png");