Update ScrollView when image is resized (WPF) - wpf

I'm doing a graphic editor for a GBA game and I got an image into a ScrollView. When the user click on a NumericUpDown, the image is resized with a magnify factor. I would like my ScrollView to adapt itself to the new size of the image to be able to scroll the entire image. I'm not using the MVVM patter.
Here is portion of my XAML:
<ScrollViewer x:Name="PortraitScrollBar" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<Image x:Name="ImagePortrait" Width="280" Height="280" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top" />
</ScrollViewer>
Here also is my function that is called on the ValueChanged event of the NumericUpDown:
public static void Magnify(ref Image img, WriteableBitmap wBmp, int factor)
{
img.Source = wBmp.Resize(wBmp.PixelWidth * factor, wBmp.PixelHeight * factor, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
}
private void PorMagnifyUpDown_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (IsInit)
{
Magnify(ref ImagePortrait, currentImage, (int)e.NewValue);
}
}

Related

Mouse click on WPF (Time)Slider has no effect when slider thumb is moving bound to a timer

I use MediaElement and (Time)Slider to play and control playing of a video.
I used answer 2 to this question as a base.
In addition to the dragging capability I would also like the slider thumb to be moved to a mouse click point.
This works ok when the MediaElement and (Time)Slider are paused, but when the video is playing a mouse click has no effect
Here is my code
XAML:
<MediaElement Source="..."
Name="mediaView"
Height="450" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="UniformToFill"
MediaOpened="OnMediaOpened" MediaEnded="OnMediaEnded" MediaFailed="OnMediaFailed" Grid.Row="0" Grid.Column="0"/>
<Grid Name="mediaBar" VerticalAlignment="Bottom" Margin="5,10,5,0" Background="#B2282828" Grid.Row="0" Grid.Column="0">
<!-- ... -->
<Slider Name="timeSlider" Margin="5,5,5,0"
Thumb.DragStarted="OnDragStarted" Thumb.DragCompleted="OnDragCompleted" ValueChanged="OnTimeSliderValueChanged"
PreviewMouseLeftButtonUp="OnMouseLeftButtonUp" IsMoveToPointEnabled="True"
MinWidth="200" FlowDirection="LeftToRight"
Grid.Column="4" Cursor="ScrollWE" VerticalAlignment="Center"/>
<!-- ... -->
</Grid>
relevant c# part:
private void OnDragStarted(object sender, DragStartedEventArgs args)
{
isDragging = true;
ticks.Stop();
}
private void OnDragCompleted(object sender, DragCompletedEventArgs args)
{
isDragging = false;
int SliderValue = (int)timeSlider.Value;
TimeSpan ts = new TimeSpan(0, 0, 0, 0, SliderValue);
mediaView.Position = ts;
if(currentStatus == Status.PLAYING)
ticks.Start();
}
private void OnMouseLeftButtonUp(object sender, EventArgs ea)
{
if(!isDragging)
{
mediaView.Pause();
ticks.Stop();
int SliderValue = (int)timeSlider.Value; // when video is playing this not the point of the mouse click
// ...
}
}
I can understand that timeSlider.Value delivers the current point in time instead of the mouse click position when the video is playing.
Is there another way to measure the position of the mouse click and update the slider value with that ?
Or a better solution for the Mouse-click-while-slider-is-running-situation ?
Try this:
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs ea)
{
mediaView.Position = TimeSpan.FromSeconds((int)(mediaView.NaturalDuration.TimeSpan.TotalSeconds * (double)(ea.GetPosition(timeSlider).X / this.Width)));
}

How to change collection images in silverlight?

My Design Code like this:
<Grid HorizontalAlignment="Left" Height="42" VerticalAlignment="Top" Width="302" Margin="12,471,0,0" Background="{StaticResource AppBarItemForegroundThemeBrush}">
<TextBlock HorizontalAlignment="Left" Margin="6,6,0,0" TextWrapping="Wrap" Text="Change Color" VerticalAlignment="Top" Height="26" Width="137" FontSize="18" Foreground="Black" />
<Image HorizontalAlignment="Left" Height="33" Margin="163,3,0,0" VerticalAlignment="Top" Width="41" Source="Assets/c1-1.png" x:Name="c1" Tapped="c1_Tapped" />
<Image HorizontalAlignment="Left" Height="32" Margin="212,4,0,0" VerticalAlignment="Top" Width="45" Source="Assets/c3-1.png" x:Name="c2" Tapped="c2_Tapped" RenderTransformOrigin="0.825,0.5" />
<Image HorizontalAlignment="Left" Height="33" Margin="262,3,0,0" VerticalAlignment="Top" Width="40" Source="Assets/c2-1.png" x:Name="c3" Tapped="c3_Tapped" />
</Grid>
Code Behind code like this:
private void c1_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/02_perspective_img_1.png");
Images.Add(#"Assets/02_perspective_img_2.png");
Images.Add(#"Assets/02_perspective_img_3.png");
this.DataContext = this;
}
private void c2_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/03_perspective_img_1.png");
Images.Add(#"Assets/03_perspective_img_2.png");
Images.Add(#"Assets/03_perspective_img_3.png");
this.DataContext = this;
}
private void c3_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/01_perspective_img_1.png");
Images.Add(#"Assets/01_perspective_img_2.png");
Images.Add(#"Assets/01_perspective_img_3.png");
this.DataContext = this;
}
When tapped on particular image need to show that particular images.But not showing that .
only showing first clicked items images only.Please let me know how to change the collection.
i am binding that collection to flipview control in windows 8.
<FlipView.ItemTemplate>
<DataTemplate>
<Image HorizontalAlignment="Left" Source="{Binding}" Height="450" VerticalAlignment="Top" Width="792" x:Name="imagecontrol" Stretch="Fill"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I'm going under a few assumptions. One is that the FlipView control has it's ItemsSource binding to your Images property. If you are going to set the DataContext to yourself (the page in question) you need to do one of a few options.
One: Do not set Images to a new collection. You are using an ObservableCollection so take advantage of it. Clear the collection and add items back to it.
private void c3_Tapped(object sender, TappedRoutedEventArgs e)
{
Images.Clear();
Images.Add(#"Assets/01_perspective_img_1.png");
Images.Add(#"Assets/01_perspective_img_2.png");
Images.Add(#"Assets/01_perspective_img_3.png");
}
Two: Implement INotifyPropertyChanged in the page and fire the PropertyChanged event when you reset the Images property
private ICollection<string> _images;
public ICollection<string> Images
{
get { return _images; }
set
{
_images = value;
OnPropertyChanged("Images");
}
}
You will probably find that you will need more and more bindings. Because of this it is usually best to have a separate ViewModel class that holds your data.
the way Shawn is suggested is i will recommend but when i tested it in case of Windows 8 app page which is inherited by LayoutAwarePage then in that case - Firstly you got error that Page Can not Implement InotifypropertyChanged secondly when i Tested it With ObsevableCollection that time also it is not working..so i have come with this workaround for solving your problem. in this case you just have to update the itemsSource Property of your FlipView Control each time when you are updating your Image collection..
private void c1_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/02_perspective_img_1.png");
Images.Add(#"Assets/02_perspective_img_2.png");
Images.Add(#"Assets/02_perspective_img_3.png");
FlipviewControlName.ItemsSource = Images;
}
I know this is not the proper solution but i think i will solve your problem...

Canvas overlay image outside

I have a image inside a canvas. When a UserControl loaded, image move up.
<Canvas x:Name="cnvMain" Width="300" VerticalAlignment="Center" Height="200" SnapsToDevicePixels="True">
<Image x:Name="Image1" Width="200" Stretch="None" Canvas.Bottom="0" Source="ImageGallery/Desert.jpg" ></Image>
</Canvas>
I used DoubleAnimation.
DoubleAnimation _Animation;
private Storyboard _StoryBoard;
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
_Animation = new DoubleAnimation();
_Animation.From = -Image1.ActualHeight;
_Animation.To = cnvMain.ActualHeight;
_Animation.RepeatBehavior = RepeatBehavior.Forever;
_Animation.Duration = new Duration(TimeSpan.Parse("0:0:10"));
_Animation.FillBehavior = FillBehavior.Stop;
Storyboard.SetTarget(_Animation, Image1);
Storyboard.SetTargetProperty(_Animation, new PropertyPath(Canvas.BottomProperty));
_StoryBoard = new Storyboard();
_StoryBoard.Children.Add(_Animation);
_StoryBoard.Begin();
}
This code work well. My problem is the canvas did not overlay around of image like a frame (Image size is bigger of canvas and I want area of image inside canvas viewed). When I change Canvas to Grid it overlay outside of image but the animation did not work.
Try and use ClipToBounds="True" on your Canvas:

Dynamically change the width of a WPF window when content gets visible

I want to be able to hide the left hand side of the screen and right hand side of the screen at the beginning of the program.
Then when the user presses create new button the left hand side of the screen becomes available so they can create the new item. Then when they press save it comes back to the middle datagrid only.
Then I want to add an event when the double click on the datagrid row (data is programmed in to the datagrid in the code) the right hand side of the screen becomes visible then when the button allocate is pressed the right hand side disappears again just leaving the datagrid.
I am fairly new to WPF so unsure whether this can be done or not. I am trying to do it in the same window at the moment I am making prototypes for my company and already have some that use separate windows. I had posted an image but am un able to post it as I am a new user.
To hide and show controls I would either recommend using expanders (as your comment says you've done) or Grids and setting their visibility as needed. If you want your side panels to appear over the datagrid then displaying them is just a matter of changing their visibility. If don't want obscure the DataGrid then you will need to change the visibility of the panels as well as the size of the window.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<!-- DataGrid display -->
<Grid>
<StackPanel>
<Button Content="Add New" Click="OnAddNewButtonClick" Width="100"/>
<DataGrid ItemsSource="{Binding GridItems}" IsReadOnly="True" Name="dataGrid">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseDoubleClick" Handler="OnRowDoubleClick"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</StackPanel>
</Grid>
<!-- Left column pops up over DataGrid -->
<Grid Name="LeftColumn" Visibility="Collapsed" Background="Red" Width="200" HorizontalAlignment="Left">
<StackPanel VerticalAlignment="Center">
<Button Content="Hide Column" Click="OnLeftColumnButtonClick"/>
</StackPanel>
</Grid>
<!-- Right Column expands screen size-->
<Grid Visibility="Collapsed" Name="RightColumn" Width="200" HorizontalAlignment="Right">
<StackPanel Background="Green" >
<TextBlock Text="Hidden Column"/>
<Button Content="Hide Panel" Click="OnRightColumnButtonClick"/>
</StackPanel>
</Grid>
</Grid>
</Window
C# - I know you're working in VB, but this was quicker for me. The code should be fairly self explanatory, but if you need a VB sample let me know:
public partial class MainWindow : Window
{
public ObservableCollection<Person> GridItems { get; set; }
private const double CollapsedWidth = 500;
private const double ExpandedWidth = 700;
public MainWindow()
{
DataContext = this;
GridItems = new ObservableCollection<Person>();
GridItems.Add(new Person { Name = "Foo", Age = 1 });
GridItems.Add(new Person { Name = "Bar", Age = 2 });
InitializeComponent();
Width = CollapsedWidth;
}
private void OnAddNewButtonClick(object sender, RoutedEventArgs e)
{
LeftColumn.Visibility = Visibility.Visible;
}
private void OnLeftColumnButtonClick(object sender, RoutedEventArgs e)
{
LeftColumn.Visibility = Visibility.Collapsed;
}
private void OnRowDoubleClick(object sender, MouseButtonEventArgs e)
{
Width = ExpandedWidth;
RightColumn.Visibility = Visibility.Visible;
}
private void OnRightColumnButtonClick(object sender, RoutedEventArgs e)
{
RightColumn.Visibility = Visibility.Collapsed;
Width = CollapsedWidth;
}
}

Tooltip on scrollviewer in documentviewer

I have a documentviewer which i used in my wpf project to show xps document reports of having around 600 pages which is working great. But from user point of view i like to show the current page number as a tooltip on my scrollviewer while dragging the scroll stating the current page number in view. Somewhat like in a PDF file like this -
I was looking out for some ideas how to implement this. Just a current page number if not possible to show a thumbnail image would be good enough for me.
Is there any in-built support in documentviewer for this functionality??
Thanks for any help..
I cannot find anything like IsScrolling so i would approach it like this:
<Popup Name="docPopup" AllowsTransparency="True" PlacementTarget="{x:Reference docViewer}" Placement="Center">
<Border Background="Black" CornerRadius="5" Padding="10" BorderBrush="White" BorderThickness="1">
<TextBlock Foreground="White">
<Run Text="{Binding ElementName=docViewer, Path=MasterPageNumber, Mode=OneWay}"/>
<Run Text=" / "/>
<Run Text="{Binding ElementName=docViewer, Path=PageCount, Mode=OneWay}"/>
</TextBlock>
</Border>
</Popup>
<DocumentViewer Name="docViewer" ScrollViewer.ScrollChanged="docViewer_ScrollChanged"/>
The popup should be displayed when the document is scrolled, then it should fade out after some time. This is done in the handler:
DoubleAnimationUsingKeyFrames anim;
private void docViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (anim == null)
{
anim = new DoubleAnimationUsingKeyFrames();
anim.Duration = (Duration)TimeSpan.FromSeconds(1);
anim.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0))));
anim.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.5))));
anim.KeyFrames.Add(new LinearDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1))));
}
anim.Completed -= anim_Completed;
docPopup.Child.BeginAnimation(UIElement.OpacityProperty, null);
docPopup.Child.Opacity = 1;
docPopup.IsOpen = true;
anim.Completed += anim_Completed;
docPopup.Child.BeginAnimation(UIElement.OpacityProperty, anim);
}
void anim_Completed(object sender, EventArgs e)
{
docPopup.IsOpen = false;
}
Edit: The event fires also on scrolls done via mouse-wheel etc. you could wrap everything in the handler in if (Mouse.LeftButton == MouseButtonState.Pressed), not 100% accurate but who scrolls with the MouseWheel while left-clicking?

Resources