How to implement Camera app style photo strip on WP7? - silverlight

I'm running into a number of problems creating an effect very similar to the photo strip in the Camera app.
All I want to do is display a row of grids that each have the same dimensions as the screen (whether in portrait or landscape). Already, I had to do something hacky and create dependency properties that the grids width and height properties bind to to maintain the aspect ratio.
And this works fine. But when I create a StackPanel for my strip and implement my navigation (or just zoom back with the z-index transform) I see that my StackPanel can't display larger than the screen dimensions (it's clipped to the size of just one grid). I thought I found a post describing this issue but I can't find it now - please post if you know which post I'm thinking of or if you know more about this limitation.
The only workaround I've found is to use a ScrollViewer, which is absolutely not the behavior I want, but it allows the StackPanel to be wider than the screen.
My real problem is with the ScrollViewer behavior - because I need to hop from grid to grid (just like the photo strip does) instead of freely scrolling, and as far as I can tell the HorizontalOffset is not an animatable property. I can force it to animate by calling ScrollToHorizontalOffset every 15 milliseconds, basically implementing my own easing effect manually. This seems like a huge hack, and the behavior is very glitchy (either I'm not getting the ManipulationCompleted event every time I expect it - at the end of every swipe action - or the built in inertia physics of ScrollViewer is interfering with my effect).
Does anyone know better workarounds for the issues I've run into, or a completely different way to get the experience of the Camera Photo strip in Silverlight?
I have considered using the Pivot control, but it isn't quite what I want (if I wanted each item to animate out completely before the next one comes in, instead of appearing to be all attached to one strip, there should be less constraining ways to achieve that). More importantly, the strip is just one of many effects I want to be able to do dynamically. I'd like to alternately have a CoolIris-like 3d-tilt, or a FlipPad style page turn. I believe if I could get my current setup working nicely it would be easy to implement these other effects (as themable transitions). Committing to a control like Pivot won't get me any closer to that vision.
Here is my XAML:
<Grid x:Name="LayoutRoot" Background="Transparent" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" HorizontalAlignment="Left" VerticalAlignment="Top">
<ScrollViewer x:Name="SlideScroller" VerticalScrollBarVisibility="Disabled" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Margin="0,0,0,-31" ScrollViewer.HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel x:Name="SlidePanel" Orientation="Horizontal" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid x:Name="Slide0" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
<Image x:Name="Photo0" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
</Grid>
<Grid x:Name="Slide1" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
<Image x:Name="Photo1" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
</Grid>
<Grid x:Name="Slide2" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
<Image x:Name="Photo2" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
</Grid>
</StackPanel>
</ScrollViewer>
</Grid>

It turns out the setup I described works pretty well if I just prevent the ScrollViewer from getting manipulated directly by the user and position it manually. This eliminates the physics effects that were causing most of the glitchiness I mentioned.
XAML
<ScrollViewer x:Name="SlideScroller" VerticalScrollBarVisibility="Disabled" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" ScrollViewer.HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel x:Name="SlidePanel" Orientation="Horizontal" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left">
</StackPanel>
</ScrollViewer>
<Rectangle x:Name="ScrollInterceptRect" Margin="0,0,0,-31" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" HorizontalAlignment="Left" VerticalAlignment="Top">
Codebehind
public MainPage()
{
InitializeComponent();
ScrollInterceptRect.MouseLeftButtonUp += new MouseButtonEventHandler(ScrollInterceptRect_MouseLeftButtonUp);
ScrollInterceptRect.MouseLeftButtonDown += new MouseButtonEventHandler(ScrollInterceptRect_MouseLeftButtonDown);
ScrollInterceptRect.MouseMove += new MouseEventHandler(ScrollInterceptRect_MouseMove);
}
//...
NavigationIndices navigationIndices = new NavigationIndices();
readonly double swipeThreshold = 80.0;
SwipeDirection swipeDirection;
bool tapCancelled;
Point swipeDelta;
Point swipeStartPosition;
double startScrollOffsetX;
void SlideScroller_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
swipeStartPosition = e.GetPosition(this);
startScrollOffsetX = SlideScroller.HorizontalOffset;
}
void ScrollInterceptRect_MouseMove(object sender, MouseEventArgs e)
{
Point touchPosition = e.GetPosition(this);
swipeDelta = new Point() { X = swipeStartPosition.X - touchPosition.X, Y = swipeStartPosition.Y - touchPosition.Y };
SlideScroller.ScrollToHorizontalOffset(startScrollOffsetX + swipeDelta.X);
// swipe right
if (swipeDelta.X > swipeThreshold)
{
swipeDirection = SwipeDirection.Left;
tapCancelled = true;
}
// swipe left
else if (swipeDelta.X < -swipeThreshold)
{
swipeDirection = SwipeDirection.Right;
tapCancelled = true;
}
}
void ScrollInterceptRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (swipeDirection == SwipeDirection.Left && navigationIndices.X < photos.Count - 1 && photos[navigationIndices.X] != null)
{
navigationIndices.X++;
}
// only go back when you aren't already at the beginning
else if (swipeDirection == SwipeDirection.Right && navigationIndices.X > 0)
{
navigationIndices.X--;
}
if (!tapCancelled)
{
// handle tap
}
else
{
animateScrollViewerToCurrentPhoto();
}
}
This is simplified a bit for clarity (I also use vertical swipe for something in my app and I omitted how I'm animating the ScrollViewer - probably worthy of it's own post).
I'd love to hear any improvements you can offer to this, or suggestions on better ways to implement it altogether. Perhaps extending the Panel Class or as a custom Behavior. Thoughts?

Related

Binding UserControl Width To A TextBox Input

It's my first time using the MVVM pattern and I have a bit of trouble understanding how everything ties together.
I have a UserControl with a Textbox element which should change the Width of said UserControl based on it's input.
I'm facing two problems:
For my idea to work, I need to change and bind to d:DesignWidth and my ColumnDefinition Width. How and where do I implement those changes? Based on my knowledge of MVVM the View (in this case my UserControl) is controlled by a ViewModel for said UserControl. Is it nessesary to implement one or is it possible to bind directly to both properties? I know I can name my ColumnDefinition with x:Name="MyColumnDefinition" but is the same possible for the actual UserControl Width?
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="170">
I have an ObservableCollection filled with two different UserControls and I want the Controls not to overlap when I display them. I use a ListBox element to display the ObservableCollection and implement the different UserControls over DataTemplates with a DataTemplateSelector. This works fine now but I'm worried if I dynamically change the Control Width that it will just overlap the next Control in the list. How do I ensure this won't happen?
Below is the code I have for now for the UserControl:
<Border Background="LightGray" CornerRadius="6">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Button VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="0"
BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DeleteCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DeleteCommandParameter}">
<Rectangle Width="8" Height="8" Fill="White">
<Rectangle.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_close}" Stretch="Fill" />
</Rectangle.OpacityMask>
</Rectangle>
</Button>
<TextBlock Grid.Column="1" Grid.Row="0" FontSize="12" Margin="0,4,0,18" Foreground="White" HorizontalAlignment="Center" Grid.RowSpan="2">Delay</TextBlock>
<TextBox Grid.Column="1" Grid.Row="1" Width="46" Margin="0,4,0,16" HorizontalAlignment="Center" Grid.RowSpan="2"
Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Delay.MiddlePosition, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Grid.Column="1" Grid.Row="2" FontSize="8" Margin="20,5,20,5" Foreground="Gray" HorizontalAlignment="Center">[s]</TextBlock>
</Grid>
</Border>
Edit:
ListBox-XAML to hold the other UserControls (I'm trying to build an Axis which can be filled with custom Positioning- and DelayControls:
<ListBox Name="Test" SelectionMode="Single" Grid.Column="1"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=BlockList}"
ItemTemplateSelector="{StaticResource BlockTemplateSelector}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
End result should look kind of like this, but with differently sized Positioning and Delay blocks:
Check this code will help you to set width of one control to other control.
<Border>
<Grid x:Name="grv">
<TextBox Width="{Binding ElementName=grv,
Path=ActualWidth}">
</TextBox>
</Grid>
</Border>
I struggeled quite a while to figure out how to address your issue and even though I am not completely happy with the outcome, I managed to solve it.
First I create a ListBox with a DummyList, which contains Model-Objects called 'UserControlModel' with a singe Property 'modelWidth', from which I create my UserControls with their default size.
<ListBox ItemsSource="{Binding SimpleList, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Width="Auto" Height="200">
<ListBox.ItemTemplate>
<DataTemplate>
<osv:UserControl1 Width="{Binding modelWidth}" OnTextValidated="UserControlSizeChangeEvent"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
OnTextValidated is a RoutedEvent to hand up the KeyDown-Event from my Textbox to my Window(which i will show later)
The UserControl1.xaml then adds the textbox
<TextBox Width="60" Height="30" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" KeyDown="TextBox_KeyDown" Text="{Binding myText, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBox>
with a KeyDown event and a textbinding.
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)//press enter to change
{
if (double.TryParse(myText, out double d) == true)
{
if (d >= 50) //minimum width, so i won't set it to 0 by accident
{
myWidth = d; //set the new Width
OnTextValidated(this, new RoutedEventArgs()); //tell window to resize the UserControl
}
}
}
}
Once I validated the new size is neither wrong nor too small i call a RoutedEventHandler
private RoutedEventHandler m_OnTextValidated;
/// <summary>
///
/// </summary>
public RoutedEventHandler OnTextValidated
{
get { return m_OnTextValidated; }
set
{
m_OnTextValidated = value;
OnPropertyChanged("CustomClick");
}
}
now i can bind on this like shown above.
next i have to do is passing down my event from the xaml.cs to the MinWindowViewModel
//Variables
private MainWindowViewModel m_DataContext;
//Constructor
DataContext = new MainWindowViewModel ();
m_DataContext = (MainWindowViewModel)this.DataContext;
private void UserControlSizeChangeEvent(object sender, RoutedEventArgs e)
{
if (m_DataContext != null)
{
m_DataContext.UserControlSizeChangeEvent(sender, e);
}
}
and finally update the size of my object in my code behind
public void UserControlSizeChangeEvent(object sender, RoutedEventArgs e)
{
UserControl1 uc = sender as UserControl1;
uc.Width = uc.myWidth;
}
Note: Although this works quite fine i'd be much happier if i found a way to change the width of the model instead of the object, so they would still have the same width in case the list is redrawn.
I also didn't use a MVVM pattern for my UserContrl, so you'll have to pass the event from your xaml.cs to your viewmodel first like I did for the MainWindow

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.

Horizontal dashed line stretched to container width

I have a layout contained within a ScrollViewer in which I need to draw a horizontal dashed line that stretches to the full width of the container. The closest I've managed is the following
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel>
<Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
<Line HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Stroke="Black"
X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
StrokeDashArray="2 2" StrokeThickness="1" />
</StackPanel>
</ScrollViewer>
This nearly works, however once the container (in my case a window) has been enlarged, the line doesn't shrink back down to the appropriate size when the container is sized back down. The below is the screenshot of the same window after I have horizontally sized the window up and down.
Note that the fact that the line is dashed is important as it means that solutions that involve stretching the line don't work (the dashes appear stretched).
I know that this is because of the X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" binding (by design the line is always the widest thing in the scrollable region, so when I size the window down the scrollable region the line defines the width of the scrollable region), however I can't think of a solution.
How can I fix this problem?
Screenshot of why using ViewportWidth doesn't work
I realised that what I needed was for the Line to ask for zero space during the measure step of layout, but then use up all the available space during the arrange step. I happened to stumble across the question Make WPF/SL grid ignore a child element when determining size which introduced the approach of using a custom decorator which included this logic.
public class NoSizeDecorator : Decorator
{
protected override Size MeasureOverride(Size constraint) {
// Ask for no space
Child.Measure(new Size(0,0));
return new Size(0, 0);
}
}
(I was hoping that some existing layout control incorporated this logic to avoid having to write my own layout logic, however the logic here is so simple that I'm not really that fussed). The modified XAML then becomes
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel>
<Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
<local:NoSizeDecorator Height="1">
<Line Stroke="Black" HorizontalAlignment="Stretch"
X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
StrokeDashArray="2 2" StrokeThickness="1" />
</local:NoSizeDecorator>
</StackPanel>
</ScrollViewer>
This works perfectly
You may put a very long Line in a left-aligned Canvas with zero Width and ClipToBounds set to false.
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel>
<Button Width="400" Height="50" VerticalAlignment="Top" Margin="10" />
<Canvas HorizontalAlignment="Left" Width="0" ClipToBounds="False">
<Line Stroke="Black" StrokeDashArray="2 2" X2="10000"/>
</Canvas>
</StackPanel>
</ScrollViewer>

Creating a sidebar - flyout like Windows desktop app in WPF

what i am trying to do is create a Desktop application in WPF whose UI is such that a small icon will remain fixed in the center of the left edge of screen and on click(or maybe hover) will slide open a sidebar(like the google desktop bar) running along the left edge of the screen (fixed position, cannot be moved).
do note that what i'm asking for might be like an appbar but i do not want the desktop icons along the left edge to be moved as it happens with an appbar i.e. i do not want it to hog up the desktop spacce....can anyone please suggest me a way out ??
I have implemented a partial solution using this, but i cant get the slide animation and fixed position to workout
Something like this could work:
then of course you could create a slide in animation for the sidebar. This shows (partial) transparency and the switching principle.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStyle="None" Topmost="True" WindowState="Maximized"
AllowsTransparency="True" Background="Transparent">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Name="rect" Width="100" VerticalAlignment="Stretch" Fill="#99000000" Visibility="Collapsed" />
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Width="32" Height="32" FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Right" Background="White" Click="Button_Click">></Button>
</Grid>
</Window>
C#:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (rect.Visibility == System.Windows.Visibility.Collapsed)
{
rect.Visibility = System.Windows.Visibility.Visible;
(sender as Button).Content = "<";
}
else
{
rect.Visibility = System.Windows.Visibility.Collapsed;
(sender as Button).Content = ">";
}
}
Based on this answer and more answers on this site I made a side bar, I liked the result so i made a repo.
https://github.com/beto-rodriguez/MaterialMenu
you can install it from nuget too.
here is an example
<materialMenu:SideMenu HorizontalAlignment="Left" x:Name="Menu"
MenuWidth="300"
Theme="Default"
State="Hidden">
<materialMenu:SideMenu.Menu>
<ScrollViewer VerticalScrollBarVisibility="Hidden">
<StackPanel Orientation="Vertical">
<Border Background="#337AB5">
<Grid Margin="10">
<TextBox Height="150" BorderThickness="0" Background="Transparent"
VerticalContentAlignment="Bottom" FontFamily="Calibri" FontSize="18"
Foreground="WhiteSmoke" FontWeight="Bold">Welcome</TextBox>
</Grid>
</Border>
<materialMenu:MenuButton Text="Administration"></materialMenu:MenuButton>
<materialMenu:MenuButton Text="Packing"></materialMenu:MenuButton>
<materialMenu:MenuButton Text="Logistics"></materialMenu:MenuButton>
</StackPanel>
</ScrollViewer>
</materialMenu:SideMenu.Menu>
</materialMenu:SideMenu>

Rectangle Stretch Property Behavior

Can someone explain my misunderstanding of the Rectangle's Stretch property when setting it to NONE? According to MSDN, the definition for this value is "The content preserves its original size." Look at the code below. When I set Stretch = NONE on the fourth rectangle, it disappears.
<Grid Margin="20">
<Rectangle Height="Auto" Width="2" HorizontalAlignment="Left" VerticalAlignment="Stretch" Fill="Black"/>
<Rectangle Height="2" Width="10" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Black"/>
<Rectangle Height="2" Width="10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Fill="Black"/>
<Rectangle Stretch="None" Name="Right" Height="Auto" Width="2" HorizontalAlignment="Right" VerticalAlignment="Stretch" Fill="Black"/>
<Rectangle Height="2" Width="10" HorizontalAlignment="Right" VerticalAlignment="Top" Fill="Black"/>
<Rectangle Height="2" Width="10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Fill="Black"/>
</Grid>
Why is this happening? This code is an excerpt from a paging control I'm using on a custom chart. I am wrapping the paging control in a ViewBox to allow auto-resizing, but I do not want my border markers resizing (the example above is what the page border markers look like).
Rectangle class uses private _rect field for rendering.
Here is the code of the Rectangle.OnRender:
protected override void OnRender(DrawingContext drawingContext)
{
...
drawingContext.DrawRoundedRectangle(base.Fill, pen, this._rect, this.RadiusX, this.RadiusY);
}
Now let's look at the ArrangeOverride method:
protected override Size ArrangeOverride(Size finalSize)
{
...
switch (base.Stretch)
{
case Stretch.None:
{
this._rect.Width = (this._rect.Height = 0.0);
break;
}
...
}
...
return finalSize;
}
It seems that when Stretch in None there is only empty rectangle. You may see it only adding a stroke. Maybe it's a bug.

Resources