How to make WPF Expander expand upwards while keeping the header fixed - wpf

I am trying to create an Expander where the header stays fixed and the contents appear above the header overlaying any other controls above. Setting ExpandDirection="Up" and putting the Expander inside a Canvas achieves half of this - the contents expands up relative to the header and it overlays other controls (albeit below), but the header moves downwards.
Is there any way to do this but keeping the header in a fixed position so that the contents ends up overlaying controls above?
This is what I have poduced so far:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="900" Width="1100">
<StackPanel>
<StackPanel Margin="20,0,0,0">
<RadioButton Content="Choice One"/>
<RadioButton Content="Choice Two"/>
<RadioButton Content="Choice Three"/>
<RadioButton Content="Choice Four"/>
<RadioButton Content="Choice Five"/>
<RadioButton Content="Choice Six"/>
</StackPanel>
<Canvas MinHeight="25" Panel.ZIndex="99">
<Expander Header="This must stay fixed" ExpandDirection="Up" Width="175">
<Grid Background="Cornsilk">
<Grid.BitmapEffect>
<DropShadowBitmapEffect />
</Grid.BitmapEffect>
<TextBlock TextWrapping="Wrap" Margin="5">
This must expand upwards, not downwards.
The header must remain exactly where it is.
This TextBlock must appear above the header
and overlay the top radio buttons instead.
</TextBlock>
</Grid>
</Expander>
</Canvas>
<StackPanel Margin="20,0,0,0">
<RadioButton Content="Choice One"/>
<RadioButton Content="Choice Two"/>
<RadioButton Content="Choice Three"/>
<RadioButton Content="Choice Four"/>
<RadioButton Content="Choice Five"/>
<RadioButton Content="Choice Six"/>
</StackPanel>
</StackPanel>
</Window>

You can use ToggleButton and Popup instead of Expander:
<Canvas MinHeight="25" Panel.ZIndex="99">
<ToggleButton x:Name="toggle" Content="This must stay fixed" Width="175" />
<Popup Placement="Top" PlacementTarget="{Binding ElementName=toggle}"
IsOpen="{Binding ElementName=toggle, Path=IsChecked}">
<Grid Background="Cornsilk" Width="175">
<Grid.BitmapEffect>
<DropShadowBitmapEffect />
</Grid.BitmapEffect>
<TextBlock TextWrapping="Wrap" Margin="5">
This must expand upwards, not downwards.
The header must remain exactly where it is.
This TextBlock must appear above the header
and overlay the top radio buttons instead.
</TextBlock>
</Grid>
</Popup>
</Canvas>

The other way is to use CheckBox control instead of ToggleButton. It is easy to hide the box using e.g. StackPanel with negative margin value. Also, you don't need to care about borders etc. It is much easier in some cases.

Related

Is it possible to make the contents of a ScrollViewer scroll with the mouse wheel?

I'm on a team of developers working on a new WPF application. We've got a usercontrol on the MainWindow which has a ScrollViewer. We've got VerticalScrollBarVisibility="Auto" set, which works fine, if the contents within the ScrollViewer become larger than the available space for the ScrollViewer. However, what doesn't happen, which I wish would happen, is the user cannot scroll the contents with the mouse wheel. In other places where we're using DataGrids, if the content of the DataGrid is larger than can be displayed vertically, the user can scroll the contents vertically using their mouse wheel. But they cannot do so within the ScrollViewer in the user control on MainWindow. If it's possible to make the contents scroll vertically with the mouse wheel, how is it done?
Here's a snippet of the ScrollViewer, with content removed for brevity:
<ScrollViewer
Grid.Row="1"
Margin="0,20,0,0"
Width="300"
VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<StackPanel>
<Expander
x:Name="PeopleSideExpander"
IsExpanded="{Binding PeopleSideIsExpanded}"
IsEnabled="True">
<Expander.Header>
<Button
x:Name="tilePeopleSide"
Content="People Side"
Command="{Binding PeopleSideExpanderCommand}"
Cursor="Hand" />
</Expander.Header>
<ListBox>
<ListBoxItem>
<Button Name="tileAgency" Content="Agencies" Command="{Binding ViewAgencyCommand}" />
</ListBoxItem>
<ListBoxItem>
<Button Name="tilePerson" Content="Personnel" Command="{Binding ViewPersonCommand}" />
</ListBoxItem>
<!-- Several other ListBoxItems removed for brevity -->
</ListBox>
</Expander>
<Expander
x:Name="HardwareSideExpander"
Grid.Row="1"
IsExpanded="{Binding HardwareSideIsExpanded}"
IsEnabled="True">
<Expander.Header>
<Button
x:Name="tileHardwareSide"
Content="Hardware Side"
Command="{Binding HardwareSideExpanderCommand}"
Cursor="Hand" />
</Expander.Header>
<ListBox>
<ListBoxItem>
<Button Name="tileEvent"
Content="Event"
IsEnabled="False"
Foreground="LightGray"
Command="{Binding ViewEventCommand}" />
</ListBoxItem>
<ListBoxItem>
<Button Name="tileInstrument"
Content="Instrument"
IsEnabled="False"
Foreground="LightGray"
Command="{Binding ViewInstrumentCommand}" />
</ListBoxItem>
<ListBoxItem>
<Button x:Name="tileHardwareProficiency"
Content="Proficiencies"
Visibility="Collapsed"
ToolTip="Not Implemented" />
</ListBoxItem>
<ListBoxItem>
<Button x:Name="tileSolution"
Content="Proficiency Solutions"
Command="{Binding ViewSolutionCommand}" />
</ListBoxItem>
</ListBox>
</Expander>
</StackPanel>
</ScrollViewer>

Border that bulges near mouse pointer

I have a ListView populated with ListViewItems, like navigation bar style menu options.
I would like to create the effect of the ListViewItem border "bulging" like in the Mail application on Win10 as shown in this picture
The border "bulges" (fisheye?) as the mouse moves left and right along the ListViewItem. It's a nice effect that I'd like to replicate but am struggling to even get started on how to tackle this.
My list items are bound to a content control which is templated with a ControlTemplate. The ItemsControl ItemTemplate is a DataTemplate containing an image and a Textbox wrapped inside a button control, like this .... (although I suspect how it's created is probably not relevant to my question)
<ContentControl Content="{Binding Path=MenuModel.AllMenuItems}"
x:Name="menuCtrl1"
Template="{StaticResource MenuListItemControlTemplate}"
VerticalAlignment="Top" />
...
<ControlTemplate x:Key="MenuListItemControlTemplate" TargetType="ContentControl">
<Border Background="Transparent" >
<ScrollViewer Margin="{StaticResource DefaultNoMargin}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{TemplateBinding Content}"
ItemTemplate="{StaticResource MenuListItemDataTemplate}" />
</ScrollViewer>
</Border>
</ControlTemplate>
...
<DataTemplate x:Key="MenuListItemDataTemplate" DataType="local:SingleMenuItem">
<Button Command="{Binding MenuCommand}" Style="{StaticResource MenuButtonStyle}" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding MenuIconSource}" Style="{StaticResource MenuItemImage}" />
<TextBox Text="{Binding DisplayText}" Style="{StaticResource MenuItemStyle}" />
</StackPanel>
</Button>
</DataTemplate>
Any tips on how to achieve it or even where to start would be gratefully received.

Fill visible border in StackPanel

I put 2 borders inside stackpanel and it should be one border is visible at a time .
I like the visible border to fill stackpanel
<StackPanel>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding Title_RoleVisibilty,
Converter={StaticResource WriteRoleVisibilityToVisibilityConverter}}">
<TextBox Text="{Binding Title}" MaxLength="50" />
</Border>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding Title_RoleVisibilty ,
Converter={StaticResource ReadRoleVisibilityToVisibilityConverter}}">
<TextBlock Text="{Binding Title }" />
</Border>
</StackPanel>
I used stackpanel /Dockpanel and both not filled the desired border filled
Please advice
The duplication in the borders is adding unnecessary problems. Limit the situation to only one border. Hence you don't have to focus on the border visibility, with only one border and you can the put in styles(?) maybe to focus on the difference needed for the target parameters instead.
For example to solve the problem I have taken your example and added a new binding on MaxLength size which will adjust as needed; for that appears to be the only change required.
<StackPanel>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center" >
<TextBox Text="{Binding Title}" MaxLength="{Binding MaxSize, Converter=???" />
</Border>
</StackPanel>

RibbonCheckBox alignment in a RibbonGroup

<RibbonWindow x:Class="xxx.yyy"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
<Ribbon x:Name="mainRibbon" Grid.Row="0" >
...
<RibbonTab Name="ComparisonTab" HorizontalContentAlignment="Left" Header="Comparison" >
<!--<StackPanel Orientation="Vertical" HorizontalAlignment="Left">-->
<RibbonGroup HorizontalContentAlignment="Left" Header="Images" >
<RibbonCheckBox HorizontalAlignment="Left" Width="200" IsChecked="{Binding CompInfo1Check}" Label="{Binding CompInfo1Text}" />
<RibbonCheckBox HorizontalAlignment="Left" Width="200" IsChecked="{Binding CompInfo2Check}" Label="{Binding CompInfo2Text}" />
<RibbonCheckBox HorizontalAlignment="Left" Width="200" IsChecked="{Binding CompInfo3Check}" Label="{Binding CompInfo3Text}" />
</RibbonGroup>
<!--</StackPanel>-->
</RibbonTab>
I have some checkboxes with a dynamic text (labels) and I want to have them left aligned within a ribbon group.
I've tried every imaginable combination of Horizontal(Content)Alignment on them and on their parents. I tried to put them into a container (StackPanel, Grid). I even tried to set a label as a separate element.
The checkboxes stubbornly remain centered within a ribbon group. How do I align them horizontally to the left?
I've found a workaround in using standard check boxes inside a ribbon. There is no problem with their alignment. They could be styled to look as the ribbon check boxes, if required.
So, the modified xaml is:
<RibbonTab Name="ComparisonTab" HorizontalContentAlignment="Left" Header="Comparison" >
<RibbonGroup Header="Images" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<CheckBox Height="25" IsChecked="{Binding CompInfo1Check}" Content="{Binding CompInfo1Text}" />
<CheckBox Height="25" IsChecked="{Binding CompInfo2Check}" Content="{Binding CompInfo2Text}" />
<CheckBox Height="25" IsChecked="{Binding CompInfo3Check}" Content="{Binding CompInfo3Text}" />
</StackPanel>
</RibbonGroup>
</RibbonTab>
EDIT
Probably a better way is to use a grid, RibbonCheckBox without a label and a TextBlock instead.
Explained here.

Why does this WPF button stretch across the window?

The button below always expands to be as wide as the TextBlock. I've tried StackPanel, DockPanel, Width="Auto", etc.
How can I make the button expand to the size of its own text (as in HTML) and not to the size of text in its environement?
<DockPanel HorizontalAlignment="Left">
<Button x:Name="ButtonFavorite"
DockPanel.Dock="Top"
Content="Customers"
Margin="10"
Width="Auto"
Click="ButtonFavorite_Click">
</Button>
<TextBlock DockPanel.Dock="Top" Text="this is a long text which makes the button stretch across the window, if this text is just a couple words, the button will be smaller, and this drives me up the wall" Margin="10" TextWrapping="Wrap" />
</DockPanel>
ANSWER:
Thanks Greg, that did it. Here is the full XAML that works now, you can right-click the button to change its Content so see that the button expands and contracts appropriately.
<Window x:Class="Test3784234.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<DockPanel HorizontalAlignment="Left">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" >
<Button x:Name="ButtonFavorite"
Padding="5"
Cursor="Hand"
DockPanel.Dock="Top"
Content="Customers"
Margin="10"
Click="ButtonFavorite_Click">
<Button.ContextMenu>
<ContextMenu>
<MenuItem x:Name="menuItemReports" Header="Reports" Click="MenuItem_Click" />
<MenuItem x:Name="menuItemContracts" Header="Contracts" Click="MenuItem_Click"/>
<MenuItem x:Name="menuItemCustomers" Header="Customers" Click="MenuItem_Click" />
<MenuItem x:Name="menuItemDocumentation" Header="Documentation Creation Instructions" Click="MenuItem_Click" />
<MenuItem x:Name="menuItemEmail" Header="E-Mail" Click="MenuItem_Click" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
<TextBlock x:Name="TheMessage" DockPanel.Dock="Top" Text="Right-click the 'favorites' button to change its function." Margin="10" TextWrapping="Wrap"/>
</DockPanel>
</Window>
All you need to do is set the HorizontalAlignment property on your button. It defaults to stretch therefore filling the available space.
<Button x:Name="ButtonFavorite"
HorizontalAlignment="Left"
Content="Customers"
Margin="10"
Width="Auto"
Click="ButtonFavorite_Click">
Regarding your annoyance at the sizing of buttons, this is something that seems to be targeted at the designer in the designer/developer workflow, while you're clearly working on the developer portion. For the sake of development, I always apply a few styles in my App.xaml to ensure somewhat better button sizing. For example, in the application tag in your app.xaml file:
<Application.Resources>
<Style TargetType="Button">
<Setter Property="MinWidth" Value="60" />
<Setter Property="MinHeight" Value="23" />
<Setter Property="Margin" Value="3" />
</Style>
</Application.Resources>
Regarding your actual question:
The problem is that your DockPanel is stretching to the width of the text and the button will naturally expand to fill the available area. If you want the quick and dirty solution you can do something like:
<DockPanel HorizontalAlignment="Left">
<Button x:Name="ButtonFavorite"
DockPanel.Dock="Top"
Content="Customers"
Margin="10"
Width="Auto"
MaxWidth="100"
Click="ButtonFavorite_Click">
</Button>
</DockPanel>
Note the MaxWidth. If you want a more composable result, isolate your button in another panel. (I'm using a stackpanel because I believe someone else already used a grid in their example):
<DockPanel HorizontalAlignment="Left">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button x:Name="ButtonFavorite"
Content="Customers"
Margin="10"
Width="Auto"
Click="ButtonFavorite_Click" />
</StackPanel>
<TextBlock DockPanel.Dock="Top" Text="this is a long text which makes the button stretch across the window, if this text is just a couple words, the button will be smaller, and this drives me up the wall" Margin="10" TextWrapping="Wrap" />
</DockPanel>
I like the StackPanel in this case because I find myself using it to create the horizontal "bar" of buttons along the bottom of a Form- err- Window in the right corner.
You could try isolating the button from the main panel by putting it in another panel.
<DockPanel HorizontalAlignment="Left">
<Grid DockPanel.Dock="Top">
<Button x:Name="ButtonFavorite"
Content="Customers"
Margin="10"
Width="Auto"
Click="ButtonFavorite_Click">
</Button>
</Grid>
<TextBlock DockPanel.Dock="Top" Text="this is a long text which makes the button stretch across the window, if this text is just a couple words, the button will be smaller, and this drives me up the wall" Margin="10" TextWrapping="Wrap" />
</DockPanel>
Can you place them in a two column Grid with the button spanning just one column and the text spanning two columns?
Here's an example using a Grid layout versus a DockPanel. The idea is to have 2 columns and 2 rows. Put the Button it a single cell and make that row/column pair auto-sizing. Then put the TextBox into the second row and have it span both of the columns. This will essentially make the top-right cell just filler space and will achieve the behavior you're looking for.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button
x:Name="ButtonFavorite"
Grid.Column="0"
Grid.Row="0"
Content="Customers"
Margin="10"
Width="Auto"
Click="ButtonFavorite_Click">
</Button>
<TextBlock
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
Margin="10"
TextWrapping="Wrap"
Text="this is a long text which makes the button stretch across the window, if this text is just a couple words, the button will be smaller, and this drives me up the wall" />
</Grid>
As another method to do this: You could change the button's template so it's essentially wrapped in a centered StackPanel. Something like this:
<Button Content="Back">
<Button.Template>
<ControlTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="{TemplateBinding Content}"></Button>
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
Or you could add a style to app.xaml (or any other place where you're storing your global styles) like this:
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Style="{x:Null}" Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type Button}} }" FontWeight="Bold" Padding="5"></Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note that it's important to include the Style="{x:Null}" attribute on the button within the template if adding to the global styles, otherwise you'll get an infinite loop when it comes to rendering the button.

Resources