I have an Image control in a Grid that is displayed when the user clicks on an image in a list. I want to add an InkCanvas control directly on top of the Image control so the user can draw on it.
However, it seems like the height and width of the InkCanvas is not being bound correctly to the image and I am able to draw outside the image. What else do I need to do?
My XAML code:
<Grid>
<Image x:Name="result_img" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<InkCanvas x:Name="inkCanvas" x:Load="False" Height="{x:Bind result_img.ActualHeight}" Width="{x:Bind result_img.ActualWidth}"/>
</Grid>
Code-behind (C++/CX):
void MyGui::test::ListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
{
this->FindName("inkCanvas");
inkCanvas->InkPresenter->InputDeviceTypes = CoreInputDeviceTypes::Mouse;
}
Any help would be much appreciated!
If you want to use element-to-element binding,you should use the ElementName property.The ElementName is the name of the control you want to bind and the Path is the property of the control you want to bind.
<Image x:Name="result_img" VerticalAlignment="Center" HorizontalAlignment="Center" Width="300" Height="400" />
<InkCanvas x:Name="inkCanvas" x:Load="False" Height="{Binding ElementName=result_img, Path=ActualHeight}" Width="{Binding ElementName=result_img, Path=ActualWidth}"/>
I want to show a collection of images with entry animations (opacity, translation and rescaling) in a UWP app.
How do I apply unique storyboards to each of these images within the ItemsControl.ItemTemplate DataTemplate so that each image has unique translations and sizes? While the animations are the same, their parameters do change from image to image.
Have been looking for several days on something that works reliably, and all I have found so far is stuff that will not work in UWP apps.
How do I apply unique storyboards to each of these images
Firstly, since you want each of these images has a different animation, I suggest to have a property for the image instance to indicate which animation the image will play.
Then secondly, we need to resolve the issue that with different property value to trigger different Storyboard. For this we could consider to use DataTriggerBehavior of XamlBehaviors package. Install the XamlBehaviors Nuget package through this link.
At last binding the corresponding Storyboard to the trigger it will work.
The following is a completed working demo you could have a test.
XAML
...
xmlns:Interactions="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
...
<ItemsControl x:Name="itemcontrol">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<Storyboard x:Name="StoryBoardOpacity">
<DoubleAnimation
Storyboard.TargetName="img"
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:5" />
</Storyboard>
<Storyboard x:Name="StoryboardScale">
<DoubleAnimation
Storyboard.TargetName="transform"
Storyboard.TargetProperty="ScaleX"
From="1.0"
To="2.0"
Duration="0:0:5" />
</Storyboard>
</Grid.Resources>
<Image
x:Name="img"
Width="100"
Height="100"
Source="{Binding imageurl}">
<Interactivity:Interaction.Behaviors>
<Interactions:DataTriggerBehavior
Binding="{Binding storyboard}"
ComparisonCondition="Equal"
Value="Opacity">
<Media:ControlStoryboardAction Storyboard="{StaticResource StoryBoardOpacity}" />
</Interactions:DataTriggerBehavior>
<Interactions:DataTriggerBehavior
Binding="{Binding storyboard}"
ComparisonCondition="Equal"
Value="Scale">
<Media:ControlStoryboardAction Storyboard="{StaticResource StoryboardScale}" />
</Interactions:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Image.RenderTransform>
<CompositeTransform x:Name="transform" />
</Image.RenderTransform>
</Image>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Code behind
private ObservableCollection<OneImage> imageSources;
public MainPage()
{
this.InitializeComponent();
imageSources = new ObservableCollection<OneImage>()
{
new OneImage()
{
imageurl="ms-appx:///Assets/caffe1.jpg",
storyboard="Opacity"
},
new OneImage()
{
imageurl="ms-appx:///Assets/caffe2.jpg",
storyboard="Opacity"
},
new OneImage()
{
imageurl="ms-appx:///Assets/caffe3.jpg",
storyboard="Scale"
}
};
itemcontrol.ItemsSource = imageSources;
}
public class OneImage
{
public string imageurl { get; set; }
public string storyboard { get; set; }
}
I want to have a logout icon in the appbar.
I was sure it should be in the common styles, but discovered that unfortunately it's not.
So, how can I do it?
I've found this site, where I found the icon I wanted to the logout button, and copied the xaml which gave me the path. Next I added this code to the common:
<Style x:Key="LogoutAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="LogoutAppBarButton" />
<Setter Property="AutomationProperties.Name" Value="Logout" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Viewbox RenderTransformOrigin="0.47,0.47">
<Viewbox.RenderTransform>
<TransformGroup>
<CompositeTransform Rotation="0" ScaleX="0.551720260135184" ScaleY="0.551720260135184" />
</TransformGroup>
</Viewbox.RenderTransform>
<Path Stretch="Uniform"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Data="F1 M 0,71.4297C -0.0207825,54.2669 7.09511,41.2825 13.974,33.0403C 20.8893,24.7292 27.6055,20.7252 28.2083,20.3522C 32.6992,17.6946 38.4714,19.2199 41.0976,23.7583C 43.7188,28.2812 42.2317,34.0878 37.7787,36.7583L 37.7604,36.7707L 37.7044,36.8053L 37.2148,37.1308L 35.1185,38.6797C 33.3203,40.1106 30.849,42.3333 28.414,45.2734C 23.5221,51.2292 18.8593,59.6705 18.8385,71.427C 18.8424,83.2799 23.5678,93.9374 31.2579,101.724C 38.9648,109.497 49.5065,114.279 61.2304,114.281C 72.9544,114.279 83.4961,109.497 91.2019,101.724C 98.8932,93.9374 103.619,83.2799 103.622,71.427C 103.602,60.0305 99.2304,51.7707 94.5065,45.8346C 90.0091,40.207 85.1979,37.0722 84.7188,36.7799L 84.7031,36.7721L 84.6888,36.7642L 84.6784,36.7597C 80.2304,34.0865 78.7435,28.2799 81.362,23.7571C 83.9909,19.2187 89.7618,17.6933 94.2514,20.3509C 94.8568,20.7226 101.573,24.7265 108.488,33.0384C 115.371,41.2825 122.483,54.2655 122.464,71.427C 122.457,105.611 95.0443,133.322 61.2317,133.333C 27.4205,133.322 0.00785828,105.611 0,71.4297 Z M 51.8125,66.668L 51.8125,38.0944L 51.8125,9.52411C 51.8125,4.26556 56.03,-3.05176e-005 61.233,-3.05176e-005C 66.4323,-3.05176e-005 70.6497,4.26556 70.6497,9.52411L 70.6497,38.0944L 70.6497,66.668L 70.6524,66.668C 70.6524,71.9218 66.4323,76.1901 61.233,76.1901C 56.03,76.1901 51.8125,71.9218 51.8125,66.668 Z " />
</Viewbox>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
It is important to pay attention to the Path's Fill property, it can't be white color, because when we press on it and hold it, the default appbar icons become white, so the color should be changed to black, and therefore, we use this code which get's the foreground color from the parent - AppBarButtonStyle, which already has this logic on appbar icon pressed. This is the code:
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
One more thing, I have used some renders because the image was a little bit crooked, so I have fixed it by rendering it, maybe not the best way, but now it works perfectly !
The last step was to add this to all the pages I wanted to use the login appbar icon:
<Button Style="{StaticResource LogoutAppBarButtonStyle}" Click="Logout_Click" />
Both VariableSizedWrapGrid and WrapGrid have strange measuring - they measure all children based on the first item.
Because of that, the following XAML will clip the third item.
<VariableSizedWrapGrid Orientation="Horizontal">
<Rectangle Width="50" Height="100" Margin="5" Fill="Blue" />
<Rectangle Width="50" Height="50" Margin="5" Fill="Red" />
<Rectangle Width="50" Height="150" Margin="5" Fill="Green" />
<Rectangle Width="50" Height="50" Margin="5" Fill="Red" />
<Rectangle Width="50" Height="100" Margin="5" Fill="Red" />
</VariableSizedWrapGrid>
Seems like VariableSizedWrapGrid measures the first item and then the rest children are measured with desired size of the first one.
Any workarounds?
You need to use the Attached Properties on each Rectangle VariableSizeWrapGrid.ColumnSpan and VariableSizeWrapGrid.RowSpan as well as add an ItemHeight and ItemWidth to the VariableSizeWrapGrid:
<VariableSizedWrapGrid Orientation="Horizontal" ItemHeight="50" ItemWidth="50">
<Rectangle
VariableSizedWrapGrid.ColumnSpan="1"
VariableSizedWrapGrid.RowSpan="2"
Width="50" Height="100" Margin="5" Fill="Blue" />
</VariableSizedWrapGrid>
Its may be not the best way but this is how I have done this in my #MetroRSSReader app
<common:VariableGridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="225"
ItemHeight="{Binding ElementName=bounds, Path=Text}"
MaximumRowsOrColumns="5" Orientation="Vertical"
/>
</ItemsPanelTemplate>
</common:VariableGridView.ItemsPanel>
</common:VariableGridView>
Notice the ItemHeight value is bound to a TextBlock
<TextBlock x:Name="bounds" Grid.Row="1" Margin="316,8,0,33" Visibility="Collapsed"/>
Which is set in the LayoutAwarePage.cs
public string Fix_item_height_for_current_screen_resolution()
{
var screenheight = CoreWindow.GetForCurrentThread().Bounds.Height;
var itemHeight = screenheight < 1000 ? "100" : "140";
return itemHeight;
}
You can browse the full source code http://metrorssreader.codeplex.com/SourceControl/changeset/view/18233#265970
To use a VariableSizeWrapGrid you should create your own GridView custom control and override PrepareContainerForItemOverride and set the elements RowSpan and ColumnSpan inside that method. That way each element will have its own height/width.
Here is a nice tutorial/walk through by Jerry Nixon : http://dotnet.dzone.com/articles/windows-8-beauty-tip-using
Managed to figure this one out today. You'll need to make use of VisualTreeHelperExtension.cs in the WinRT XAML Toolkit (http://winrtxamltoolkit.codeplex.com). For me I was trying to adjust a ListView that had a GridView as its ItemsPanelTemplate, the same concept should apply for you.
1) Attach to the LayoutUpdated event of your ListView (this is when you'll want to update the sizes)
_myList.LayoutUpdated += _myList_LayoutUpdated;
2) Use VisualTreeHelperExtensions.GetDescendantsOfType() to find a common (and unique) element type in your item's data template (ex: a TextBlock that is dynamic in width):
var items = VisualTreeHelperExtensions.GetDescendantsOfType<TextBlock>(_myList);
if (items == null || items.Count() == 0)
return;
3) Get the max width of the items found:
double maxWidth = items.Max(i => i.ActualWidth) + 8;
4) Use VisualTreeHelperExtensions.GetDescendantsOfType() to find the main WrapGrid container for your ListView:
var wg = _categoryList.GetDescendantsOfType<WrapGrid>();
if (wg == null || wg.Count() != 1)
throw new InvalidOperationException("Couldn't find main ListView container");
5) Set the WrapGrid's ItemWidth to the maxWidth you calculated:
wg.First().ItemWidth = maxWidth;
I'm trying to create a similar experience as in the ScrollViewerSample from the Windows 8 SDK samples to be able to snap to the items inside a ScrollViewer when scrolling left and right. The implementation from the sample (which works) is like this:
<ScrollViewer x:Name="scrollViewer" Width="480" Height="270"
HorizontalAlignment="Left" VerticalAlignment="Top"
VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto"
ZoomMode="Disabled" HorizontalSnapPointsType="Mandatory">
<StackPanel Orientation="Horizontal">
<Image Width="480" Height="270" AutomationProperties.Name="Image of a cliff" Source="images/cliff.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of Grapes" Source="images/grapes.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of Mount Rainier" Source="images/Rainier.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of a sunset" Source="images/sunset.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of a valley" Source="images/valley.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
</ScrollViewer>
The only difference with my desired implementation is that I don't want a StackPanel with items inside, but something I can bind to. I am trying to accomplish this with an ItemsControl, but for some reason the Snap behavior does not kick in:
<ScrollViewer x:Name="scrollViewer" Width="480" Height="270"
HorizontalAlignment="Left" VerticalAlignment="Top"
VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto"
ZoomMode="Disabled" HorizontalSnapPointsType="Mandatory">
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<Image Width="480" Height="270" AutomationProperties.Name="Image of a cliff" Source="images/cliff.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of Grapes" Source="images/grapes.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of Mount Rainier" Source="images/Rainier.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of a sunset" Source="images/sunset.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Width="480" Height="270" AutomationProperties.Name="Image of a valley" Source="images/valley.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</ItemsControl>
</ScrollViewer>
Suggestions would be greatly appreciated!
Thanks to Denis, I ended up using the following Style on the ItemsControl and removed the ScrollViewer and inline ItemsPanelTemplate altogether:
<Style x:Key="ItemsControlStyle" TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}" HorizontalSnapPointsType="Mandatory">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Getting snap points to work for bound collections can be tricky. For snap points to work immediate child of ScrollViewer should implement IScrollSnapPointsInfo interface. ItemsControl doesn't implement IScrollSnapPointsInfo and consequently you wouldn't see snapping behaviour.
To work around this issue you got couple options:
Create custom class derived from ItemsControl and implement IScrollSnapPointsInfo interface.
Create custom style for items control and set HorizontalSnapPointsType property on ScrollViewer inside the style.
I've implemented former approach and can confirm that it works, but in your case custom style could be a better choice.
Ok, here is the simplest (and standalone) example for horizontal ListView with binded items and correctly working snapping (see comments in following code).
xaml:
<ListView x:Name="YourListView"
ItemsSource="{x:Bind Path=Items}"
Loaded="YourListView_OnLoaded">
<!--Set items panel to horizontal-->
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<!--Some item template-->
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
background code:
private void YourListView_OnLoaded(object sender, RoutedEventArgs e)
{
//get ListView
var yourList = sender as ListView;
//*** yourList style-based changes ***
//see Style here https://msdn.microsoft.com/en-us/library/windows/apps/mt299137.aspx
//** Change orientation of scrollviewer (name in the Style "ScrollViewer") **
//1. get scrollviewer (child element of yourList)
var sv = GetFirstChildDependencyObjectOfType<ScrollViewer>(yourList);
//2. enable ScrollViewer horizontal scrolling
sv.HorizontalScrollMode =ScrollMode.Auto;
sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
sv.IsHorizontalRailEnabled = true;
//3. disable ScrollViewer vertical scrolling
sv.VerticalScrollMode = ScrollMode.Disabled;
sv.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
sv.IsVerticalRailEnabled = false;
// //no we have horizontally scrolling ListView
//** Enable snapping **
sv.HorizontalSnapPointsType = SnapPointsType.MandatorySingle; //or you can use SnapPointsType.Mandatory
sv.HorizontalSnapPointsAlignment = SnapPointsAlignment.Near; //example works only for Near case, for other there should be some changes
// //no we have horizontally scrolling ListView with snapping and "scroll last item into view" bug (about bug see here http://stackoverflow.com/questions/11084493/snapping-scrollviewer-in-windows-8-metro-in-wide-screens-not-snapping-to-the-las)
//** fix "scroll last item into view" bug **
//1. Get items presenter (child element of yourList)
var ip = GetFirstChildDependencyObjectOfType<ItemsPresenter>(yourList);
// or var ip = GetFirstChildDependencyObjectOfType<ItemsPresenter>(sv); //also will work here
//2. Subscribe to its SizeChanged event
ip.SizeChanged += ip_SizeChanged;
//3. see the continuation in: private void ip_SizeChanged(object sender, SizeChangedEventArgs e)
}
public static T GetFirstChildDependencyObjectOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj is T) return depObj as T;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetFirstChildDependencyObjectOfType<T>(child);
if (result != null) return result;
}
return null;
}
private void ip_SizeChanged(object sender, SizeChangedEventArgs e)
{
//3.0 if rev size is same as new - do nothing
//here should be one more condition added by && but it is a little bit complicated and rare, so it is omitted.
//The condition is: yourList.Items.Last() must be equal to (yourList.Items.Last() used on previous call of ip_SizeChanged)
if (e.PreviousSize.Equals(e.NewSize)) return;
//3.1 get sender as our ItemsPresenter
var ip = sender as ItemsPresenter;
//3.2 get the ItemsPresenter parent to get "viewable" width of ItemsPresenter that is ActualWidth of the Scrollviewer (it is scrollviewer actually, but we need just its ActualWidth so - as FrameworkElement is used)
var sv = ip.Parent as FrameworkElement;
//3.3 get parent ListView to be able to get elements Containers
var yourList = GetParent<ListView>(ip);
//3.4 get last item ActualWidth
var lastItem = yourList.Items.Last();
var lastItemContainerObject = yourList.ContainerFromItem(lastItem);
var lastItemContainer = lastItemContainerObject as FrameworkElement;
if (lastItemContainer == null)
{
//NO lastItemContainer YET, wait for next call
return;
}
var lastItemWidth = lastItemContainer.ActualWidth;
//3.5 get margin fix value
var rightMarginFixValue = sv.ActualWidth - lastItemWidth;
//3.6. fix "scroll last item into view" bug
ip.Margin = new Thickness(ip.Margin.Left,
ip.Margin.Top,
ip.Margin.Right + rightMarginFixValue, //APPLY FIX
ip.Margin.Bottom);
}
public static T GetParent<T>(DependencyObject reference) where T : class
{
var depObj = VisualTreeHelper.GetParent(reference);
if (depObj == null) return (T)null;
while (true)
{
var depClass = depObj as T;
if (depClass != null) return depClass;
depObj = VisualTreeHelper.GetParent(depObj);
if (depObj == null) return (T)null;
}
}
About this example.
Most of checks and errors handling is omitted.
If you override ListView Style/Template, VisualTree search parts must be changed accordingly
I'd rather create inherited from ListView control with this logic, than use provided example as-is in real code.
Same code works for Vertical case (or both) with small changes.
Mentioned snapping bug - ScrollViewer bug of handling SnapPointsType.MandatorySingle and SnapPointsType.Mandatory cases. It appears for items with not-fixed sizes
.