Vertically expand textbox automatically when text is too long - wpf

I am trying to mimic the Sticky Notes application in Windows 7. In the original application, if you type text into a Sticky Note and the text becomes too large (vertically, as in number of lines) to fit in the window, the window automatically expands vertically, one line at a time, to allow for more room. In other words, where in a normal Textbox a vertical scrollbar would appear and the text would scroll down (so that the first line becomes invisible), in the Sticky Notes the textbox expands exactly enough to fit the text so that no scrollbar appears. The scrollbar still appears when you manually resize the window afterwards, of course.
If you have Windows 7 just open the Sticky Notes application and type a few lines in the sticky note until it enlarges.
I am trying to mimic this effect but I'm having no luck. The problem seems that the actual Window should resize, not just the Textbox (I don't think WPF works this way, that a resize of a child element can 'force' the parent element to become larger? At least not for a Window, right?).
The contents of the Window at this point are such:
<Window Background="Transparent" BorderBrush="Transparent">
<!-- Transparent border to draw dropshadow on -->
<Border Background="Transparent" BorderBrush="Transparent">
<!-- Grid with UI elements -->
<Grid Margin="5" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="27" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Stickynote header -->
<Border ... />
<!-- Content -->
<Border Grid.Row="1">
<TextBox Text="{Binding ContentText}" ... />
</Border>
</Grid>
</Border>
</Window>
Does anybody know how I can achieve this effect? Thanks!

Try the Window Property SizeToContent="Height"
Sample
<Window ...
MaxHeight="500"
SizeToContent="Height">
<Border Background="Transparent" BorderBrush="Transparent">
<Grid Margin="5" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="27" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="1">
<TextBox AcceptsReturn="True" MinHeight="100"/>
</Border>
</Grid>
</Border>
</Window>
Edit
To use it with the TransparentWindow you posted, add transparentWindow.SizeToContent = SizeToContent.Manual in OnDragDelta (TransparentWindow.cs)
private static void OnDragDelta(object sender, DragDeltaEventArgs e)
{
TransparentWindow transparentWindow = (TransparentWindow)sender;
Thumb thumb = e.OriginalSource as Thumb;
transparentWindow.SizeToContent = SizeToContent.Manual;
if (thumb != null && transparentWindow.WindowState == WindowState.Normal)
{
//...
}
}

Related

WPF button position dependent on another element

I would like to place my button always on the middle of the bottom frame of ScrollViewer. I am going to change both the size of window, and a size of ScrollViewer, but I want my button to be always as on the pictures.
Owing to the fact that i am following MVVM, I have just xaml. Basically, I would like to bind (live) the button top position from the pattern:
button.top = (scrollViewer.top + scrollViewer.height) - button.height/2
I would be grateful for your suggestions.
[EDIT] I forgot to add that all other controls are in grid rows and columns.
You can try to use Grid to achieve that. If you need to change the ScrollViewer size, just change ScrollGrid Grid size instead. To overlap bottom or top content, you can use negative margins for the button.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Border Background="Red"/>
<Border Background="Red" Grid.Row="2"/>
<Grid x:Name="ScrollGrid" Grid.Row="1">
<ScrollViewer></ScrollViewer>
<Button Width="100" Height="100" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</Grid>
</Grid>

WPF ScrollViewer gives my listview (in a grid row) the whole screen instead of a portion

The main grid on my usercontrol has 3 rows. The top row is a data-bound listvew that takes about 60% of the whole window (there's more data rows than can be displayed and the listview automatically displays a scroll bar - This is good). Second row is a gridsplitter and the 3rd takes up the rest of the display.
This 3rd row has a grid wrapped in a border and also contains a textbox that wraps & can grow larger. When it does, it sometimes pushes the buttons at the very bottom off the screen, so I thought that if I wrapped a ScrollViewer around the main grid that I'd keep my 3 rows on the screen in the same proportion with the existing listview scrollbar left intact and then just get an additional scrollbar on the right that would let me scroll down to see the buttons if the 3rd row grew too tall (like you do on this browser page with scroll bars for the code & the page scroller too.
What happens instead, is that the first row with the listview has expanded vertically to take the whole screen and I can't see rows 2 or 3 at all until I've scrolled to the end of all the items in the listview. I've tried various combinations of hardcoding row heights (bad, I know) 'Auto' & '*' to now avail.
Is there a way to accomplish what I'm trying? I didn't think I'd have to (and down't want to) re-engineer the whole screen for this.
Thanks, I'm new to WPF & it's fun but very frustrating at times!
I'm posting some XAML, below, but I'm not sure it will help.
<ScrollViewer>
<Grid Name="grdEvents" HorizontalAlignment="Center" >
<Grid.RowDefinitions>
<RowDefinition Height="60*" />
<RowDefinition Height="10" />
<RowDefinition Height="30*"/>
</Grid.RowDefinitions>
<ListView SelectionChanged="lvActivities_SelectionChanged" MouseDoubleClick="ListView_MouseDoubleClick" Grid.Row="0" Name="lvActivities" HorizontalAlignment="Stretch" LVLO:ListViewLayoutManager.Enabled="True" >
<!--ItemContainerStyle="{StaticResource SelectedItem}" MouseEnter="lvActivities_MouseEnter" -->
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
...
</ListView>
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch"> </GridSplitter>
<Border Grid.Row="2" CornerRadius="20"
BorderBrush="Gray"
Background="White"
BorderThickness="2"
Padding="8">
<Grid Name="wpCurrentRow" DataContext="{Binding ElementName=lvActivities, Path=SelectedItem}" Grid.Row="2" Background="{StaticResource ResourceKey=MyBackGroundBrush}">
I don't think you can accomplish what you want with relative row sizes. What you can do, however, is manually set a proportional height to the top row any time the ScrollViewer's size changes. But since you also have a splitter, you will want to stop doing this once the user adjusts the height manually. Take a look at this:
XAML:
<Window x:Class="StackOverflow.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ScrollViewer x:Name="_scrollViewer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="_mainRow" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView />
<GridSplitter x:Name="_splitter"
Grid.Row="1"
Height="5"
HorizontalAlignment="Stretch"
ResizeDirection="Rows"
ResizeBehavior="PreviousAndNext"
MouseDoubleClick="OnSplitterMouseDoubleClick" />
<Grid Grid.Row="2" />
</Grid>
</ScrollViewer>
</Grid>
</Window>
Code behind:
public partial class MainWindow
{
private bool _shouldUpdateGridLayout;
public MainWindow()
{
InitializeComponent();
EnsureGridLayoutUpdates();
}
private void EnsureGridLayoutUpdates()
{
if (_shouldUpdateGridLayout)
return;
_scrollViewer.SizeChanged += OnScrollViewerSizeChanged;
_splitter.DragCompleted += OnSplitterDragCompleted;
_shouldUpdateGridLayout = true;
}
private void CancelGridLayoutUpdates()
{
if (!_shouldUpdateGridLayout)
return;
_scrollViewer.SizeChanged -= OnScrollViewerSizeChanged;
_splitter.DragCompleted -= OnSplitterDragCompleted;
_shouldUpdateGridLayout = false;
}
private void UpdateGridLayout()
{
_mainRow.Height = new GridLength(2 * _scrollViewer.ActualHeight / 3);
}
private void OnScrollViewerSizeChanged(object s, SizeChangedEventArgs e)
{
UpdateGridLayout();
}
private void OnSplitterDragCompleted(object s, DragCompletedEventArgs e)
{
CancelGridLayoutUpdates();
}
private void OnSplitterMouseDoubleClick(object s, MouseButtonEventArgs e)
{
EnsureGridLayoutUpdates();
UpdateGridLayout();
// Handle the event to prevent DragCompleted from being raised
// in response to the double-click.
e.Handled = true;
}
}
Note that I chose to restore both the default size and automatic size management when the user double-clicks the splitter. It's not the prettiest solution, but it beats using fixed heights. Feel free to encapsulate the behavior in custom panel/control.
Instead of using proportions, use hardcoded heights on the rows. Change the 60* to 60 for the first row.
Also just for the sake of experimentation you could try this:
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="ListBox1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Grid.Row="0" Background="Blue"/>
<ScrollViewer Grid.Row="1">
<StackPanel >
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click" />
<TextBox>Hello </TextBox>
</StackPanel>
</ScrollViewer>
It adds a scroll viewer just to the second row and you then use a stack panel to store the rest of the elements. It makes it look a bit better imo too.
The elements were just added for example; replace them with your own.

How to add a buttom that is always visible after a scrollviewer?

I have a ScrollViewer which includes a lot of content (datagrids, stackpanels, textboxes, labels, etc...), and outside of it I want to add a button (PRINT), and it is important that the button is not part of the ScrollViewer. My goal is that the top 90% of my screen is the scrollviewer and the bottom 10% is a "frozen panel" that always shows the PRINT button, and this should remain true when maximized and minimized.
After having a lo of problems with 'the property content is set more then once' I realized I need to add both my ScrollViewer and the Button inside another container, so far the only one that seems to work is GRID - but honestly after you read this if you have anything else to recommend I am open to suggestions, I only used GRID because it seemed to almost give me what I wanted.
This is my code right now:
[Code]
<Window DataContext="{Binding PrintView, Source={StaticResource Locator}}" Width="900">
<Grid Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer Name="PrintView" Grid.Row="0" Height="Auto">
<StackPanel>
... a LOT of stuff ...
</StackPanel>
</ScrollViewer>
<Button Content="Print"
Margin="0,20,0,20"
Height="50"
Width="150"
FontSize="24"
FontWeight="Bold"
Grid.Row="1"
/>
</Grid>
</Window>
[Code]
When done like this my ScrollViewer doesn't have a Scrollbar so I see the first page but I cannot scroll down, also there is no PRINT button seen
One interesting test was to change the following:
<ScrollViewer Name="Apercu" Grid.Row="0" Height="600">
Now I see my scrollbar again (and I can scroll) and my PRINT button is beneth it and always visible (this is almost perfect) but when I maximumize my window the ScrollViewer stays 600 of height and as such well it doesn't acctually maximize (everything below the PRINT button is just white).
Any ideas? Is there another way I could specify my HEIGHTS or is there a different control I should be using (not GRID)?
Thanks,
Found it ...
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
And remove height from ScollViewer

Is there any way to use the mouse to drag/resize a canvas?

I am using a canvas with an Expander embedded within it, so that when the expander is expanded, it will overlay the controls below.
<Canvas Grid.Row="0" Panel.ZIndex="99">
<Border Width="450" BorderThickness="1">
<Expander etc />
</Border>
</Canvas>
<OtherControls Grid.Row="1"/> etc
Instead of setting the size of the canvas, is there a way to allow the user to drag size it instead?
here's a thought: Put everything in the grid. Let the grid resize itself automatically, put canvas in the grid (make sure it takes up the whole grid) so that it will follow the parent's size:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Canvas Background="Transparent" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Border Width="450" BorderThickness="1">
<Expander etc />
</Border>
<OtherControls Grid.Row="1"/>
</Grid>
not sure how relevant this might be, but check out this post. Maybe it'll give you some ideas (I wrote that 4 years ago, way too long to remember specifics, but code compiles and runs):
http://denismorozov.blogspot.com/2008/01/how-to-resize-wpf-controls-at-runtime.html

Part of text is not shown after animation changes StackPanel location

I have a windows phone 7 project with a page that has the following structure:
I have scrollviewer and a stackpanel inside called main stackpanel
in the main stackpanel I have couple of stackpanels with horizontal orientation
the horizontal stackpanels has several textblocks each containing a single letter
sometimes the horizontal stackpanel contains more letters than it fits to the phone screen. From time to time I need to show a group of letters that are outside of the screen.
To be able to show these letters, I need to move the stackpanel, so the letters are becoming visible on screen. When I am finished the animation, the letters that are moved in to the screen are not visible at all.
What should I do to make it visible?
I added a screenshot and a pseudo xaml of my page to demonstrate the structure. I hope this will help!
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Disabled">
<StackPanel x:Name="MainStackPanel" Margin="12,0,12,-177" Grid.RowSpan="2" RenderTransformOrigin="0.5,0.5" >
<StackPanel x:Name="stackPanel" Orientation="Horizontal" Width="580" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<CompositeTransform/>
</StackPanel.RenderTransform>
<TextBlock Style="{StaticResource LetterStyle1}" VerticalAlignment="Stretch"><Run Text="e"/></TextBlock>
<TextBlock Style="{StaticResource LetterStyle1}" VerticalAlignment="Stretch"><Run Text="u"/></TextBlock>
... many more textblock each containing a letter
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource LetterStyle1}" VerticalAlignment="Stretch"><Run Text="e"/></TextBlock>
<TextBlock Style="{StaticResource LetterStyle1}" VerticalAlignment="Stretch"><Run Text="u"/></TextBlock>
... many more textblock each containing a letter
</StackPanel>
Fortunately, I managed to find the solution. The problem is that some of the horizontal stackpanel were wider then MainStackPanel.
Changing the MainStackPanel and the ScrollViewer to be as wide as the widest horizontal solves the problem.
<ScrollViewer VerticalScrollBarVisibility="Disabled" Margin="0,0,-106,0">
<StackPanel x:Name="MainStackPanel" Margin="12,0,12,-177" Grid.RowSpan="2" RenderTransformOrigin="0.5,0.5" >

Resources