Maintaining position of one of the controls inside ScrollViewer - c#

Let's say I have XAML code like this:
<UserControl x:Class="Sample.MyClass"
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"
mc:Ignorable="d"
d:DesignHeight="220" d:DesignWidth="750">
<ScrollViewer Width="730" Height="150" CanContentScroll="True" HorizontalScrollBarVisibility="Visible">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="680" />
</Grid.ColumnDefinitions>
<TextBox Width="50" Height="200" Grid.Row="0" Grid.Column="0" />
<TextBox Width="680" Height="200" Grid.Row="0" Grid.Column="1" />
</Grid>
</ScrollViewer>
</UserControl>
Now when I scroll to the right I'd like the first TextBox to be fully visible. In other words - I would like the horizontal scrolling (only horizontal scrolling) to apply only to the second TextBox and vertical scrolling to aplly to both. I can't put the first one outside of the ScrollViewer because then vertical scrolling would not work on it.
To give you a more real-life example:
In VisualStudio you have the text area where you can enter code. And on the left side there's a panel showing line numbers and code folding. If you scroll the text area vertically also the left panel is scrolling down or up. When you scroll the text area horizontally, only the text area is affected by it.

You can try to modify XAML as following:
<UserControl x:Class="Sample.MyClass"
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"
mc:Ignorable="d"
d:DesignHeight="220" d:DesignWidth="750">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="680" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0"
CanContentScroll="True"
VerticalScrollBarVisibility="Visible"
HorizontalAlignment="Stretch">
<TextBox />
</ScrollViewer>
<ScrollViewer Grid.Row="0" Grid.Column="1"
CanContentScroll="True"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox />
</ScrollViewer>
</Grid>
</UserControl>
Hope this may help.

I think what you want to do is svnc 2 scroll viewers,
You can do it with a little code behind voodoo, check this out
Synchronized scrolling of two ScrollViewers whenever any one is scrolled in wpf

Related

Can a WPF control be centered around its parent but also respect the position of its sibling control using xaml only?

I'm trying to center a modal popup-style control on a xaml screen relative to the entire screen, but also have the central control get pushed out of the way by a sibling (side panel) in the event the control is so large the two would intersect. This is feasible with codebehind, datanbindings, data triggers, custom controls and other less than totally elegant approaches, but is there a way to solve this problem out of the box with only xaml?
Here's a greatly simplified version of the problem which is a window with two rectangles. The orange rectangle is always 200px. The green rectangle is variably sized but never larger than 600px. Can we make the green rectangle center on the screen unless it is wide enough that it would collide with the orange rectangle in which case the green rectangle is laid out to the right of the orange rectangle (like a stackpanel)? The green and orange rectangles can be placed into any containers you want and the containers can be nested any way you want.
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Width="800">
<Rectangle Width="200" Fill="Orange" HorizontalAlignment="Left" ></Rectangle>
<Rectangle Width="300" Fill="Green" HorizontalAlignment="Center"></Rectangle>
</Grid>
</Window>
There is no built-in way to do this, but this approach works and is fairly simple:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Width="200" Fill="Orange" HorizontalAlignment="Left" />
<Rectangle Grid.Column="1" Width="300" Fill="Green" HorizontalAlignment="Center"/>
</Grid>
And if you prefer not to duplicate the width of the orange rectangle you can do this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="{Binding ElementName=OrangeRectangle, Path=ActualWidth}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="OrangeRectangle" Width="200" Fill="Orange" HorizontalAlignment="Left" />
<Rectangle Grid.Column="1" Width="300" Fill="Green" HorizontalAlignment="Center"/>
</Grid>

Wrappanel children not streching while resize the window

I am a beginner in using WPF and try to create a responsive application. I read many blogs and websites about the responsive design possibilities in WPF. Now I try to create a sample form. Please see the below image to find element structure in my form.
In this image first red box layout was maximized window and the second one was the resized or small screen layout
Red box is main container grid and it have to column (column definition)
Blue Boxes are two children of the main grid ( first blue box is a grid and second is wrappanel )
Green boxes in side the second blue box are the child elements of the wrap panel.
I am try to when I resize the window I need to change wrap panel contents like above image. I mean wrappanel orientation is horizontal, child contents are arranged in newline if the space not available on the right side.
please see the sample code below.
<Window x:Class="ResponsiveWPFApp.Responsive"
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:ResponsiveWPFApp"
mc:Ignorable="d"
Title="Responsive" Height="450" Width="800">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="400*"/>
</Grid.ColumnDefinitions>
<Grid Background="Yellow">
</Grid>
<WrapPanel x:Name="wrPanel" Background="Aqua" Grid.Column="1" Orientation="Horizontal" ItemWidth="Auto">
<WrapPanel.Children>
<Grid x:Name="gd1" Height="400" Width="Auto" HorizontalAlignment="Stretch" Background="Black" >
<TextBlock>terdf</TextBlock>
</Grid>
<Grid x:Name="gd2" Height="400" Width="Auto" Background="Green" >
<TextBlock >sdfdf</TextBlock>
</Grid>
</WrapPanel.Children>
</WrapPanel>
</Grid>
</Grid>
</Window>
In my code wrap panel contains two child elements, it's not filled the wrap panel available space.
You must decide: either you need to stretch the children, or you need WrapPanel. These are mutually exclusive options. The main purpose of the WrapPanel is to transfer the children to the next line (column) if they do not fit in the current line. If each child is stretched horizontally (vertically) to the limit, then each line will always have one child, and the functionality of the WrapPanel will lose its meaning. If you need to stretch children, you should use a Grid or UniformGrid. Here is an example of code with Grid in which children are stretched:
<Window x:Class="ResponsiveWPFApp.Responsive"
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:ResponsiveWPFApp"
mc:Ignorable="d"
Title="Responsive" Height="450" Width="800">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="400*"/>
</Grid.ColumnDefinitions>
<Grid Background="Yellow">
</Grid>
<Grid x:Name="grid" Background="Aqua" Grid.Column="1" VerticalAlignment="Top" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" x:Name="gd1" Height="400" HorizontalAlignment="Stretch" Background="Black" >
<TextBlock>terdf</TextBlock>
</Grid>
<Grid Grid.Column="1" x:Name="gd2" Height="400" Background="Green" >
<TextBlock >sdfdf</TextBlock>
</Grid>
</Grid>
</Grid>
</Grid>
</Window>
UniformGrid is a hybrid WrapPanel and Grid. Here is code snipet with UniformGrid:
<Window x:Class="ResponsiveWPFApp.Responsive"
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:ResponsiveWPFApp"
mc:Ignorable="d"
Title="Responsive" Height="450" Width="800">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="400*"/>
</Grid.ColumnDefinitions>
<Grid Background="Yellow">
</Grid>
<UniformGrid x:Name="grid" Background="Aqua" Grid.Column="1" Rows="1" VerticalAlignment="Top" >
<Grid x:Name="gd1" Height="400" HorizontalAlignment="Stretch" Background="Black" >
<TextBlock>terdf</TextBlock>
</Grid>
<Grid x:Name="gd2" Height="400" Background="Green" >
<TextBlock >sdfdf</TextBlock>
</Grid>
</UniformGrid>
</Grid>
</Grid>
</Window>
Take account on Rows="1" for the UniformGrid. Number of Rows is fixed for the UniformGrid. WrapPanel may have different number of rows.

WPF adds black bar to Window

I have built a WPF application with a variable width and height (xaml height and width definition for the window is set to Auto). Once the contents of the window are loaded, the width does not (well, is not supposed to) change. The height might change as items are removed or added from the list.
The background is a gradient, not an image.
After a period where the application is idling and isn't the top-most, when switching back to the application, the window is wider as a black bar was added, extending the window to the right.
.
(Before this black bar appears, the window's width is the section with the blue gradient background)
I've added a hidden menu item to the window and when the user presses Alt the menu appears. At first, I simply added the menu and opening it caused the window to re-render as menu the item suddenly became visible and added to the window's height. As the window was re-rendered, the black bar disappeared and the window was its original width.
I tried the following solutions by adding an event where Window.OnFocus calls a function which:
changes the width (adds 1 pixel and then removes 1 pixel)... but this doesn't seem to do anything.
checks the width of the window. If the width is larger than the expected width, a re-render is manually called. But no dice...
What is causing this black bar to appear? How can I prevent it from happening?
The biggest problem here is that I can't seem to consistently reproduce the problem... sometimes, the application will sit open, idling in the background for the whole day and this won't happen. Sometimes, I'll go out to lunch, come back and there it is...
The content is loaded dynamically upon starting up, and there's a webservice call initiated every few minutes to check for changes. At this stage of development and testing, there aren't all that many changes happening so that the ui items are simply static most of the time. Even so, this black bar will appear at times after the application idles for a while, as mentioned.
Per suggestion, here is the xaml for the application:
MainWindow:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:MyApp"
Title="MyApp"
Height="Auto" Width="390"
SizeToContent="WidthAndHeight"
WindowStyle="SingleBorderWindow"
WindowStartupLocation="Manual"
ResizeMode="CanMinimize">
<Grid Name="MainGrid" Style="{StaticResource Normal}">
<Grid.RowDefinitions>
<RowDefinition Height="18" />
<RowDefinition Height="65"/>
<RowDefinition Height="Auto" MinHeight="200"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="218" />
</Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="2"
[SETTINGS REMOVED FOR BREVITY]>
</Menu>
[ELEMENTS REMOVED FOR BREVITY]
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Height="Auto" Width="Auto" x:Name="ContentGrid"></Grid>
</Grid>
</Window>
MainPanel (which goes into the ContentGrid):
<UserControl x:Class="MyApp.MainPanel"
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"
Height="Auto" Width="Auto" FlowDirection="RightToLeft">
<Grid Margin="19,0,19,0">
<Grid.RowDefinitions>
<RowDefinition Height="65" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="75" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="85" />
<ColumnDefinition Width="85" />
</Grid.ColumnDefinitions>
<TextBlock Name="FirstHeader" Grid.Column="0" Grid.Row="0" Style="{StaticResource Header}" Text="***" />
<TextBlock Name="SecondHeader" Grid.Column="1" Grid.Row="0" Style="{StaticResource Header}">***<LineBreak />***</TextBlock>
<TextBlock Name="ThirdHeader" Grid.Column="2" Grid.Row="0" Style="{StaticResource Header}" Text="***" />
<StackPanel Name="MainStack" Grid.Row="1" Grid.ColumnSpan="3" />
<Button Name="ActionButton" Grid.ColumnSpan="3" Grid.Row="2" FlowDirection="LeftToRight" Style="{StaticResource NotInService}" Click="ActionButton_Click" />
</Grid>
</UserControl>
And the MainStack in the MainPanel is filled with these:
<UserControl x:Class="MyApp.mItem"
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"
mc:Ignorable="d"
d:DesignHeight="35" d:DesignWidth="348" FlowDirection="RightToLeft">
<Grid Margin="0,0,0,05">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="85" />
<ColumnDefinition Width="85" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Name="PanelActionButton" Style="{StaticResource PanelActionButton}" Click="PanelActionButton_Click">
<TextBlock Name="AnswerButtonText" Style="{StaticResource ButtonText}"></TextBlock>
</Button>
<Label Grid.Row="0" Grid.Column="1" Name="SecondCol" Style="{StaticResource SecondCol}" />
<Label Grid.Row="0" Grid.Column="2" Name="ThirdCol" Style="{StaticResource ThirdCol}" />
</Grid>
</UserControl>
I don't know if it's pertinent, but this is the way I've styled the background:
<Style TargetType="{x:Type Grid}" x:Key="Normal">
<Setter Property="Background">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="1" RadiusY="1">
<RadialGradientBrush.GradientStops>
<GradientStop Color="#00AEEF" Offset="0" />
<GradientStop Color="#034ea2" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="FlowDirection" Value="RightToLeft" />
</Style>
Some suggestions:
SizeToContent="WidthAndHeight"
could be changed to
SizeToContent="Height"
since you also defined the width to be 390.
Besides that, check if the window with the black area is still the expected width of 390.
Although it´s not in your posted code, check if elements could extend the width with it´s content (like images, long strings, etc.).
Another thing you should consider is to carefully choose your margins and widths (and heights) since they could easily sum up.
E.g. your using
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="85" />
<ColumnDefinition Width="85" />
</Grid.ColumnDefinitions>
twice and inside grids which each have a margin.
Also have a look at the menu you´re using. Since it´s using some visibility converter, my guess is, that it might generate a menu that could be more than 390 wide.
And finally... what´s with the
FlowDirection="RightToLeft"
If there´s no real need for it, delete it :)

New to WPF, how would I create these colored bars in my form?

I'm trying to ditch Windows Forms, and learn to use WPF professionally from now on. Pictured above is a form done in Windows Forms that I'd like to recreate in WPF.
Here is the XAML I have so far:
<Window x:Class="PizzaSoftware.UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="297" Width="466" >
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".20*"/>
<ColumnDefinition />
<ColumnDefinition Width=".20*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0">
<Backcolor?
</Rectangle>
</Grid>
</Window>
Is this even the right approach, I mean using a Rectangle. In my Windows Forms example, I used a Panel and gave it a .BackColor property.
What's the WPF way to achieve this?
Thank you.
Yes, your approach is just fine. Set the background color with the Fill property:
<Rectangle Grid.Column="0" Fill="Orange" />
<Rectangle Grid.Column="2" Fill="Orange" />
<Rectangle Grid.Column="0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Fill="Orange" />
Set the background of the window to your orange color. Then set the background of the grid to white, set the width of the grid so that it leaves space on either side, and set the grids HorizontalAlignment to center.
<Windows ....
Background="Orange>
<Grid Background="White" HorizontalAlignment="Center" Width="300">
...
</Grid>
</Window>
I'd skip the rectangle and set the background color on the grid itself, then set colors on components within to grid.
<Grid Background="Orange">
You can set the Background property of your grid to Orange and then add your control in the middle column and Set its Background property to White. Here is a sample code:
<Window x:Class="PizzaSoftware.UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="297" Width="466"
Height="297"
Width="466">
<Grid ShowGridLines="True" Background="Orange">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".20*"/>
<ColumnDefinition />
<ColumnDefinition Width=".20*" />
</Grid.ColumnDefinitions>
<!--Control with white background in second column-->
<GroupBox Background="White" Grid.Column="1">
<!-- Groupbox content here-->
</GroupBox>
</Grid>
</Window>

How to make items in a DockPanel expand to fit all available space in WPF?

I have a StackPanel containing a StackPanel and some other items. The first StackPanel has a vertical orientation, the the inner one has a horizontal orientation. The inner one has a TreeView and a ListView, I would like them to expand and fit the width of the window, which I set by the window and allow the user to change. I would also like the outer StackPanel to fit the height of the window. How do I do this?
Edit:
I've converted to using a DockPanel, and I've set the DockPanel.Dock properties correctly in each of the elements, and have disabled LastChildFill in both of the dockpanels, the layout still does not stretch.
The Code:
<Window x:Class="Clippy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="400" Width="600" MinHeight="400" MinWidth="600" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged">
<DockPanel Name="wrapperDockPanel" LastChildFill="False">
<Menu Height="22" Name="mainMenu" Width="Auto" DockPanel.Dock="Top" />
<ToolBar Height="26" Name="mainToolBar" Width="Auto" DockPanel.Dock="Top" />
<DockPanel Height="Auto" Name="contentDockPanel" DockPanel.Dock="Top" LastChildFill="False">
<TreeView Name="categoryTreeView" />
<ListView Name="clipListView" />
</DockPanel>
<StatusBar Height="23" Name="mainStatusBar" DockPanel.Dock="Top" />
</DockPanel>
</Window>
Use a DockPanel instead. StackPanel explicitly doesn't care about visible space, whereas DockPanel does all of it's size calculation based on available space.
Update:
In addition, in my experience, putting the body of the window into a View, and only having the View in the Window makes for a better Auto Size experience.
For some reason putting all of the children directly into the Window seems to not auto size very well.
Update 2:
I would remove the explicit DockPanel.Dock attribute from the element that you want to stretch (fill) the unused space.
This should do it - I set it up so that the TreeView and the ListView shared the main view 50/50; if you don't want that, set it to 'Auto' and '*' or something. Use "LastChildFill" to your advantage!
<Window x:Class="Clippy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="400" Width="600" MinHeight="400" MinWidth="600" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged">
<DockPanel LastChildFill="True">
<Menu Width="Auto" DockPanel.Dock="Top" />
<ToolBar Width="Auto" DockPanel.Dock="Top" />
<StatusBar DockPanel.Dock="Bottom" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<TreeView Name="categoryTreeView" Grid.Row="0" />
<ListView Name="clipListView" Grid.Row="1" />
</Grid>
</DockPanel>
</Window>
Set width and height properties to "auto"

Categories