User Control in DataGridTemplateColumn - wpf

I have a datagrid in which I rotated everything 90 degrees to show my info vertically. Not that that has to do with anything I don't think. I'm trying to add a UserControl which is basically an Expander into a DataGridTemplateColumn. The problem I'm having is when I go to open my expander(usercontrol), it is opening up behind the cells that are below it. I need to bring it to front to overlap it basically. I have it within a canvas so that the usercontrol won't open up within the cell and expand. I want the cell to stay the same size and upon opening the expander, it will overlap and display covering the datacells below it. Any ideas what I'm doing wrong?
<DataGridTemplateColumn Header="Expander" CanUserResize="False" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="UserControl">
<Grid Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SnapsToDevicePixels="True" OverridesDefaultStyle="True">
<Canvas>
<local:DescriptionUserControl DataContext="{Binding Path=DescriptionViewModel}" x:Name="Description" Height="Auto" Width="Auto" Focusable="True" Margin="0,-2">
</local:DescriptionUserControl>
</Canvas>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Worksheet" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="View Worksheet" HorizontalAlignment="Center" VerticalAlignment="Center" Height="26" Width="110" ></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Veeps" width="Auto"></DataGridTextColumn>
</DataGrid.Columns>

The Canvas is a problem here. It never tries to enlarge it's parent, that's why the cells don't expand.
But, it's still part of the visual tree and there are other elements with higher ZIndex. You could try to fix ZIndex for when the expander is expanded, but I believe it will take too much work and will have bugs that will be difficult to find and fix.
I'd go down the "custom control" route. Build a control that inherits from HeaderdContentControl and has one additional property: IsPopupShowing. The content will be in a popup and that will be outside of the visual tree.
Actually, it could be achieved with a template only (although, I still recommend custom control). Something like this:
<Style x:Key="PoppingExpander" TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<Grid>
<ToggleButton x:Name="HeaderHolder"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
ContentStringFormat="{TemplateBinding HeaderStringFormat}"/>
<Popup x:Name="ContentHolder"
Placement="Bottom">
<Border Background="Aqua"
BorderBrush="LightBlue"
BorderThickness="1">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBiding Padding}" />
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="HeaderHolder" Property="IsChecked" Value="True">
<Setter TargetName="ContentHolder" Property="IsOpen" Value="True"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[I could have bound IsOpen property of the popup to IsChecked property of the toggle button, but I really don't like using ElementName syntax since it's very particular about it's scope and creates bugs that are very difficult to hunt down. I prefer any other way than ElementName]
This will give you a HeaderedContentControl that has it's content in a popup. You'd need to style the toggle button to look like the one used in the expander (you can extract the style for expander via blend and it will have the style for the button).
Change your use of Expander to HeaderedContentControl, remove setters of properties that are specific to Expander (like IsExpanded) and you should be done.
P.S. - you can change the Placement property of the popup if you want it to open in another direction.

Related

Bind TextBlock.Text to specific item-property in collection where value matches another binding

Trying to stay on the MVVM road, I keep struggling with the following task for hours:
I want to show the String-value of a specific Item (in a TextBlock), which is part of the UserCollection (ObservableCollection<Tuple<int, string>>). The selection should take place via the Int-property of the item in the Collection, matching the bound IdCreatedByUser-Property in MyOrder.
To make things more clear:
An UserCollection that holds an ID (int) and NAME (string):
public ObservableCollection<Tuple<int, string>> UserCollection;
A MyOrder-Property holding an Instance of the Orders-Class:
public Order MyOrder;
Here an example of the Orders-class.
public class Order: INotifyPropertyChanged
{
public string Comment;
public int IdCreatedByUser;
public bool IsComplete;
}
Please note that this is just an example for the properties..knowing that get,set are missing here..
The only solution I came up with is to hijack a Combox like this:
<ComboBox ItemsSource="{Binding UserCollection}"
DisplayMemberPath="Item2"
SelectedValue="{Binding MyOrder.IdCreatedByUser}"
SelectedValuePath="Item1">
<ComboBox.Template>
<ControlTemplate>
<TextBlock Text="{Binding SelectedItem.Item2,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
The fact that I can use ItemsSource, SelectedValue and SelectedValuePath makes it possible for me to select and show the desired Item. Any solutions for the use of TextBlocks with this one?
I was also thinking about a converter or extra property..but maybe you can show me a way to design this in a better way..
Thanks!
This essentially makes your specialized ComboBox easily reusable. Stuff like FontWeight will be inherited by the ContentPresenter.
<Style x:Key="CollectionLookupComboBox" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<!--
Default to readonly, but you can override that for particular instances
if that's useful somewhere.
-->
<Setter Property="IsReadOnly" Value="True" />
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Border
x:Name="OuterBorder"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
>
<!--
The margin here keeps the text in the same spot when I toggle IsReadOnly,
with the default theme I have. May need to fiddle with that to get it to
look right for you.
-->
<ContentPresenter
Margin="3,2,2,0"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
VerticalAlignment="Stretch"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Example -- this is overkill; you don't need an ItemTemplate or boldface, but it demonstrates how all the usual ComboBox stuff is supported:
<StackPanel
Orientation="Vertical"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<CheckBox
x:Name="ReadOnlyCheckBox"
IsChecked="True"
Margin="1"
Content="Read-Only"
/>
<ComboBox
Margin="1"
Style="{StaticResource CollectionLookupComboBox}"
IsReadOnly="{Binding IsChecked, ElementName=ReadOnlyCheckBox}"
MinWidth="80"
SelectedIndex="0"
FontWeight="Bold"
Foreground="Green"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="DeepSkyBlue" BorderThickness="2" CornerRadius="4">
<Label Content="{Binding}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>First Item</sys:String>
<sys:String>Second Item</sys:String>
</ComboBox>
</StackPanel>

Height of WPF TreeViewItem controltemplate not collapsing

I have an issue that I have been trying to solve for a week and have not only played around extensively trying to figure this out but have done lots of research on StackOverFlow and other websites on how to fix this problem. Just to be clear, I have been learning WPF for about 3 or so months and come from WinForms and am still in the learning phase.
Here is my problem.
I have a TreeViewItems that I am adding to a TreeView control. These TreeView items use a Style that creates a custom look that I am trying to accomplish that is pretty much the look and feel of the entire application. The Style uses an explicit Setter.Value against the Template property to create the custom look of the TreeView item. It has its own custom expander arrow, TextBlock header that is bound to the TreeViewItem header, and also of course a ContentPresenter and a ItemsPresenter. There is also a trigger that is wired up to the value of the TreeViewItem's IsExpanded value so that way the ItemsPresenter can be shown or hidden when the TreeViewItem is expanded or collapsed. Everything works as it should except the collapse and expand part. Of course the ItemsPresenter hides and shows like it should but the TreeViewItem itself does not actually collapse its height when the IsExpanded is false. To show what I mean, here are 2 pictures to illustrate what is going on. I added a green border around the grid in the template of the style to show that the individual TreeViewItem itself is not shrinking its "height" when collapsed.
Expanded
Pic of expanded tree view item
Collapsed
Pic of collapsed tree view item
As you can see, the green border, or the treeview item itself is still the same height when collapsed as it is when expanded. Here is the XAML used to create the custom style of the TreeViewItem's themselves.
TreeViewItem Style XAML Code:
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Border x:Name="MyBorder" BorderThickness="1" BorderBrush="LawnGreen">
<Grid HorizontalAlignment="Left" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ui:TreeViewItemExpander x:Name="TreeViewItemExpander" Grid.Row="0" Grid.Column="0" IsPointingDown="{TemplateBinding IsExpanded}"/>
<!--This represents the text for the tree view item itself-->
<TextBlock Text="{TemplateBinding Header}" Grid.Row ="0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="White"/>
<ContentPresenter x:Name="ContentPresenter" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ItemsPresenter x:Name="ItemsPresenter" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ItemsPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This code below is how I am using the style
<TreeView Style="{StaticResource TreeViewStyle}" Width="200" Margin="419,337,19,328">
<controls:CustomTreeViewItem Header="Folder 1" Style="{StaticResource TreeViewItemStyle}">
<Button Content="Item 1" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20"/>
<Button Content="Item 2" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20"/>
</controls:CustomTreeViewItem>
</TreeView>
Thanks for any input or help that anybody can provide. I hope that I was clear enough.
I figured it out. What I did was just add a setter in the IsExpanded trigger to set the property of the grid row that the ItemsPresenter resides in. All I did was set the height of the row to 0 which essentially hides the items. Here is the code of the trigger itself from the code above with the change applied.
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="Items" Property="Visibility" Value="Visible"/>
<Setter TargetName="ItemsRow" Property="Height" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
I am interested though if anybody else has a better solution. After I figured this out, I then thought about animating the height of the row until it is 0 to give a better effect, but was not successful. I found out that StoryBoards are freezable and are frozen when inside of a Style or ControlTemplate. That means if you wanted it to be animated, then the animation of the item collapsing would have to be implemented with code behind, or on each TreeViewItem individually? I am sure there is a better way. If I find out how to accomplish this, I will update this post for everybody to reference. Please feel free to add to this post on a better solution!!

Selection broken in custom RadioButton control template

I'm attempting to create a control template for the Silverlight RadioButton, using a ToggleButton for each item. The problem I'm running into is that the selection mechanism appears to be broken. Here's the (simplified) style I'm using:
<Style TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid>
<ToggleButton x:Name="toggle"
IsChecked="{TemplateBinding IsChecked}">
<ContentPresenter x:Name="contentPresenter" />
</ToggleButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The test is simply whether the radio selection actually works -- so for example, the text blocks below don't show the correct value when you click on the radio buttons:
<StackPanel>
<RadioButton x:Name="radio1" GroupName="Test" Content="1" />
<RadioButton x:Name="radio2" GroupName="Test" Content="2" />
<TextBlock Text="{Binding ElementName=radio1,Path=IsChecked,StringFormat='Radio 1 checked: {0}'}" />
<TextBlock Text="{Binding ElementName=radio2,Path=IsChecked,StringFormat='Radio 2 checked: {0}'}" />
</StackPanel>
You would think that maybe there is a named part in the control template, which the control uses to update the selection -- however, the docs indicate no named parts. So what is going on here, and how can I get my example working?
You would think that maybe there is a named part in the control template
Yes ... it turns out there is an "undocumented feature". Trial and error shows that the "BoxMiddleLine" is the named part that controls the selection. So adding to the control template a clickable element with that name, fixes the issue:
<Grid>
<Border x:Name="BoxMiddleLine" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#0000" />
<ToggleButton x:Name="toggle"
IsChecked="{TemplateBinding IsChecked}"
IsHitTestVisible="False">
<ContentPresenter x:Name="contentPresenter" />
</ToggleButton>
</Grid>

WPF clipping even when no clipping is desired - how to turn it off?

I need to float out some content out of the ListBox as specified in a DataTemplate for an ListBox.ItemTemplate. I am using RenderTransform but the content gets clipped on ListBox boundaries. ClipToBounds is False for the entire visual tree.
I have read somewhere that WPF internally performs some clipping even when none is specified with dedicated clipping properties. I have also found out that using Canvas can sometimes cure the clipping problem but it does not help here.
How can I overcome this problem? Here is some XAML that I want to fix. Please note the entire left part of rectangle is missing.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
<Rectangle.RenderTransform>
<TranslateTransform X="-50" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate>
42
</ListBox>
The ListBoxItem's are getting clipped by the ScrollViewer in the ListBox Template. To work around this I think you'll need to remove the ScrollViewer from the Template and if you need scrolling you can wrap the ListBox in a ScrollViewer
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox Margin="100,10,0,0">
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
<Rectangle.RenderTransform>
<TranslateTransform X="-50" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate> 42
</ListBox>
</ScrollViewer>
Update
The ScrollViewer in the Template will generate a ScrollContentPresenter which in turn has the following GetLayoutClip
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
return new RectangleGeometry(new Rect(base.RenderSize));
}
This class is Sealed so you can't derive from it to override this method. You would have to implement your own ScrollContentPresenter (e.g MyScrollContentPresenter) and probably your own ScrollViewer that uses MyScrollContentPresenter as well to make this work (and if you return null in this method I think that some items below the bounds of the ListBox could become visible as well)
I stumbled upon a solution to this problem by accident while working around it. If you change the ScrollViewer's HorizontalScrollMode and VerticalScrollMode to "Disabled" within the style template, it will stop clipping in each direction respectively.
Edit: May not work for WPF. I tested with a UWP app. The fields in question are:
ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"

Use checkbox as togglebutton in expander

Im quite new to wpf and have to following problem.
I need to create a List (i am using a listbox) of items that can be expanded (expander).
The problem is, that they can be expanded, only if they have been 'selected'.
Each listboxitem should have a checkbox and some text.
So very basic example to illustrate what i mean:
<listbox>
<item>(checkbox) John Doe</item>
<item>(checkbox) Mike Murray</item>
</listbox>
If any (so multiple is allowed) of the checkboxes in the listbox are checked, then
the item expands showing more data.
Again an example:
<listbox>
<item>
(checkbox-checked) John Doe
Some extra data shown in expanded area
</item>
<item>
(checkbox-unchecked) Mike Murray</item>
</listbox>
I cant get a expander to use a checkbox as 'togglebutton'.
Could anyone help me out? Some example code would be very welcome...
This should do the trick:
<ListBox>
<ListBox.Resources>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Content="{TemplateBinding Header}"
/>
<ContentControl
x:Name="body"
Grid.Row="1" Content="{TemplateBinding Content}"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="body" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<Expander Header="One">
Content one
</Expander>
<Expander Header="Two">
Content two
</Expander>
</ListBox>
I've defined a Style here that changes the Template of any Expander controls to which the Style is applied. (And since I've put the Style in the ListBox.Resources it'll automatically apply to an Expander controls in the list.)
The trick to getting the CheckBox to work is that when you put it (or indeed any ToggleButton based control) into an Expander template, you need to use a data binding configured with its RelativeSource set to the TemplatedParent. This enables two-way binding - it means that not only does the CheckBox reflect the current state of the expander, it is also able to change the current state.
All you need to add a check box in the header is this code:
<telerik:RadExpander.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center"/>
<TextBlock Margin="5,0,0,0">Legend</TextBlock>
</StackPanel>
</telerik:RadExpander.Header>
I am using Rad Control, The same can be done using the standard expander

Resources