Scrollbars in a dynamically filled stack panel - c#

I am new for WPF so apologies if the answer is so obvious.
I got a WPF resizable window with a single stack panel control that is stretched vertically and horizontally to fill the window.
On window activated event, I use ".Children.Add" to add button controls to the panel. I have no idea how many buttons will be there at runtime so I checked "CanVerticallyScroll" in the panel. ScrollViewer.VerticalScrollBarVisibility is set to Visible by default.
I am still not seeing scroll bars at runtime though.
What properties did I miss to show scrolling panel with buttons?
Thanks
XAML:
<Window x:Class="ResMed.Ecp.Utility.ConnectionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ConnectionWindow" Height="388" Width="641.6" Activated="Window_Activated">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="359*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="pnlConnectionButtons" Margin="10,10.2,10.2,10" Grid.Row="1" CanVerticallyScroll="True"/>
</Grid>
</Window>
Code behind:
private void Window_Activated(object sender, EventArgs e)
{
for (int i = 0; i < 20; i++)
{
Button btn = new Button();
btn.Content = "Hello";
pnlConnectionButtons.Children.Add(btn);
}
}

Place your StackPanel inside a ScrollViewer:
<ScrollViewer>
<StackPanel>
<Button Content="Hello World"></Button>
...
...
</StackPanel>
</ScrollViewer>
You can also remove CanVerticallyScroll="True". From MSDN:
This property is not intended for use in your code. It is exposed publicly to fulfill an interface contract (IScrollInfo). Setting this property has no effect.

Related

UWP - How to prevent InputPane from showing and hiding automatically

I want to manually control the behavior of InputPane to prevent it from showing or hiding automatically.
In my page that I put its image top, I want to InputPane show as user navigate to the page and keep showing until he/she clicks on specified button and prevent it from hiding if user clicks anywhere else in the page.
Also I want to InputPane remain hidden even if user clicks on TextBox.
I already know that there are TryShow() and TryHide(), but i can't revent auto showing and hiding.
The easy way to control it is by controlling focus of your TextBox. If you set IsTabStop on the TextBox to false - it won't take focus and so the SIP won't show up. If it already has focus - you'll need to move it out. If you want to display the SIP - focus the TextBox. Note that for performance reasons and also to prevent user confusion - it might make sense to use a TextBlock instead of a TextBox when the control should not be editable.
XAML
<Page
x:Class="App18.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App18"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TextBox
x:Name="myTextBox"
IsTabStop="False"
AcceptsReturn="True"
VerticalAlignment="Stretch"
TextChanged="MyTextBox_OnTextChanged"/>
<Button
x:Name="myButton"
Grid.Row="1"
Click="ButtonBase_OnClick">Edit</Button>
</Grid>
</Page>
C#
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace App18
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
myTextBox.IsTabStop = true;
myTextBox.Focus(FocusState.Programmatic);
}
private void MyTextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
if (myTextBox.Text.ToLower().Contains("done"))
{
myTextBox.IsTabStop = false;
myButton.Focus(FocusState.Programmatic);
}
}
}
}

Make XAML control with WebView full screen

Ultimate Goal
When a user clicks the Expand button, I want the WebContainerControl to be full screen, be focused, not allow scrolling in the ScrollViewer, and overlap the title grid (with the back button, page title, etc.)
Basically, it should be like clicking on a photo in a nice photo viewing app. Exapnd to full screen, have an X button in the top right corner and when you click it, it goes back to the regular view.
Problem
Since it's a WebView, I can't simply pass the view to a popup (It gives me an invalid args exception since the current browsing session can't be passed by reference... they logged in on a site, so it would be insecure I assume)
I have a XAML control with a webview in it:
<UserControl x:Class="App.WebContainerControl">
<Grid x:Name="grdWebContainer">
<StackPanel>
<Button Click="btnExpandView_Click"/>
<WebView x:Name="wvSite"/>
</StackPanel>
</Grid>
</UserControl>
Here is an example view it would be loaded into:
<Grid x:Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title Grid -->
<Grid x:Name="grdTitleBar" Grid.Row="0">
<TextBlock Text="App Title"/>
</Grid>
<!-- Web Views -->
<Grid Grid.Row="1">
<ScrollViewer>
<StackPanel>
<controls:WebContainerControl x:Name="First Site"/>
<controls:WebContainerControl x:Name="Second Site"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
What I have so far
So far, when they press the Expand button, it makes the control full screen (using Current.Window.Bounds)
Then, I pass the event that the button is pressed to the main view:
private void OnAccount_Expanded(object sender, ExpandedEventArgs args) {
// Expanded button is pressed and control is made full screen
if (args.IsExpanded) {
// Hide titlebar
grdMain.RowDefinitions[0].Height = GridLength.Auto;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else {
// show titlebar again
GridLength gl = new GridLength(140);
grdMain.RowDefinitions[0].Height = gl;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
The Question
Right now, it makes it full screen, but I can still scroll. Any idea how to set the ScrollViewer to horizontally center on the control? If anyone has a better idea on how to achieve my Ultimate Goal, you would make me one happy camper! (Remember, it won't allow me to pass my control around, only manipulate it)
If I understand well, Put name on you Scroll Viewer
<ScrollViewer Name="uiScroll" >
............
</ScrollViewer >
When you doing full screen set visibility of you scrollBar
uiScroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;

WPF webBrowser floating on all controls outside grid

I have a webbrowser control and a canvas in grid row 0 & 1 receptively.
canvas have large picture and makes a horizontal scroll on grid.
So when i scroll down the webbrowser is floating on every control, where it should have scrolled within grid along canvas.
please guide what might be problem.
The code is here:
var wb = new WebBrowser();
wb.NavigateToString(#"<html><head><style> h1{color:red} </style></head><body>
<h-1>HTML code go here</h-1>
<h-1>HTML code go here</h-1>
</body></html>");
wb.Width = 700;
wb.Height = 200;
Grid.SetRow(wb, 0);
T1Grid.Children.Add(wb);
var imagePath = AppDomain.CurrentDomain.BaseDirectory + "images/Jellyfish.jpg";
var mainBitmap = new BitmapImage();
mainBitmap.BeginInit();
mainBitmap.UriSource = new Uri(#"" + imagePath, UriKind.RelativeOrAbsolute);
mainBitmap.EndInit();
_canvas = new Canvas
{
Width = mainBitmap.PixelWidth,
Height = mainBitmap.PixelHeight,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left
};
var imageBrush = new Image { Source = mainBitmap };
_canvas.Children.Add(imageBrush);
Grid.SetRow(_canvas, 1);
T1Grid.Children.Add(_canvas);
xaml
<TabControl Grid.Row="1">
<TabItem Header="TabItem1">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid x:Name="T1Grid" Background="Aqua">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ScrollViewer>
</TabItem>
<TabItem Header="TabItem2">
<Grid x:Name="T2Grid" Background="Aqua">
</Grid>
</TabItem>
<TabItem Header="TabItem3">
<Grid x:Name="T3Grid" Background="Aqua">
</Grid>
</TabItem>
</TabControl>
I had a look at your code and even tried replicating it in XAML, but there really is a problem there. Something is keeping the WebBrowser control at a certain size and somehow its z-index appears to always be on top.
After looking into it, I found out that it's because of the way it was implemented. From #SvenG's answer to the WPF c# webbrowser scrolls over top menu question here on Stack Overflow:
The WPF WebBrowser control is basically the old Win32 Webbrowser control and is technically rendered above the WPF content. Additionally it has some serious issues with size calculations.
The linked question also contains links to more information. Also see the links in the Why WPF WebBrowser Control is always on top ? post on the Visual Studio Forum for further information.

Grid column size is not recalculated properly when restoring a maximized window

I am trying to implement a kind of single-line tab control in WPF which shows scroll buttons left and right of the area containing the tabs. The tabs are implemented inside a custom control. The scroll buttons shall be displayed only if the window is too small to display all tabs.
When the window is resized by dragging on its borders, everything works as expected.
But when the window is maximimized and then restored, then the right scroll button remains hidden.
The problem seems to appear only if the Visibility property of the right scroll button is data-bound to a dependency property of the custom control that is updated within the custom control's Measure pass.
My question is: am I using WPF correctly here, or is there something that needs to be done in a different way? (Please note: I need to use the data-binding and the custom control; therefore please avoid answers that suggest to take a completely different approach.)
Here is a small sample program which illustrates the problem:
When width is small:
When width is large:
These are the files of the sample program:
MainWindow.xaml:
<Window x:Class="GridTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gridTest="clr-namespace:GridTest"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="theGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="btnScrollLeft" Content="<" Grid.Row="0" Grid.Column="0" Width="30"/>
<gridTest:MyCustomControl x:Name="cust" Grid.Row="0" Grid.Column="1"/>
<Button x:Name="btnScrollRight" Content=">" Grid.Row="0" Grid.Column="2" Width="30"
Visibility="{Binding ElementName=cust, Path=ShowButton}"/>
<TextBox Text="The content goes here..." Grid.Row="1" Grid.ColumnSpan="3"
Background="LightGreen" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Button x:Name="btnRedraw" Grid.Row="1" Grid.Column="1" Content="Redraw" VerticalAlignment="Bottom"
HorizontalAlignment="Center" Click="btnRedraw_Click" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace GridTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnRedraw_Click(object sender, RoutedEventArgs e)
{
theGrid.InvalidateMeasure();
}
}
}
MyCustomControl.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace GridTest
{
public class MyCustomControl : Control
{
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
public Visibility ShowButton
{
get { return (Visibility)GetValue(ShowButtonProperty); }
set { SetValue(ShowButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowButtonProperty =
DependencyProperty.Register("ShowButton", typeof(Visibility), typeof(MyCustomControl), new UIPropertyMetadata(Visibility.Visible));
protected override Size MeasureOverride(Size constraint)
{
if (constraint.Width > 800)
{
ShowButton = Visibility.Collapsed;
}
else
{
ShowButton = Visibility.Visible;
}
double width = Math.Min(2000.0, constraint.Width);
double height = Math.Min(50.0, constraint.Height);
return new Size(width, height);
}
}
}
Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GridTest">
<Style TargetType="{x:Type local:MyCustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Border Background="LightCyan">
<TextBlock VerticalAlignment="Center" TextAlignment="Center">Custom Control</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
The problem can be reproduced as follows:
Make sure that the window is small, such that the right scroll button is visible.
Now maximize the window. => The right scroll button should become invisible.
Now restore the window to its original size. => The right scroll button should become visible again. (The problem is: the right scroll button remains invisible.)
EDIT: Just for information: I could reproduce the problem with both VS2010+.NET4.0 and VS2013+.NET4.51.
You need to dispatch your ShowButton visibility change back into the dispatch queue for this to work(give the application the time it needs for the render and in sequence) rather than do it directly in the MeasureOverride method.
So say I change your MeasureOverride to
protected override Size MeasureOverride(Size constraint) {
if (constraint.Width > 800) {
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ShowButton = Visibility.Collapsed));
} else {
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ShowButton = Visibility.Visible));
}
double width = Math.Min(2000.0, constraint.Width);
double height = Math.Min(50.0, constraint.Height);
return new Size(width, height);
}
You can see it works fine.
With the original code you posted, you can see that even when you maximize your Window, the Button on the right will be hidden but not actually Collapsed like that your setting it to, that would be again due to the same reason of the control not getting it's new size.
Also after restoring and having the Button remain hidden, if you re-size the Window by dragging it's size, you can see the button become Visible again.
By dispatching the Visibility change, if you have a break-point in your MeasureOverride function, you can see it gets called twice(once for Window size change and second size change due to Button being Hidden/Shown) essentially when maximizing/restoring the Window and thereby produce the correct dimension calculations and your desired output.
Having worked with some custom Panels before, I seem to remember that the value passed into the MeasureOverride method is not the actual size used... try moving that code to the ArrangeOverride method instead and see what happens.

Execute method after value from UIElement changes

Is it possible to subscribe to a Property of a specific UIElement in WPF?
I want to animate an UIElement as soon as the Heightvalue changes and add the new height to a list, but I don't see how I can subscribe to the HeightProperty?
Samplecode:
Something like this:
MainWindow.xaml:
<Window x:Class="BibVisualization.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="Red" Width="30" Grid.Row="0" x:Name="myBorder">
<TextBlock Text="Really really long text with wrapping, but the wrapping changes based on border's width"
Width="{Binding ElementName=myBorder, Path=Width}"
TextWrapping="Wrap" />
</Border>
<Button Grid.Row="1" Height="10"
Content="Make border bigger" Click="OnButtonClick" />
</Grid>
</Window>
MainWindow.xaml.cs
private void OnButtonClick(Object sender, RoutedEventArgs e)
{
myBorder.Width += 10;
//Bind to textblock's actualheight and execute OnHeightChange?
}
private int accumulatedChange;
private void OnHeightChange(Object sender, SomeEventArgs? e)
{
accumulatedChange -= e.OldValue (if possible);
accumulatedChange += e.NewValue;
}
I think you can use the SizeChanged-Event of the FrameworkElement class to do what you want. All UIElements such as Button or Textblock derive from that class and therefore provide the event.
The SizeChangedEventArgs passed to registered method contains information if height or width has changed and provide the new values.
If I understood properly you'd want to 'bind' to ActualHeight ?
Take a look at this link (http://meleak.wordpress.com/2011/08/28/onewaytosource-binding-for-readonly-dependency-property/) - it describes how it can be done using attached properties basically.
Also take a look at this answer of mine from the other day, which basically describes very similar problem.
https://stackoverflow.com/a/15367642/417747
(use link there to download the support code - you can bind to via Style or as described in the article - it's all similar thing)
What you'd need is to bind to ActiveHeight using the method described in the article, that changes your view-model's MyHeight property - handle it's set to get when the active height changes. Let me know if any questions.
Hope it helps.
You could use the DependencyPropertyDescriptor to add a ValueChangedHandler for the property:
DependencyPropertyDescriptor descriptor=DependencyPropertyDescriptor.FromProperty(UIElement.HeightProperty,typeof(UIElement));
descriptor.AddValueChanged(myUIElementToWatch, new EventHandler(OnHeightOfUiElementChanged));

Categories

Resources