WPF: get the content of a GroupBox to fill available space - wpf

I'm facing an irritating problem with WPF GroupBox, hope someone can help me out. Basically the problem is this: I have a listview inside a GroupBox, but no matter what I do I can't seem to be able to make it fill the GroupBox.
Here is the basic code:
<GroupBox Grid.Row="2" Header="Field" Visibility="{Binding ElementName=radioUnbound, Path=IsChecked, Converter={StaticResource bool2vis}}" Margin="0" VerticalContentAlignment="Stretch">
<ListView ItemsSource="{Binding ElementName=nnf1, Path=UnboundFields}" x:Name="listUnbound" SelectionChanged="listSelectionChanged" VerticalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Margin="2"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GroupBox>
I tried encasing the list inside Grids, StackPanels, DockPanel, etc... but no matter what I try I always invariably end up with this:

I tried your code in XamlPad it works as you would expect it. Make sure you don't have global styles that set your ListView or GroupBox appearance.
You can clear global styles by putting this in the resources section of the GroupBox's parent control:
<Style TargetType="GroupBox" />
<Style TargetType="ListView" />

Related

ItemsControl items bindings called when collapsed

I have an ItemsControl which displays a list of messages. It's defined as ...
<ItemsControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Messages}" >
</ItemsControl>
I then have a DataTemplate which handles the display for each message. It's defined as...
<DataTemplate DataType="{x:Type vm:MessageViewModel}">
<Button Command="{Binding CommandOpenPage}">
<Button.Template>
<ControlTemplate>
<Border Margin="2" BorderThickness="1"
BorderBrush="{Binding Flags, Converter={StaticResource msgFlagConverter}}"
Background="{Binding Flags, Converter={StaticResource msgFlagConverter}, ConverterParameter=1}" >
<TextBlock Text="{Binding Path=Message}" Style="{StaticResource ActionItem}" TextWrapping="Wrap" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
Everything displays OK. My problem is when the parent controls are set to Visibility=Collapsed my ItemsControl still goes through the DataTemplate and calls the converters for BorderBrush and BackgroundBrush for each MessageViewModel.
This is bothersome because when the list is very large the bindings are set and converters are executed when they shouldn't. This list is only visible when the user chooses to see it. I understood the binding engine ignores elements under a collapsed parent. Is there an exception to this rule? Or am I just missing something?
I found my problem. The above ItemsControl and DataTemplate were in a UserControl. The visibility was originally handled inside the usercontrol itself by binding the main layout grid to a visibility property. By simply setting the user controls visibility in the parent XAML all bindings started behaving as expected.
This fixes my problem but I still don't understand the difference between setting the visibility of the main layout grid vs the visibility of the usercontrol itself.
<c:ApplicationMenuView Grid.Column="1" Grid.Row="4"
HorizontalAlignment="Left" Margin="1"
VerticalAlignment="Stretch"
DataContext="{Binding Menu}"
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>

Hide the group header of a listview by binding in Windows Store App

I'm trying to remove group headers for groups where the header title is empty. But I can not make the binding in HeaderContainerStyle work. Neither can I set visibility on the TextBlock in TemplateHeader 'cause that will leave a small space and not be completely invisible.
This is my XAML:
<Page.Resources>
<CollectionViewSource
x:Name="MenuItemsGrouped"
IsSourceGrouped="True"
Source="{Binding MenuItems}" />
</Page.Resources>
<ListView Grid.Row="1" Margin="0"
ItemsSource="{Binding Source={StaticResource MenuItemsGrouped}}"
IsSynchronizedWithCurrentItem="False"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.HeaderContainerStyle>
<Style TargetType="ListViewHeaderItem">
<Setter Property="Visibility" Value="{Binding GroupHeaderVisibility}"></Setter>
</Style>
</GroupStyle.HeaderContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0" Orientation="Horizontal">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Does anyone have a solution - and maybe a reason the binding won't work?
EDIT:
Ok, it's is actually a limitation in Windows Store Apps and earlier Silverlight apps:
Windows Presentation Foundation (WPF) and Microsoft Silverlight
supported the ability to use a Binding expression to supply the Value
for a Setter in a Style. The Windows Runtime doesn't support a Binding
usage for Setter.Value (the Binding won't evaluate and the Setter has
no effect, you won't get errors, but you won't get the desired result
either). When you convert XAML styles from WPF or Silverlight XAML,
replace any Binding expression usages with strings or objects that set
values, or refactor the values as shared StaticResource values rather
than Binding-obtained values.
from http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.setter
And see also Silverlight: How to use a binding in setter for a style (or an equivalent work around)
Just try to bind the Visibility of the root element of HeaderTemplate.
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" Visibility = "{Binding GroupHeaderVisibility}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
It should work.Good luck!
[Edit]
I have figured out a solution, it's not very elegant, but it works. Here are the steps:
Add this xaml code to your ListView:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
2.Get a copy of ContainerStyle of GroupStyle(Right click the Listview on the design surface.Select: "Edit GroupStyle"->"ContainerStyle"). Then remove this line from the attributes of ContentControl:
Margin = "4"
3.Apply the above ContainerStyle to your listview. It should work.
NOTE: Step 1 is necessary, because ContainerStyle is no longer honored on Windows 8.1 when ItemsPanel is an ItemsStackPanel(which is the default).
http://msdn.microsoft.com/en-us/library/windows/apps/dn263110.aspx
You are binding to the Visibility property which is of type Visibility. My guess is that the GroupHasHeader property is a boolean; you should use a Boolean to visibility converter.

How can I make an "Accordion Widget" in WPF?

The goal:
I'm trying to achieve something like this in WPF:
(source: wordpress.org)
An initial solution:
At the moment, I'm trying to use an ItemsControl with an ItemTemplate composed of an Expander.
I want a consistent look for the Header portion of the Expander, but I want the Content portion of the Expander to be completely flexible. So, it's basically a set of "portlets" stacked vertically, where each portlet has a consistent title bar but different content.
The code so far:
This is what I have at the moment:
<ItemsControl
Grid.Row="2"
Grid.Column="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander>
<Expander.HeaderTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal">
<TextBlock
FontSize="14"
FontWeight="Bold"
Text="Title_Of_Expander_Goes_Here" />
<TextBlock
Margin="10,0,0,0"
FontWeight="Bold"
FontSize="18"
Text="*" />
</StackPanel>
</DataTemplate>
</Expander.HeaderTemplate>
<Expander.Template>
<ControlTemplate
TargetType="Expander">
<Border
BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
</Expander.Template>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<StackPanel>
<TextBlock
FontSize="14"
FontWeight="Bold"
Text="Users:" />
<wt:DataGrid
Margin="0,1,0,0"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="True"
ItemsSource="{Binding Source={StaticResource Main_SystemUsers}, XPath=//Users/*}">
<wt:DataGrid.Columns>
<wt:DataGridTextColumn
Header="User Name"
Binding="{Binding XPath=#UserName}" />
<wt:DataGridComboBoxColumn
Header="Role"
ItemsSource="{Binding Source={StaticResource Main_UserRoles}, XPath=//Roles/*}"
SelectedValueBinding="{Binding XPath=#Role}" />
</wt:DataGrid.Columns>
</wt:DataGrid>
<StackPanel
Margin="0,10,0,0"
Orientation="Horizontal">
<Button
Content="Add New User..." />
<Button
Margin="10,0,0,0"
Content="Delete User..." />
</StackPanel>
</StackPanel>
</ItemsControl.Items>
</ItemsControl>
Discussion:
The only thing that shows up when I run this is the DataGrid of users and the buttons ("Add New User" and "Delete User") below it. There is no Expander or title bar. Also, even if I did see one, I'm not sure how to set up a Binding for the title that appears on the title bar. I know how to do bindings if I use ItemsSource, but I wanted to set my items declaratively.
The question:
How should I go about this? I'm looking for either a fix for what I have now or a clean-sheet solution.
Edit:
What I ended up doing was replacing the ItemsControl with a StackPanel and just writing a style for my expanders. This proved to be much simpler, and there really was no benefit to the ItemsControl since I needed to declare custom content for each item anyway. The one issue remaining was how to achieve a custom title for each expander. That's where #Thomas Levesque's suggestion to use TemplateBinding came in. All I had to do was replace Text="Title_Of_Expander_Goes_Here" in my header's template (see code above) with Text="{TemplateBinding Content}".
You're not seeing the Expander because you redefined its template. This one should work better :
...
<Expander.Template>
<ControlTemplate
TargetType="Expander">
<Border
BorderThickness="1">
<Expander Content="{TemplateBinding Content}" Header="{TemplateBinding Header}"/>
</Border>
</ControlTemplate>
</Expander.Template>
...
Personally I think a TreeView control would give you a much better base to work from, especially if you're using Expression Blend as a basis to create new/blank Templates from for items. Seeing the default Templates is extremely enlightening and gives you much more fine-grained control and better understanding and insight into how things work by default. Then you can go to town on them. It also looks like you're working with Hierchical Data and TreeViews inherently lend themselves well to working with such data.

SL3 Dataform Datafields in Border

Can you surround Datafields in a border? I have a large form that needs to be organized into sections, "Customer INformation" for instance.
Is there a way to surround these with a border?
I kinda get the feeling you are not specifying your own edit template for the control but a letting data form do it for you. I get this feeling because if you are already using an edit template you wouldn't be asking the question. Consider this:-
<dataform:DataForm x:Name="dataForm">
<dataform:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<Border BorderBrush="Black" BorderThickness="2">
<StackPanel>
<dataform:DataField>
<TextBox Text="{Binding ID, Mode=TwoWay}" />
</dataform:DataField>
<dataform:DataField>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</dataform:DataField>
</StackPanel>
</Border>
<dataform:DataField>
<CheckBox IsChecked="{Binding Test, Mode=TwoWay}" />
</dataform:DataField>
</StackPanel>
</DataTemplate>
</dataform:DataForm.EditTemplate>
</dataform:DataForm>
Adding a border around the ID and Name fields is a simple case of placing them in their own StackPanel and putting that in a Border. Basically with a template you can do anything you want with the form appearance, in fact you don't even need the DataField if you feel you can do a better job with label placement etc in your own XAML.

Force TextBlock to wrap in WPF ListBox

I have a WPF listbox which displays messages. It contains an avatar on the left side and the username and message stacked vertically to the right of the avatar. The layout is fine until the message text should word wrap, but instead I get a horizontal scroll bar on the listbox.
I've Googled and found solutions to similar issues, but none of them worked.
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding Path=FriendsTimeline}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="DarkBlue" BorderThickness="3" CornerRadius="2" Margin="3" >
<Image Height="32" Width="32" Source="{Binding Path=User.ProfileImageUrl}"/>
</Border>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=User.UserName}"/>
<TextBlock Text="{Binding Path=Text}" TextWrapping="WrapWithOverflow"/> <!-- This is the textblock I'm having issues with. -->
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Contents of the TextBlock can be wrapped using property TextWrapping.
Instead of StackPanel, use DockPanel/Grid.
One more thing - set ScrollViewer.HorizontalScrollBarVisibility property to Disabled value for the ListBox.
Updated Hidden to Disabled based on comment from Matt. Thanks Matt.
The problem might not be located in the ListBox. The TextBlock won't wrap, if one of the parent controls provides enough space, so that it hasn't the need to wrap. This might be caused by a ScrollViewer control.
If you want to prevent TextBlock to grow, and you want it to just fit in the size of the listbox, you should set the width of it explicitly.
In order to change it dynamically, it means not a fix value, but you need to bind it to its proper parent element in the visual tree. You can have something like this:
<ListBox ItemsSource="{Binding MyItems}" Name="MyListBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Width"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollContentPresenter}, Path=ActualWidth}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If it does not work, try to find the proper elements (which has to be binded to what) with the Live Visual Tree in Visual Studio.

Resources