How can I clip/shape a ProgressBar indicator using a property in the ViewModel?
I want to add a feedback indicator to a Slider control using a ProgressBar, where I clip so that only a line of circles are shown. And since the View is resizeable and the circle diameter is fixed, I want to bind the clipped shape to a property indicating how many circles that are to be shown.
I tried using VisualBrush, where I tiled Elipse shapes. But it ended with the individual circles being cut, which didn't look any good.
The circle line here is what I want to achieve:
<ProgressBar x:Name="PART_FeedbackBar" Grid.Column="1" Grid.Row="1"
Value="{Binding Path=Value}"
Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Slider}}, Path=Maximum}"
Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Slider}}, Path=Minimum}"
Orientation="Horizontal"
BorderThickness="0"
Background="{StaticResource PrimaryColor}"
Foreground="{StaticResource HighlightColor}"
Height="20">
<ProgressBar.Clip>
<!-- What to add here? -->
</ProgressBar.Clip>
</ProgressBar>
Well, I found a solution using ProgressBar.OpacityMask rather than ProgressBar.Clip, since ProgressBar.OpacityMask supports VisualBrush. This way I was able to tile an Elipse, which the ProgressBar Opacity is set to 0 around.
To get the circle count to adjust to the ProgressBar width, I added a SizeChanged event handler which calculates the number of Elipse shapes. The VisualBrush Viewport property is bound to the Elipse count property, using a Converter to convert the int value to a Rect.
Result:
XAML:
<ProgressBar x:Name="PART_FeedbackBar" Grid.Column="1" Grid.Row="1"
SizeChanged="PART_FeedbackBar_SizeChanged"
Value="{Binding Path=Value}"
Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Slider}}, Path=Maximum}"
Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Slider}}, Path=Minimum}"
Orientation="Horizontal"
BorderThickness="0"
Foreground="{StaticResource HighlightColor}"
Background="{StaticResource PrimaryColor}"
Height="15"
Margin="25,10">
<ProgressBar.OpacityMask>
<VisualBrush x:Name="FeedbackBarOpacityMask" TileMode="Tile"
Viewport="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}}, Path=FeedbackDotCount,
Converter={converters:IntegerToViewportConverter}}"
Stretch="Uniform">
<VisualBrush.Visual>
<Ellipse Height="1" Width="1" Fill="Black" />
</VisualBrush.Visual>
</VisualBrush>
</ProgressBar.OpacityMask>
</ProgressBar>
SizeChanged event handler:
private void PART_FeedbackBar_SizeChanged(object sender, SizeChangedEventArgs e)
{
FeedbackDotCount = (int)Math.Ceiling(e.NewSize.Width / 25);
}
Converter:
public class IntegerToViewportConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var count = (int)value;
var viewport = new Rect(0, 0, 0, 1);
if (count > 0)
{
viewport.Width = 1 / (double)count;
}
return viewport;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Related
Goal: To align combobox popup to bottom Left. Kindly check image below for reference:
What i tried?: I tried playing with Placement property of PART_Popup. If i set Placement="Bottom", the Popup is rendering from Bottom right. Check this image below:
Also, in VS Designer it is showing correctly:
ComboBox PART_Popup:
<Popup x:Name="PART_Popup"
AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom"
MinWidth="170">
<themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{StaticResource ComboBox.Static.Background}"
CornerRadius="3">
<ScrollViewer x:Name="DropDownScrollViewer" CanContentScroll="False">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}"
Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="16"/>
</Grid>
</ScrollViewer>
</Border>
</themes:SystemDropShadowChrome>
</Popup>
XAML:
<ComboBox
HorizontalAlignment="Left"
VerticalAlignment="Center"
SelectedIndex="0"
Margin="50,0"
Style="{StaticResource FlatComboBoxStyle2}">
<ComboBoxItem Content="Recent"/>
<ComboBoxItem Content="Alphabetical"/>
</ComboBox>
I am looking for XAML code solution only. Thanks.
I fixed it by adding a converter in App.xaml.cs:
public class PositiveToNegativeConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (double) value > 0 ? 0 - (double) value : 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And define it in my ComboBoxTemplate:
<PositiveToNegativeConverter x:Key="PositiveToNegativeConverter" />
And call it like this in Popup's HorizontalOffest:
HorizontalOffset="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource PositiveToNegativeConverter}}"
I hope this helps out anyone looking for solution.
I had the same issue, and after following several thread here (all my XAML trial failed, to brief up, Popup.Placement = [whatever] didn't change anything on run) I ended up with the following "solution".
Not pretty, so if you knew more elegant way I'm open.
So I named my ComboBox and Add a event handler (via XAML) on DropDownOpen (this is the dirty side imo) where I force my Popup.Placement to RelativePoint and set a VerticalOffset to my ComboBox.ActualHeight.
Here is the XAML code :
<ComboBox Name="NIC_CmbBox" HorizontalAlignment="Stretch" DropDownOpened="NIC_CmbBox_DropDownOpened">
C# :
private Popup _comboBoxPopup;
private void NIC_CmbBox_DropDownOpened(object sender, EventArgs e)
{
ComboPopupPlacement();
}
public Popup ComboBoxPopup {
get
{
if(_comboBoxPopup == null)
{
//try to get it
if(NIC_CmbBox != null)
_comboBoxPopup = (Popup)NIC_CmbBox.Template.FindName("PART_Popup", NIC_CmbBox);
}
return _comboBoxPopup;
}
set => _comboBoxPopup = value; }
public void ComboPopupPlacement()
{
//If placement haven't already be done
if (ComboBoxPopup != null && _comboBoxPopup.Placement != PlacementMode.RelativePoint)
{
_comboBoxPopup.Placement = PlacementMode.RelativePoint;
_comboBoxPopup.VerticalOffset = NIC_CmbBox.ActualHeight;
}
}
So I'm trying to pass my Rectangle as a CommandParameter because I wanna get it's X & Y properties because the goal is to move it when I press W.
How do I properly pass it as a CommandParameter?
<Window.InputBindings>
<KeyBinding Key="W" Command="{Binding Forward}" CommandParameter="{ Binding RelativeSource= { RelativeSource Mode=FindAncestor,
AncestorType={x:Type Rectangle}}}"/>
</Window.InputBindings>
<Window.DataContext>
<viewmodel:BaseViewModel />
</Window.DataContext>
<Grid>
<Grid x:Name="PlayerArea">
<Border Width="25" HorizontalAlignment="Left" Background="Green"/>
<Border Width="25" HorizontalAlignment="Right" Background="Green"/>
<Border Height="25" VerticalAlignment="Top" Background="Green">
<Button Content="Connect" Width="100" Command="{Binding ConnectCommand}"/>
</Border>
<Border Height="25" VerticalAlignment="Bottom" Background="Green"/>
<Rectangle Width="50"
Height="50"
Fill="Red"
x:Name="localPlayer"/>
</Grid>
</Grid>
In this scenario maybe you want to use a Canvas instead of a Grid.
Defines an area within which you can explicitly position child elements by using coordinates that are relative to the Canvas area.
You can bind the Rectangle as command parameter, but then you pass a view control to your view model, which should be avoided to maintain a clean separation of concerns. However it would not help here, because Rectangle itself does not have coordinates.
Nevertheless, you can create properties for the X and Y coordinates in your view model. Do not forget to implement INotifyPropertyChanged, otherwise these properties will not be updated in the user interface.
private double _x;
private double _y;
public double X
{
get => _x;
set
{
if (_x == value)
return;
_x = value;
OnPropertyChanged(nameof(X));
}
}
public double Y
{
get => _y;
set
{
if (_y == value)
return;
_y = value;
OnPropertyChanged(nameof(Y));
}
}
As your rectangle has a certain size, you have to compensate it to get the correct coordinates. You can do that in the view model, too, but I create a multi value converter as an example here, that is only used in XAML bindings.
public class SizeCompensatingCoordinateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (double)value - System.Convert.ToDouble(parameter) / 2;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (double)value - System.Convert.ToDouble(parameter) / 2;
}
}
I used your example and adapted it for a Canvas using the converter to set the coordinates via bindings to the X and Y properties in the view model.
<Canvas x:Name="PlayerArea" Width="200" Height="200">
<Canvas.Resources>
<local:SizeCompensatingCoordinateConverter x:Key="CoordinateWithRespectToSizeConverter"/>
</Canvas.Resources>
<Border Width="25"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=Height}"
HorizontalAlignment="Left"
Background="Green"/>
<Border Canvas.Right="0"
Width="25"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=Height}"
HorizontalAlignment="Right"
Background="Green"/>
<Border Height="25"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=Width}"
Background="Green">
<Button Content="Connect"
Width="100"
Command="{Binding ConnectCommand}"/>
</Border>
<Border Canvas.Bottom="0"
Height="25"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=Width}"
Background="Green"/>
<Rectangle Canvas.Top="{Binding X, Converter={StaticResource CoordinateWithRespectToSizeConverter}, ConverterParameter=50}"
Canvas.Left="{Binding Y, Converter={StaticResource CoordinateWithRespectToSizeConverter}, ConverterParameter=50}"
Width="50"
Height="50"
Fill="Red"
x:Name="localPlayer" />
</Canvas>
This is not a perfect example that might fit all your requirements, but it should provide you the basics of binding coordinates even via a custom converter to view model properties and using coordinates in a Canvas. Now, you can directly read and set the X and Y coordinates in your Forward command.
This question already has answers here:
Add n rectangles to canvas with MVVM in WPF
(2 answers)
Closed 5 years ago.
How do i add Rectangle to my View using MVVM?
This is the code for my view.
<Grid>
<Image x:Name="img" Source="{Binding ImagePath, Source={x:Static vm:DrawingVM.instance}, Converter={StaticResource nullImageConverter}}" Stretch="None" >
</Image>
<ItemsControl ItemsSource="{Binding ListRectangle, Source={x:Static vm:DrawingVM.instance}}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Transparent" x:Name="cnvas" Width="{Binding ElementName=img, Path=ActualWidth}"
Height="{Binding ElementName=img,Path=ActualHeight}"
LayoutTransform="{Binding ElementName=img, Path=LayoutTransform}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<!--<command:EventToCommand CommandParameter="{Binding ElementName=cnvas}" Command="{Binding MouseDownCommand, Source={x:Static vm:DrawingVM.instance}}" PassEventArgsToCommand="True" />-->
<ei:CallMethodAction MethodName="MouseDownEvente" TargetObject="{Binding Source={x:Static vm:DrawingVM.instance}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Stroke="Blue" Fill="Transparent" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and this is my View Model
Canvas canvas = new Canvas();
public void MouseDownEvente(object s, MouseButtonEventArgs e)
{
try
{
if (s == null) return;
canvas = s as Canvas;
if (canvas == null) return;
startPoint = e.GetPosition(canvas);
// Remove the drawn rectanglke if any.
// At a time only one rectangle should be there
//if (rectSelectArea != null)
// canvas.Children.Remove(rectSelectArea);
// Initialize the rectangle.
// Set border color and width
rectSelectArea = new Rectangle
{
Stroke = Brushes.Blue,
StrokeThickness = 2,
Fill = Brushes.Transparent,
};
Canvas.SetLeft(rectSelectArea, startPoint.X);
Canvas.SetTop(rectSelectArea, startPoint.X);
canvas.Children.Add(rectSelectArea);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
throw ex;
}
}
But its throwing an error:
Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel.
So how do i solve this?
I tried searching the same problem with mine. And used the solution that worked to them. But the error still persist. Can someone help me. Thank you.
Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel.
This means that you cannot use Canvas.Children.Add when you use Canvas as an ItemsPanel for an ItemsControl. You should add items on where the ItemsControl.ItemsSource property is bound to (in your case ListRectangle).
I'm trying to make a navigator, that shows a thumbnail of ScrollViewer Content. My application has work area, where I place objects and which I want to show in navigator:
<local:WorkArea x:Name="WorkArea" Grid.Row="1"/>
Work area is a UserControl, which has this structure:
<UserControl>
<ScrollViewer>
<Canvas/>
<ScrollViewer/>
<UserControl/>
My navigator is also a UserControl:
<local:Navigator DataContext="{Binding ElementName=WorkArea, Path=Content}"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="250" Height="250" Margin="0,0,10,10" Grid.Row="1"/>
It's structure:
<UserControl x:Class="DbCreator.Navigator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DbCreator"
mc:Ignorable="d" Opacity="0.9"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<local:WorkAreaToNavigatorConverter x:Key="WorkAreaToNavigatorConverter"/>
</UserControl.Resources>
<Border BorderBrush="#757575" BorderThickness="1"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="{Binding ElementName=ViewBox, Path=ActualWidth}"
Height="{Binding ElementName=ViewBox, Path=ActualHeight}">
<Viewbox x:Name="ViewBox" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=ActualHeight}">
<Grid Background="White">
<Rectangle Width="{Binding Content.ActualWidth}"
Height="{Binding Content.ActualHeight}"
Name="Thumbnail">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Content, Converter={StaticResource WorkAreaToNavigatorConverter}}"/>
</Rectangle.Fill>
</Rectangle>
<Border Background="#20000000" x:Name="ViewPort" Cursor="SizeAll"
Width="{Binding ViewportWidth}" Height="{Binding ViewportHeight}"
MaxWidth="{Binding Content.ActualWidth}" MaxHeight="{Binding Content.ActualHeight}"
HorizontalAlignment="Left" VerticalAlignment="Top"
MouseDown="Border_MouseDown"
MouseUp="Border_MouseUp"
MouseMove="Border_MouseMove">
<Border.RenderTransform>
<TranslateTransform X="{Binding HorizontalOffset}" Y="{Binding VerticalOffset}"/>
</Border.RenderTransform>
</Border>
</Grid>
</Viewbox>
</Border>
So, I bind work area's content (ScrollViewer) to navigator's DataContext. Rectangle inside Viewbox represents thumbnail of work area's Canvas. I fill Rectangle with VisualBrush, which binds to work area's Canvas. Without converter I have this:
I want to convert work area's Canvas to another Canvas, that doesn't have grid lines and contains rectangles instead of tables. So i wrote WorkAreaToNavigatorConverter:
public class WorkAreaToNavigatorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Canvas workAreaCanvas = value as Canvas;
Canvas canvas = new Canvas()
{
Width = workAreaCanvas.ActualWidth,
Height = workAreaCanvas.ActualHeight,
Background = new SolidColorBrush(Colors.White)
};
foreach (Table table in workAreaCanvas.Children.OfType<Table>())
{
Rectangle rectangle = new Rectangle()
{
Width = table.ActualWidth,
Height = table.ActualHeight,
Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString((string)table.Tag))
};
Canvas.SetLeft(rectangle, Canvas.GetLeft(table));
Canvas.SetTop(rectangle, Canvas.GetTop(table));
Canvas.SetZIndex(rectangle, Canvas.GetZIndex(table));
canvas.Children.Add(rectangle);
}
return canvas;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
Convert method is called only once at the beginning of application. Interesting that if I replace return canvas with return value it works like without converter, like it skip code before return.
Why converter doesn't work? What can I do?
I'm trying to create a simple photo album (Windows Store App) using Flip View.
I have the Image element embedded within a ScrollViewer. I'm able to browse through the photos, but I'm looking to do the following things.
The image should fill the height of the screen uniformly [when the image is not zoomed]. I get vertical scrollbars for few items. I dont have this problem when the height of all the images are the same.
When I change the orientation of the screen, a part of the image is clipped on the right side.
The scrollviewer should forget the zoom level (reset zoom factor to 1) when I move between pages.
This is the code I have right now. What Am I doing wrong? And what should I add in my EventHandler to reset my ScrollViewer's zoom factor.
<FlipView
Name="MainFlipView"
Margin="0"
Height="{Binding ActualHeight, ElementName=pageRoot, Mode=OneWay}"
Width="{Binding ActualWidth, ElementName=pageRoot, Mode=OneWay}"
Background="Black">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer Name="myScrollViewer" ZoomMode="Enabled"
Height="{Binding ActualHeight, ElementName=pageRoot, Mode=OneWay}"
Width="{Binding ActualWidth, ElementName=pageRoot, Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
MinZoomFactor="0.5"
MaxZoomFactor="2.5"
Margin="0" >
<Image Source="{Binding Path=Image}"
Name="MainImage" Stretch="Uniform" />
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
What user2199147 said should solve your first bullet point, the other two I had to fix programmatically, though it should be noted that I also had to use the VisualTreeHelper class which you'll have to import, and an extension method to help me use the helper class.
First of all, I had to a method from the VisualTreeHelper extension, which finds the first element in the FlipView that is of any type:
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
if (parentElement != null)
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
return (T)child;
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
{
return result;
}
}
}
}
return null;
}
For going into portrait mode, I added a callback handler for WindowSizeChanged, and simply reset all the ScrollViewers in the flip view back to their default
private void WindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
//Reset scroll view size
int count = MainFlipView.Items.Count;
for(int i = 0; i < count; i++)
{
var flipViewItem = MainFlipView.ItemContainerGenerator.ContainerFromIndex((i));
var scrollViewItem = FindFirstElementInVisualTree<ScrollViewer>(flipViewItem);
if (scrollViewItem is ScrollViewer)
{
ScrollViewer scroll = (ScrollViewer)scrollViewItem;
scroll.Height = e.Size.Height; //Reset width and height to match the new size
scroll.Width = e.Size.Width;
scroll.ZoomToFactor(1.0f);//Zoom to default factor
}
}
}
And then in your constructor you need Window.Current.SizeChanged += WindowSizeChanged; in order for the callback to ever be called.
Now, for setting each ScrollViewer back to their default positions, we do a similar process, only whenever the FlipView selection is changed, we reset the ScrollViewer back to its default zoom factor
private void FlipViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is FlipView)
{
FlipView item = (FlipView)sender;
var flipViewItem = ((FlipView)sender).ItemContainerGenerator.ContainerFromIndex(((FlipView)sender).SelectedIndex);
var scrollViewItem = FindFirstElementInVisualTree<ScrollViewer>(flipViewItem);
if (scrollViewItem is ScrollViewer)
{
ScrollViewer scroll = (ScrollViewer)scrollViewItem;
scroll.ScrollToHorizontalOffset(0);
scroll.ScrollToVerticalOffset(0);
scroll.ZoomToFactor(1.0f);
}
}
}
And again, we have to have a call in the constructor that looks like MainFlipView.SelectionChanged += FlipViewSelectionChanged;
I know these methods seem really hackish and roundabout, because they are, but it's what worked for me, and I hope this helps.
try changing the height and width bindings from the scrollviewer to the image.
<FlipView
Name="MainFlipView"
Margin="0"
Height="{Binding ActualHeight, ElementName=pageRoot, Mode=OneWay}"
Width="{Binding ActualWidth, ElementName=pageRoot, Mode=OneWay}"
Background="Black">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer Name="myScrollViewer" ZoomMode="Enabled"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
MinZoomFactor="0.5"
MaxZoomFactor="2.5"
Margin="0" >
<Image Source="{Binding Path=Image}"
Height="{Binding ActualHeight, ElementName=pageRoot, Mode=OneWay}"
Width="{Binding ActualWidth, ElementName=pageRoot, Mode=OneWay}"
Name="MainImage" Stretch="Uniform" />
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
Good practice for WinRT is:
1) Make Attached Property for ScrollViewer, which will change ZoomFactor
public class ScrollViewerExtension : DependencyObject
{
public static readonly DependencyProperty ScrollViewerZoomFactorProperty = DependencyProperty.RegisterAttached(
"ScrollViewerZoomFactor", typeof(double), typeof(ScrollViewerExtension), new PropertyMetadata(default(double), OnZoomFactorChanged));
public static void SetScrollViewerZoomFactor(DependencyObject element, double value)
{
element.SetValue(ScrollViewerZoomFactorProperty, value);
}
public static double GetScrollViewerZoomFactor(DependencyObject element)
{
return (double)element.GetValue(ScrollViewerZoomFactorProperty);
}
private static void OnZoomFactorChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs args)
{
if (depObject is ScrollViewer)
{
var scrollViewer = (ScrollViewer)depObject;
var zoomValue = (double)args.NewValue;
if (!Double.IsNaN(zoomValue))
scrollViewer.ZoomToFactor((float)zoomValue);
}
else
{
throw new Exception("ARE YOU KIDDING ME ? ITS NOT SCROLLVIEWER");
}
}
}
2) Changer FlipViewItem Template
<Style TargetType="FlipViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Width="1040">
<ScrollViewer HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto"
MaxZoomFactor="4"
MinZoomFactor="1"
Tag="{Binding IsSelected}"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto"
ZoomMode="Enabled"
extension:ScrollViewerExtension.ScrollViewerZoomFactor="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Converter={StaticResource IsSelectedToZoom}}">
<ContentPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
3) Create converter, which will change ScrollViewerZoomFactor to default value if item isnt selected.
public class IsSelectedToZoomConverter :DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var val = (bool) value;
return val ? Double.NaN : 1.0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
4) FlipView code will look like this:
<FlipView x:Name="FlipView"
Grid.Row="5"
Width="1040"
MinHeight="392"
MaxHeight="600"
ItemsSource="{Binding Path=CurrentSession.Photos}"
Visibility="{Binding CurrentSession.HasContent,
Converter={StaticResource BoolToVisibility}}">
<FlipView.ItemContainerStyle>
<Style TargetType="FlipViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Width="1040">
<ScrollViewer HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto"
MaxZoomFactor="4"
MinZoomFactor="1"
Tag="{Binding IsSelected}"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto"
ZoomMode="Enabled"
extension:ScrollViewerExtension.ScrollViewerZoomFactor="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=IsSelected,
Converter={StaticResource IsSelectedToZoom}}">
<ContentPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</FlipView.ItemContainerStyle>
<FlipView.ItemTemplate>
<DataTemplate>
<Image HorizontalAlignment="Stretch" Source="{Binding Path=Path}" />
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
why do you have a ScrollViewer within you FlipViewItemTemplate?
Thie template will be used for each Item, so for each Image you add to your ItemList.
that said it shoud be enough to have the Image element within your Template.
this should at least avoid the scrollbars for images that are bigger than your screen, cause then the Stretch="Uniform" should handle the resizing...