I need to display the following layout with a telerik PanelBar.
With the code below I was able to achieve everything except the 92% stuff in each panel.
XAML:
<UserControl.Resources>
<DataTemplate x:Key="PanelBarItemTemplate">
<Grid x:Name="grdCategory" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60*"></ColumnDefinition>
<ColumnDefinition Width="40*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid x:Name="grdSubCategory" Grid.Column="0" Style="{StaticResource CategoryLeftStyle}" >
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"></ColumnDefinition>
<ColumnDefinition Width="25*"></ColumnDefinition>
<ColumnDefinition Width="25*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding CategoryTitle}" Grid.Row="0" Grid.Column="0"/>
<HyperlinkButton Grid.Row="0" Grid.Column="1" Style="{StaticResource DetailLinkStyle}" Content="Details" Click="Home_Click"></HyperlinkButton>
<TextBlock Text="{Binding Score}" Grid.Row="0" Grid.Column="2"/>
</Grid>
<TextBlock Text="92%" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" FontSize="32" FontWeight="Bold"/>
</Grid>
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="PanelBarHeaderTemplate"
ItemsSource="{Binding SubReports}"
ItemTemplate="{StaticResource PanelBarItemTemplate}">
<TextBlock Text="{Binding CategoryTitle}" />
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<telerik:RadPanelBar x:Name="radPanelBar"
ItemTemplate="{StaticResource PanelBarHeaderTemplate}"
IsSingleExpandPath="False" >
</telerik:RadPanelBar>
</Grid>
in the xaml.cs file I provided the ItemsSource.
Can somebody help me out?
All that code works perfectly well for the individual items, but to place the 92% relative to those items (somewhat outside of the sub-items) you would need to also modify the ItemContainerStyle of RadPanelBar. Easiest way is to extract it in Blend, then look for the section under PanelBarItemTopLevelTemplate named ItemsContainer. This is a somewhat crude version, but I made a public property on my item called CalcInt that calculates the sum of a property on the SubReport items so it can be bound from the base item level. My modified code looks like so:
<Grid x:Name="ItemsContainer" Grid.Row="1" Visibility="Collapsed">
<telerik:LayoutTransformControl x:Name="transformationRoot">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsPresenter/>
<TextBlock Text="{Binding CalcInt}" FontSize="48" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</telerik:LayoutTransformControl>
</Grid>
I basically changed it from just containing an ItemsPresenter to a grid with some layout to display my extra-large TextBlock.
If you need a sample of the code or have any other questions feel free to hit me up on Twitter - #EvanHutnick.
Cheers!
-Evan
Hi I think you need to define a converter : IValueConverter and bind to the Label so you can calculate the percentual.
Mario
Related
In XAML, we typically have <Grid> layout which contains different elements. How do I align a control in one cell of grid to a control in a different cell like below?
(This used to be rather common in traditional applications where controls maybe in different group boxes etc but we still want to align them horizontally in one plane)
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column ="2">
<RadioButton Content="Option1"/>
</StackPanel>
</Grid>
The result is below which is ugly:
In this case, I just want to option1 to aligned centered with the TextBox (which does have custom height).
I can use margins to bring it to the desired position but that's kind of hard coded and not too WPFish.
Should I use binding to tie them directly? Is there a better way? Another way I can think of is to keep making grids within grids but seems like that will over complicate for this simple thing?
Try putting them both in another Grid or a horizontal StackPanel, and put that in one of the parent Grid cells.
The following did the trick, basically wrap the <RadioButton> around in <Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<Border BorderBrush="{x:Null}" Height="50">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Grid>
This answer helped.
Now the result is:
Three variants.
The first - I will supplement #zar: I use size binding instead of explicitly assigning a value to the size.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<Border BorderBrush="{x:Null}" Height="{Binding ActualHeight, ElementName=name, Mode=OneWay}">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Grid>
Second - I am implementing #Mark Feldman proposal: Delete StaskPanel and add lines to the grid.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="name" Height="50"/>
<Label Content="John"
Grid.Row="1"/>
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"
Grid.Column="1"/>
</Grid>
The third - analogous to the first, but without the Border.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center" VerticalContentAlignment="Center"
Height="{Binding ActualHeight, ElementName=name, Mode=OneWay}"/>
</StackPanel>
</Grid>
I can't figure out why the contents of my TextBox is affecting my grid column widths. I have setup a grid with 3 columns with widths defined as 50, *, 50, as shown below
Now, when in use, the center column will grow/shrink as the text in the TextBox changes, see the 2 examples below. The actual TextBox is not changing size, so I can't understand why in the world the grid is changing. The Grid is inside of a Border inside a UserControl. The UserControl is used as a DataTemplate in a ListBox.
Edit: I've discovered that this issue is related to the UserControl being in a ListBox, see example image below (UserControl in ListBox (circled in red) vs. UserControl Placed on Form (circed in blue). The grid behaves as expected when the UserControl is placed directly on the form. Code for the form is given below.
UserControl XAML:
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Name:" Margin="2" />
<TextBox Grid.Column="1" Margin="2" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Grid.ColumnSpan="2" />
<TextBlock Text="Shift:" Grid.Row="1" Margin="2" />
<TextBox Grid.Column="1" Grid.Row="1" Margin="2" Text="{Binding TimeShift, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, FallbackValue=0}" HorizontalContentAlignment="Right" />
<TextBlock Text="s" Grid.Row="1" Grid.Column="2" Margin="2" />
</Grid>
Window/Form XAML:
<Window x:Class="CrashSimOffice.FileImport"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CrashSimOffice"
Title="File Import" Height="350" Width="450" Background="White" Icon="/CrashSimOffice;component/Images/16/page-white-save.png">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="75"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Content="Add" Margin="2" Command="{Binding AddFileCommand}" />
<Button Content="Remove" Grid.Column="1" Margin="2" Command="{Binding RemoveFileCommand}" />
<ListBox HorizontalContentAlignment="Stretch" Grid.ColumnSpan="4" Margin="2" Grid.Row="1" ItemsSource="{Binding Files}" SelectedItem="{Binding CurrentFile}" ScrollViewer.CanContentScroll="False" Background="WhiteSmoke">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:FileViewModel}">
<local:FileView DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Done" Grid.Column="3" Margin="2" Grid.Row="2" Click="Button_Click" />
<local:FileView Grid.Row="3" Grid.ColumnSpan="4" />
</Grid>
OK, I figured it out, Bruno V you got me thinking it must have something to do with the ListBox. I needed to add this to my ListBox:
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Not sure why that works, but it does.
I have two objects: Bicycle and BicycleFullSuspension, which inherits Bicycle and contains additional properties (strings and decimals). I managed to make a functioning DataTemplateSelector that will choose either my LbxItemTemplateBicycle DataTemplate or my LbxItemTemplateFullSuspension DataTemplate, depending on to which class the object in an ObservableCollection belongs. Both of these templates are bound to the parent template ListBoxItemTemplate.
I am trying to display information about each object in a ListBox, and each of the inheriting templates should add a few more fields to the grid in the parent DataTemplate. My problem is that I cannot figure out how to add WPF elements to the grid in ListBoxItemTemplate from one of the inherited templates. I cannot assign a key to the grid in the template, so I am not sure how to specify that additional TextBlocks should end up in the same grid that is in the parent template. (Right now, additional TextBlocks are stacked on top of the parent grid.)
<DataTemplate x:Key="ListBoxItemTemplate">
<Border Name="LbxItemTemplate" BorderBrush="DarkRed" BorderThickness="2" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Bike Year:" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding BikeYear}" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="Bike Color: "/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding BikeColor}"/>
...
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="LbxItemTemplateFullSuspension" DataType="{x:Type app:BicycleFullSuspension}">
<Grid>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource ListBoxItemTemplate}" />
<TextBlock Grid.Row="6" Grid.Column="0" Text="Shock Travel"/>
<TextBlock Grid.Row="6" Grid.Column="1" Text="{Binding ShockTravel}"/>
</Grid>
</DataTemplate>
I found these links helpful for getting to this point:
http://dariosantarelli.wordpress.com/2011/07/28/wpf-inheritance-and-datatemplates/
http://zamjad.wordpress.com/2009/12/31/applying-data-template-conditionally/
Is there a way to use data-template inheritance in WPF?
Edit:
I'm not sure why I didn't think to put the Border on the template inheriting the base, but by nesting a StackPanel inside of the Border in the inheriting template (StackPanel contains the ContentPresenter with the base template content along with the Grid that has the added information), everything lines up very nicely:
Working solution, using input from XAMeLi:
<DataTemplate x:Key="ListBoxItemTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" SharedSizeGroup="LabelColumnGroup" />
<ColumnDefinition Width="150" SharedSizeGroup="LabelColumnGroup" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Bike Year:" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding BikeYear}" />
...
</Grid>
</DataTemplate>
<DataTemplate x:Key="LbxItemTemplateFullSuspension" DataType="{x:Type app:BicycleFullSuspension}" >
<Border BorderBrush="DarkRed" BorderThickness="2" Padding="5" Margin="5" Grid.IsSharedSizeScope="True" Width="500">
<StackPanel>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource ListBoxItemTemplate}" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelColumnGroup" />
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelColumnGroup" />
</Grid.ColumnDefinitions>
...
<TextBlock Grid.Row="7" Grid.Column="0" Text="Shock Type: "/>
<TextBlock Grid.Row="7" Grid.Column="1" Text="{Binding ShockType}"/>
...
</Grid>
</StackPanel>
</Border>
</DataTemplate>
You cannot add UIElemets to a data template, but you can layout the bigger template to look like it has added text to the base template.
Basically, you are on the right path, only the layout of the big template is corrupt. (Too bad you omitted the structure of your rows and columns)
Try this:
<DataTemplate x:Key="ListBoxItemTemplate">
<Border Name="LbxItemTemplate" BorderBrush="DarkRed" BorderThickness="2" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
SharedSizeGroup="LabelGroupColumn"/>
...
</Grid.ColumnDefinitions>
....
</Border>
</DataTemplate>
<DataTemplate x:Key="LbxItemTemplateFullSuspension">
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/><!--This will hold the default template-->
<!--From here define the same structure as in the default template, if you have rows for margins and such-->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
SharedSizeGroup="LabelGroupColumn"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource ListBoxItemTemplate}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Shock Travel"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ShockTravel}"/>
</Grid>
</DataTemplate>
It seems that your grids have Label-Value structures and you want all the values to start from the same vertical line (this is very good from UX point of view). Notice the SharedSizeGroup setters on column definitions and the Grid.IsSharedSizeScope="True" on a mutual parent. This will keep the width of the label column to be synchronized among the grids.
I am using a grid by the definition of appropriateness defined in this question Grid vs Stackpanel. However when working with grids you have to define the controls position inside them explicitly in the grid. This becomes a pain when having to reorder controls or when adding a new control to the grid. With the code provided as an example, is there a way to get the rows and columns for the text and text boxes to line up while being easy to modify or expand later?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Value One:" Grid.Row="0" Grid.Column="0"/>
<TextBox x:Name="TextBoxOne" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="Value Two:" Grid.Row="1" Grid.Column="0"/>
<TextBox x:Name="TextBoxTwo" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Value Three:" Grid.Row="2" Grid.Column="0"/>
<TextBox x:Name="TextBoxThree" Grid.Row="2" Grid.Column="1"/>
</Grid>
I wrote a custom control I use that makes it extremely easy to do this, but before I created it I generally used this sort of thing:
<ControlTemplate x:Key="ColumnsTemplate" TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="7*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" ContentSource="Header" />
<ContentPresenter Grid.Column="1" />
</Grid>
</ControlTemplate>
<ItemsControl ... ItemTemplate="{StaticResource ColumnsTemplate}">
<HeaderedContentControl Header="Value One:">
<TextBox x:Name="TextBoxOne" />
</HeaderedContentControl>
<HeaderedContentControl Header="Value Two:">
<TextBox x:Name="TextBoxTwo" />
</HeaderedContentControl>
...
</ItemsControl>
This allows easy add/remove of items from the ItemsControl, or better yet, data binding.
If you prefer auto-sizing on the grid rather than star sizing (3* and 7*) you can use a shared sizing scope by setting IsSharedSizeScope on the ItemsControl and SharedSizeGroup on the first ColumnDefinition.
Another option is GridView, but I find it more difficult to use for this purpose.
I don't know why but for some reason I am not able to refer to my tbText control in my code behind file. Here is the XAML part:
<ComboBox.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="ic">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
</Grid>
**<TextBlock x:Name="tbText" Grid.Column="0" Grid.Row="0" Margin="10" />**
<Image Grid.Column="1" Margin="10" Grid.Row="0" Width="100" Height="100" Stretch="Fill">
</Image>
</ItemsControl>
</DataTemplate>
</ComboBox.ItemTemplate>
I cannot refer to the "tbText" control.
You can't refer to it because it is inside an Items control.
You'd have to search the ItemsControl children in order to find the textbox.
See Finding control within wpf items control for ways to do this.