I am actually solving the issue with stack panel and images I would like to show in it.
What my approach was:
Because of just about 50 pictures to be shown , I just get ti image , make a thumbnail from it , and place that one by one to the stackpanel on left side of my program. (As bitmap)
User is able to click on image and do the action with a image.
Required amount was about 50 images.
New state:
New state is that the required amount of images is about 500 so 10 times more.
The problem is even my thumbnail is too small when I am adding it like :
foreach image in list do :
create thumbnail
add on click or and on touch event to that thumbnail
add that thumbnail to the stack panel
I saw somewhere was used a picture box , not sure if that will help me.
I am thinking about creating lists of pictures links (50 in every ) and put for instance first in a scroll bar and when scrollbar reaches the bottom, load next and when the top reaches load previous list.
The problem is I am reaching with ~ 175 images the maximum of process memory.
I am waiting for garbage collector to do its job after every cycle.
Maybe my approach is not good or should be different so this is why I am asking which approach use in order to solve this problem you suggest.
The source of the photos is simply folder of thumbnails of size 150x150 .
The images are added one by one in certain period of time ( one foto every one second or so )
Thank you
An example of implementation.
Create a collection of images sources.
The collection can contain a string or an Url with a path to the image.
In this case, the image will be created only when it is shown in the Window.
You can also set an instance of ImageSource (or rather classes derived from it).
In this case, the image will be immediately loaded into memory.
Also set the property for specifying the selected image.
using System;
using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
namespace ImagesViewer
{
public class ImagesViewerViewModel
{
public ObservableCollection<object> ImagesSource { get; }
= new ObservableCollection<object>()
{
"https://miro.medium.com/max/2400/0*UEtwA2ask7vQYW06.png",
new Uri("https://149351115.v2.pressablecdn.com/wp-content/uploads/2021/06/stackoverflow-prosus-blue-orange.png"),
new BitmapImage(new Uri("https://amazinghiring.com/wp-content/uploads/2019/03/jpddcgb89ow.jpg"))
};
public object SelectedImage { get; set; }
}
}
Bind the ListBox source to the created collection.
In the Element Template, specify the Image with the binding to the element and the thumbnail sizes you need.
Assign the selected item to the property for the selected image.
The ListBox has a VirtualizingStackPanel built in by default, so you don't need to take any additional steps to limit using memory.
In the viewing region, set the Image with a binding to the property for the selected image.
<Window x:Class="ImagesViewer.ImagesViewerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImagesViewer"
mc:Ignorable="d"
Title="ImagesViewerWindow" Height="450" Width="800">
<FrameworkElement.DataContext>
<local:ImagesViewerViewModel/>
</FrameworkElement.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding ImagesSource}"
SelectedItem="{Binding SelectedImage}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Margin="5"
Width="100" Height="100"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Image Grid.Column="1"
Source="{Binding SelectedImage}"/>
</Grid>
</Window>
Ok but scoriling of list of 170 150x150 causing the slowinest of the program as I already mentioned . I did the test already. Can you confirm or refute that please ?
Тестирование.
More than 1200 images in JPG and PNG formats are pre-recorded in the folder "C:\150x150".
The ViewModel gets a list of files in this folder and creates a string array with their paths.
The time for this operation is also recorded.
public class ImagesViewModel
{
public IEnumerable<string> ImagesPaths { get; }
public long ExecutionTimeGetPaths {get;}
public ImagesViewModel()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ImagesPaths = Directory.GetFiles("C:/150x150")
.Where(path => path.EndsWith(".jpg") || path.EndsWith(".png"))
.ToArray();
stopwatch.Stop();
ExecutionTimeGetPaths = stopwatch.ElapsedMilliseconds;
}
}
In View in the upper right corner displays the time taken to get the paths of all files.
On the left side is a ListBox showing images in size 150x150 for all files.
<Window x:Class="Eee.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Eee"
mc:Ignorable="d"
Title="MainWindow" Height="1000" Width="300">
<Window.DataContext>
<local:ImagesViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding ExecutionTimeGetPaths}"
HorizontalAlignment="Right" VerticalAlignment="Top"
FontSize="30" Margin="10"/>
<ListBox ItemsSource="{Binding ImagesPaths}" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"
Width="150" Height="150"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Results: the time of obtaining the paths is 12-14 ms, the images are displayed instantly.
There are NO LAGS AT ALL when scrolling.
The same was verified for image files of arbitrary format from 128x128 to 4200x2800.
The result is the same.
I have what seems like a straightforward problem: make an application which can handle any resolution display.
Background:
This is a display-only application (no user interface). It is for display of information, and I've made a test app to work out an issue. Have spent a few days trying to find a solution to this one tiny problem. Either need a clever programming solution (which has thus far eluded me) or a different way of approaching the problem (again, has been elusive).
This is a WPF application with VB.net code behind. The composition of the one and only window needs to maintain the same relative layout to the display components, and the application is intended to run full-screen (this demo runs in a window, so I can resize it easily and test different "screen layouts").
All but one part of the layout works fine.
The part with the problem is a "bar" which is intended to animate up or down, depending on circumstances. The size of the bar already changes appropriately, depending on the size of the application window. However... the distance the bar travels is dependent on the size of that bar; the shorter it is, the fewer pixels it has to travel, and conversely, a higher resolution screen necessitates it travel further. Animation is accomplished by changing the height of a grid object which contains both a rectangle (the bar) and a viewport which in turn has a textblock object.
I've created a resource value in XAML with a value which represents the default height of the grid object which contains the bar. If that resource is bound to the value of the keyframe as a static resource, the value is passed to storyboard and the animation happens. However, I have not been able to change the value in this configuration. When the size of the window changes, VB code attempts to change the resource key value, but no joy. Have also tried this with the binding done as a dynamic resource.
Lots of reading shows that some bindings into XAML work, while others (such as keyframes) aren't so lucky; something about "being freezable," which seems silly, as this shouldn't be that hard. Other things I've read suggest that the "fix" is to implement solution in the code behind. Most don't provide any other info on how that might be done, and the few other items suggestion methods which seem absurdly circuitous.
Here is the XAML, with some inline comments to point out what's going on:
<Window x:Name="SmartClock" x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Set a variable (resource) to be used as the height value in animation -->
<System:Double x:Key="OnAirBarUp">84</System:Double>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- ****** HERE'S WHERE THE "FUN" BEGINS :/
This line needs to have a target value which changes, depending on the
height of objects in the window, and those are affected by the size
of the window. The intention is to make this a full-screen app, but
can be used on many different monitors, so the resolution is not known
at design time.
The resource I set earlier in the XAML ("84") will be used for this
keyframe value, and the animation works... but is only "correct" for
the default window size.
(yes, I know that the bar starts in the "up" position currently, but that
is just to help me see it as I debug this... when the button is pushed,
the bar should instantly disappear, and then slide back on to the screen
to the same position).
*************************************************************************** -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="{StaticResource ResourceKey=OnAirBarUp}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName" Height="1*"/>
<RowDefinition Name="row_Status" Height="1*"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
And here is the VB.net code which also has copious comments:
Class MainWindow
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' Set the size of items on the screen based on the size of the window.
' "rectangle_OnAir" is the rectangle (the "On-Air" bar for the display)
' "viewbox_OnAir" allows the textblock to properly scale.
' "grid_OnAir" is the container which will be made taller and shorter
' through animating the "height" property. This allows the contents
' to slide up and down in their region of the screen.
' Since the window can be any size, we adjust the height of the
' controls to accomodate.
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' Set the height of the dynamic resource "OnAirBarUp" to a value
' which equals the height of "rectangle_OnAir".
' This will be used in the animation to allow the whole rectangle to
' be displayed.
Resources("OnAirBarUp") = grid_Top.ActualHeight / 2
End Sub
End Class
This is one version of many permutations.
When the code runs without re-sizing the window, this is what it looks like:
This is the default layout of this test page... the "On Air" bar is visible, and pressing the "button" causes it to disappear for a moment, and then immediately animate upwards into the current position
With the window small, the animation causes the bar to overshoot the location where it need to be (the grid height value is too large for this size window
Making the window larger than default has the opposite effect on the animation; the bar undershoots the landing location.
I've contemplated wrapping the whole display in a viewbox control, so the whole mess can scale to fit whatever display, and tried some of this, with less-than-stellar results. It was overly complicated, and there are distortions in the graphics and text.
From what troubleshooting I could do, it seems that the resource value does change, but not the value at the binding point; I'm pretty much stuck with whatever value was used at the moment the application was launched.
It seems odd to provide a framework for dynamically-configurable layout, and for extensive animation capabilities, and then have it hamstrung by those two aspects not being able to "play nice" together because a simple double value can't be passed from the code to the layout markup.
Please excuse the length of the post; I'm trying to be thorough, and trying to anticipate what someone might write in response. Also please excuse the hideous colors of the sample -- what was an aid to me during the layout of this test, not intended for the final application.
Ideally, the best solution would allow for any size display, without the program having any foreknowledge of that resolution. Also, being able to resize during program execution would be nice, in case the program can be used (after all) in a less-than-full-screen arrangement. In that case, it would need to be able to gracefully handle most reasonable window sizes (I'd have to code in some lower boundary limits to the window size).
Whatever solution I use, it would need to be able to handle other similar animation needs (other parts need to move on and off the screen, moving the correct amount for the given circumstances).
I had posted this last night on both Stack Overflow and on Microsoft's MSDN forums, and got a good reply on MSDN. That pointed me in the right direction, and I posted a follow-up message with a detailed description of what worked for me. Here is that follow-up report:
Andy,
Thanks for the quick and detailed reply!
I'll start with this part: It now works.
It did take me a couple more hours of groping (using your clues) to get there. I started this reply at that time (about four hours ago), but persistence paid off.
-------------
You say: "I get that you're animating the bar but not what it's relative to and where it's coming or going to."
I'm guessing you mean literally, as in where on the screen, as opposed to conceptually (as in purpose).
The literal "where" is to have the bar rise out of the middle third (approximately) region of the screen (divided horizontally). The whole application is a clock with some value-added features for use in a newsroom. I already have a working clock based generally on this design, but the "On Air" bar isn't implemented yet. This will be triggered by an event over a network socket connection, quite literally when the studio is "on air." When that happens, the text in the upper third of the screen is shrunk gracefully as the "on air" bar rises up, essentially halfway into the top third region.
The animation works properly if I don't resize the window. Since I don't know what size monitors will be used in the different implementations of this, and since this will end up being at some of our other facilities which do the same thing, I need to build it at this stage so it is flexible enough to handle a 1024x768 screen or a 1920x1080, or something in-between, or 4K, or whatever.
I could make the bar a fixed size, but that would go against the balance of the layout (aesthetic concern) and the readability of right-sized text (usability concern).
There are to be other status fields which will be able to move into view, depending on circumstances (other things happening in real time, triggered by events over the computer network), which will need to have similar flexibility of the positioning of the animations.
Looking over the marquee code you pointed out to me, it seems that binding is done to an object, but I'm unsure how that method could change the distance that a keyframe could move the object, just adjust the properties of the object being moved.
-------------
I looked over the "marquee" code, though it took some patience to dissect it. The C# isn't too bad, but I'm not fluent, so I turned to an online code converter to translate it to VB:
Public Partial Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private SBMarquee As Storyboard
Private XAnimation As DoubleAnimation
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
SBMarquee = TryCast(Me.Resources("SBmarquee"), Storyboard)
XAnimation = TryCast(SBMarquee.Children(0), DoubleAnimation)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
AddHandler Me.SizeChanged, AddressOf Window_SizeChanged
End Sub
Private Sub Window_SizeChanged(sender As Object, e As SizeChangedEventArgs)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
MarqueeContainer.Visibility = Visibility.Hidden
SBMarquee.Begin()
MarqueeContainer.Visibility = Visibility.Visible
End Sub
End Class
Cool.
I'm not familiar with partial classes, but a quick check online seemed to suggest to me that I could just include the relevant parts into my VB class for the main window.
I don't know if the "initializeComponent" part is required, but it didn't throw an error. However, the attempt to create a Storyboard object met with some resistance. I figured that I needed an "Imports" command to get the proper namespace added (full disclosure: the idea of "namespaces" isn't obvious to me, but I get that something needed to be imported... the "namespace" moniker doesn't seem to fit, and I'm too literal). Here is what I added, right at the very top (above the "class" line):
Imports System.Windows.Media.Animation
Now these lines works:
Private story_OnAirUp As Storyboard
Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
The first line creates a Storyboard object, and the second makes the necessary timeline object. I thought at first I would be making an "EasingDoubleKeyFrame" object, which I could have made, but the child object of the Storyboard object is not a keyframe (well, not with the XAML constructed using the DoubleAnimationUsingKeyFrames element); my initial attempts to use a keyframe met with resistance. Here is what I tried which didn't work:
Private story_OnAirUp As Storyboard
Private keyframe_OnAirUp As EasingDoubleKeyFrame
No errors at this point, so I thought I was golden. Not so fast, young padawan.
Here is the next code I added, altered from the marquee example:
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
keyframe_OnAirUp = TryCast(story_OnAirUp.Children(1), EasingDoubleKeyFrame)
keyframe_OnAirUp.Value = grid_Top.ActualHeight / 2
AddHandler Me.SizeChanged, AddressOf MainWindow_SizeChanged
End Sub
No dice. The "TryCast(story_OnAirUp.Children(1)" part generates an error:
"Value of type 'Timeline' cannot be converted to 'EasingDoubleKeyFrame'."
Well, that stinks.
After some more comparison of the code, I see that my XAML code differs from the marquee example because of the inclusion of this line:
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
So, the child of the Storyboard object isn't the EasingDoubleKeyFrame (well, not directly), but rather is the "DoubleAnimationUsingKeyFrames" element. However, the value I need to bind to isn't in the DoubleAnimationUsingKeyFrames element; it is in the EasingDoubleKeyFrame.
(Note: the sample code had "DoubleAnimation" the altered property as "To:" but the "EasingDoubleKeyFrame" uses the "value" property; I noticed that and adjusted accordingly.)
I tried to make a child object of the DoubleAnimationUsingKeyFrames element, but turns out "children" is a property of it. Then it dawned on me:
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
The "(1)" points to the second keyframe, which is the one where the "On Air" grid is at the "full height," which has a default value of 84 pixels, but needs to change.
After I got the "Window_ContentRendered" done, I tried running the code.
Error in the "MainWindow_SizeChanged" subroutine:
"An unhandled exception of type 'System.NullReferenceException' occurred in WpfApplication3.exe
Additional information: Object reference not set to an instance of an object."
Hmmm. Okay, the "MainWindow_SizeChanged" routine is running before the "Window_ContentRendered" routine, not allowing the objects to be instantiated before they were used.
I moved the object creation code from "Window_ContentRendered" to "MainWindow_SizeChanged," remarked our the "ContentRendered" routine, and then tried again.
It worked. Here's the code which works:
XAML
<Window x:Name="SmartClock" x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- **** This is the keyframe which has a changed value from the VB code -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="84"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName"/>
<RowDefinition Name="row_Status" Height="Auto"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
VB
Imports System.Windows.Media.Animation
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private story_OnAirUp As Storyboard
Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' These next three lines were moved from the "ContentRendered" section
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
timeline_OnAirUp = TryCast(story_OnAirUp.Children(0), DoubleAnimationUsingKeyFrames)
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' This is the line which actually sets the value to the animation keyframe.
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
End Sub
End Class
Thanks for pointing me in the right direction, and I hope my follow-up (admittedly long) helps someone else.
Need help with a xaml Layout.
I have a Wrapanel with two elements inside. The left one, MainGrid, needs to be fixed at 900 pixels. The second one, AuxillaryDataScrollViewer, I would like to be a minimum of 650 and a maximum of 100% if the WrapPanel wraps it. Is this possible?
Here's what I got so far:
<Window x:Class="scratch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="768" Width="1024">
<Grid>
<ScrollViewer>
<WrapPanel>
<Grid Width="900" Height="800" Background="Bisque"></Grid>
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden" Width="650">
<Grid Width="1500" Height="700" Background="Tomato"></Grid>
</ScrollViewer>
</WrapPanel>
</ScrollViewer>
</Grid>
</Window>
Thanks!
Edit adding more detail:
Client would like to do data entry and see the calculated results realtime in a panel to the right, but some of the computers in his lab are only capable of 1280pixels width so on those machines he would like the results to wrap to below the data entry form.
Since the requirement is so well defined, you could simply add a SizeChanged handler, and set the second element's width manually per the container's width. If the container is less than 900 + 650, then stretch the second element to 100% of the container.
public MainWindow()
{
SizeChanged += MainWindow_SizeChanged;
}
void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
double firstElementWidth = MainGrid.Width; // 900
double secondElementMinWidth = AuxillaryDataScrollViewer.MinWidth; // 650
double secondElementWidth;
// if wrapped, stretch to the container
if (e.NewSize.Width < firstElementWidth + secondElementMinWidth)
secondElementWidth = e.NewSize.Width;
// otherwise (not wrapped), fill the remainder of the first row
else
secondElementWidth = e.NewSize.Width - firstElementWidth;
}
Obviously this is quick-and-dirty. If you need something more robust, then I suggest writing a custom panel that adheres to the conventions you need. The WrapPanel can't stretch elements to the overall width, but there's no reason you couldn't design a panel that does.
UPDATE I added the UserControl definition to the XAML.
I've noticed some strange behavior in a couple of WPF applications I've created lately, that seem to be related to using "*" for a Grid row height or column width.
The behavior I'm referring to is when trying to expand items in a control (like a treeview), the entire window will resize its height instead of creating a scrollbar. So if I just run the application, and start expanding nodes, when the items extend beyond the visible portion of the UI then the window will resize.
BUT if I resize the window first, or even just click on the bottom or right border (without actually resizing), then it will behave normally and leave the window height alone, with a scrollbar on the treeview.
<UserControl x:Class="ProjectZ.Views.GenericDefinitionView"
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:cal="http://www.caliburnproject.org"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:local="clr-namespace:ProjectZ"
xmlns:behaviors="clr-namespace:ProjectZ.Behaviors"
mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="mnuMainMenu" IsMainMenu="True">
... menu stuff
</Menu>
<xcad:DockingManager ... />
</Grid>
</UserControl>
I've played around with it and it seems to always start happening after I've used the "*" value for a grid row height or a column width. If I take that out, it seems to behave normally.
Has anyone else run into this? Any ideas what I'm doing wrong or could do differently to fix this? The only other information I think might be relevant is that this is using Caliburn.Micro. The only settings passed to the window when launching are: MinHeight, MinWidth, Title, and Icon.
The problem is that you never specify a height in the visual tree above the element.
Your UserControl or the Window that's created needs a specific height if you want to use star sizing effectively. Otherwise, a height is "chosen" at runtime, but the Window is effectively set to size by content. As you change items, the Window resizes.
As soon as you touch a border, the Height is being set (whether or not you resize), in which case it then dictates the layout correctly.
If you specify a default height for the Window as its created, the issue will likely resolve itself.
The resizing must be caused by the code:
<UserControl
//...stuff>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</UserControl>
Where the < * > can be understood as "Take the rest of all available space, after placing all the other elements first". So when you add another element in the control the view will set itself and the the
<RowDefinition Height="*" />
Will resize itself acordingly to take the remainder of space available. To remove this you can just set a specific height for the row or another idea will be to add another Grid inside the row definition so that it won't resize since it will always be inside the row, but still have all of it's functionality.
I have a relatively complex layout. It consists of:
A grid with one column and three rows.
In the first row (the on giving me trouble) I have a developer express componenet - another GridControl.
My problem is, that though the height of this first row is Auto, the vertical scrollbar displays even though there's space enough for content.
I've tried setting the ScrollViewer.VerticalScrollBarVisibility="Hidden" on the row's rowdefinition, but this doesn't help.
Likewise, I've set the inner GridControl to not use scrollbars (using some Developer Express magic - not just ScrollViewer as this doesn't work)
Yet, no matter what I do, that damn scrollbar appears... Is there any way to figure out which control renders it, so I can disable the damn thing? It's not just a question of it being ugly - scrolling it actually messes with the layout!
Thanks in advance!
The relevant code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" />
<RowDefinition Height="*" MaxHeight="240" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<dxg:GridControl Name="StudySizeGrid" Grid.Column="0" Grid.Row="0" >
<dxg:GridControl.Resources>
<ControlTemplate x:Key="{dxgt:TableViewThemeKey ResourceKey=ControlTemplate}">
<ScrollViewer x:Name="scr"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
Focusable="False"
dxg:GridControl.CurrentView="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Template="{DynamicResource {dxgt:TableViewThemeKey ResourceKey=ScrollViewerTemplate}}">
<ScrollViewer.CanContentScroll>False</ScrollViewer.CanContentScroll>
</ScrollViewer>
</ControlTemplate>
</dxg:GridControl.Resources>
...
</dxg:GridControl>
EDIT FOR CLARIFICATION: This is WPF issue :-)
You could try checking out the VisualTree, i think Snoop might be helpful for that, it probably has some other useful features too. Getting the VisualTree is a trivial matter though, you can write a single recursive method using the VisualTreeHelper, so you might not need the big guns.
e.g.
public static TreeViewItem GetVisualTree(this DependencyObject dpo)
{
TreeViewItem item = new TreeViewItem();
item.Header = dpo.GetType().ToString().Split('.').Last();
if (dpo is FrameworkElement && (dpo as FrameworkElement).Name != string.Empty) item.Header += " (" + (dpo as FrameworkElement).Name + ")";
int cCount = VisualTreeHelper.GetChildrenCount(dpo);
for (int i = 0; i < cCount; i++)
{
item.Items.Add(VisualTreeHelper.GetChild(dpo, i).GetVisualTree());
}
return item;
}
Wrote that quite some time ago, it's very sketchy (wouldn't recommend making it an extension method), gets the whole tree at one, could be modified to only fetch children on expansion of the node.
You could use something like Google Chrome's tools.
I would, in Chrome, right click around the area that has the scroll bars and select "Inspect Element". Chrome will highlight with a border what element you are looking at. You can then navigate the html within Google Chrome's inspector until it is highlighting the element with the scrollbar.
You can then find the reason from there.