I want to populate a Vertical StackPanel with objects of another UserControl. All UserControls should be scaled by the widest one. The widest control should use the full available width.
ParentControl
<Grid x:Name="mainGrid" >
<StackPanel x:Name="myStackPanel" />
</Grid>
private void OnMyStackPanel_Loaded(object sender, RoutedEventArgs e)
{
myStackPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
myStackPanel.Arrange(new Rect(0, 0, myStackPanel.DesiredSize.Width, myStackpanel.DesiredSize.Height));
var width = myStackPanel.ActualWidth;
}
The problem is that the actual width after is 0. I have to add an element to the StackPanel. Is there a way to wrap this into something that asumes the available width so I can read that? Using a Dispatcher is not working. The ParentControl is nested into a Grid Cell of a View.
Related
I am a bit new to WPF. I wanted to make my image scrollable when I scale up the image. This is my XAML code.
<UserControl xmlns: skia="clr-namespace:SkiaSharp.Views.WPF:assembly=SkiaSharp.Views.WPF">
<Grid>
<skia:SKElement Name="Canvas" PaintSurface="SKElement_PaintSurface">
</Grid>
</UserControl>
private void SKElement_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
\\Code goes here...
}
Inside the SKElement_PaintSurface method, I created a way to draw the bitmap image on the canvas. But when I scale up the image I can't scroll the image. Does anyone know to create a scrollbar for this?
Put the Canvas in a ScrollViewer element and set its Height and/or Width when you scale it:
<UserControl xmlns: skia="clr-namespace:SkiaSharp.Views.WPF:assembly=SkiaSharp.Views.WPF">
<Grid>
<ScrollViewer>
<skia:SKElement Name="DrawCanvas" PaintSurface="SKElement_PaintSurface">
</ScrollViewer>
</Grid>
</UserControl>
private void SKElement_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
\\Code goes here...
DrawCanvas.Height = 100;
}
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>
I followed this link Detect when WPF listview scrollbar is at the bottom?
But ScrollBar.Scroll doesn't exist for ListView in Windows 10 .. how to achieve this requirement in windows 10
Thanks
Use the methods described here How can I detect reaching the end of a ScrollViewer item (Windows 8)?
If you want incremental loading there's a built-in solution for that. Otherwise go and traverse the visual tree for the scrollviewer object in the template.
You can do it by simply detecting VerticalOffset and ScrollableHeight of your ScrollViewer. Here is my simple code.
XAML:
<!--Apply ScrollViwer over the ListView to detect scrolling-->
<ScrollViewer Name="ContentCommentScroll" Grid.Row="2"
ViewChanged="ContentCommentScroll_ViewChanged" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<!--My Dynamic ListView-->
<ListView Name="MyDataList" ItemsSource="{Binding MyList}"
ItemTemplate="{StaticResource MyDataTemplate}"
ItemContainerStyle="{StaticResource myStyle}"/>
</ScrollViewer>
And code in its XAML.CS:
// Hold ScrollViwer
public ScrollViewer listScrollviewer = new ScrollViewer();
// Binded event, which will trigger on scrolling of ScrollViewer
private void ContentCommentScroll_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
Scrolling(ContentCommentScroll);
}
private void Scrolling(DependencyObject depObj)
{
ScrollViewer myScroll= GetScrollViewer(depObj);
// Detecting if ScrollViewer is fully vertically scrolled or not
if (myScroll.VerticalOffset == myScroll.ScrollableHeight)
{
// ListView reached at of its end, when you are scrolling it vertically.
// Do your work here here
}
}
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
You can also apply similar logic to detect that if ListView Horizontally reached to its end.
You can wrap it with a ScrollViewer and name it. Then you can use some other methods.
This is what I have
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="25"/>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Expander IsExpanded="False" Grid.Row="0" >
<DataGrid name="FirstGrid" />
</Expander>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="5" />
<DataGrid Grid.Row="2" name="SecondGrid" />
When I click the expand button on the expander it correctly expands row 0 to the size of the DataGrid FirstGrid and it properly collapses the row as well. This does not work however if I expand the FirstGrid then manually resize that row by dragging the GridSplitter up or down and then pressing collapse button on the expander. What happens is that FirstGrid collapses but the row itself the FirstGrid is in does not. Any suggestions?
Thanks
Rob is right, once you move the gridsplitter, the Height of the first Row will not be Auto anymore, so it will not respond to the change in the expander size
In order for this to work you would need to add a behavior to the expander which will listen to the expander.expanded and collapsed event and update the grid row to be auto again. something like this:
public class GridColumnWidthReseterBehaviour : Behavior<Expander>
{
private Grid _parentGrid;
public int TargetGridRowIndex { get; set; }
protected override void OnAttached()
{
AssociatedObject.Expanded += AssociatedObject_Expanded;
AssociatedObject.Collapsed += AssociatedObject_Collapsed;
FindParentGrid();
}
private void FindParentGrid()
{
DependencyObject parent = LogicalTreeHelper.GetParent(AssociatedObject);
while (parent != null)
{
if (parent is Grid)
{
_parentGrid = parent as Grid;
return;
}
parent = LogicalTreeHelper.GetParent(AssociatedObject);
}
}
void AssociatedObject_Collapsed(object sender, System.Windows.RoutedEventArgs e)
{
_parentGrid.RowDefinitions[TargetGridRowIndex].Height= GridLength.Auto;
}
void AssociatedObject_Expanded(object sender, System.Windows.RoutedEventArgs e)
{
_parentGrid.RowDefinitions[TargetGridRowIndex].Height= GridLength.Auto;
}
}
And you use it like this:
<Expander ...>
<interactivity:Interaction.Behaviors>
<behaviors:GridColumnWidthReseterBehaviour TargetGridRowIndex="0"></behaviors:GridColumnWidthReseterBehaviour>
</interactivity:Interaction.Behaviors>
...
As soon as you move the gridsplitter, the Height of the first Row will not be Auto anymore, but a certain value, like 70. After that it doesn't matter if some child in that row changes its height.
Combining a splitter with some auto-sized child/row is very difficult; you can take a look at the side expander in Microsoft Outlook; i suspect that this is what you want. If so, you shouldn't use an expander, because a regular expander contains a StackPanel so its children are always auto-sizing in the expand direction. I'm not sure what you want, but i think going for a custom control is your best option.
I have UserControl which I display like this, it's for previewing a receipt ticket before printing:
<ScrollViewer x:Name="xzScroll" Template="{StaticResource scrollView}" Height="488" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Border x:Name="xzPreview" BorderThickness="0" Background="White" Margin="0,0,10,0" Padding="15">
// The UserControl
</Border>
</ScrollViewer>
When the user wants to print the receipt, I remove the UserControl from its parent Border, and try to re-measure and re-arrange the control according to the printeable area:
public void Print()
{
PrintDialog dlg = new PrintDialog();
this.Measure(new Size(dlg.PrintableAreaWidth, double.PositiveInfinity));
this.Arrange(new Rect(this.DesiredSize));
this.UpdateLayout();
dlg.PrintTicket.PageMediaSize = new PageMediaSize(this.ActualWidth, this.ActualHeight);
....
}
However, the Height and Width properties won't change, resulting in a faulty print with messed up margins and some text clipping on the sides.
When I create a new instance of my UserControl in code, and then use the Measure and Arrange methods, I see the Width and Height properties change according to the PrinteableAreaWidth, resulting in a proper receipt ticket.
How can I make sure the Width and Height properties are properly getting set?
Is it right that you want to to resize the Usercontrol?
If yes, why don't create a Class (ViewModel) for the Usercontrol with the two Properties Height and Width and bind the User Control to these Properties.
Than you can change the size without removing the Usercontrol.
Here for Example how such a ViewModel could look like. You will need to implement the INotifyPropertyChanged.
public class UserControlViewModel:INotifyPropertyChanged
{
private double _height;
private double _width;
public double _height
{
get { return _height;}
set {
if(_height == value)
return;
_height = value;
OnPropertyChanged();
}
public double _width
{
get { return _width;}
set {
if(_width == value)
return;
_width = value;
OnPropertyChanged();
}
And in Xaml you can bind it this way.
<ScrollViewer x:Name="xzScroll" Template="{StaticResource scrollView}" Height="488" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Border x:Name="xzPreview" BorderThickness="0" Background="White" Margin="0,0,10,0" Padding="15">
<MyUserControl
DataContext = "{Binding MyControlViewModelObject}"
Height = "{Binding Height}"
Width = "{Binding Width}"/>
</Border>
</ScrollViewer>
It is possible that you have declared the UI elements in your UserControl with exact Margin and/or Height and Width dimensions... of course, I can't be sure about that because you didn't show us your relevant code.
However, if this is the case, then you won't be able to rearrange them by re-sizing the UserControl externally. The solution is to use Grid controls to organise your UI elements, so that they can be automatically re-sized when the UserControl is resized. See the Grid Class page on MSDN for further information.