wpf rotate and translate transform issue on textblock - wpf

I've got a list of strings I'm binding to an items control.
The strings are displayed in textblocks I've declared in the itemscontrol template. I've rotated the textblocks 270 so that the text is on its side - I've also translated the textblocks down by their width so that they are at the top of the page.
My issue is they are now too far apart as its keeping the original width rather than the transformed width. I can understand why its doing it, but I need to get them stacked together with no gap.
Can anyone point me in the right direction please?
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="354" Width="632"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<Window.Resources>
<TransformGroup x:Key="Rotate">
<RotateTransform Angle="270" />
<TranslateTransform Y="200" />
</TransformGroup>
</Window.Resources>
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding MyStrings}" HorizontalAlignment="Left" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Black" Width="200" Height="20" RenderTransform="{StaticResource Rotate}" >
<TextBlock Text="{Binding }" >
</TextBlock>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
and the code behind is just ...
using System.Collections.Generic;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
MyStrings = new List<string> {"monkey", "turtle", "rabbit"};
InitializeComponent();
}
public List<string> MyStrings { get; set; }
}
}

Use LayoutTransform instead of RenderTransform. That will ensure layout logic takes into account the transformed location of the items.
<Border BorderThickness="1" BorderBrush="Black" Width="200" Height="20" LayoutTransform="{StaticResource Rotate}">

Related

UserControl default value for dependency property of type DataTemplate

I have a simple EntriesViewModel that stores a list of items as well as supporting Add/Remove commands. (Add/Remove commanding left out)
public class EntriesViewModel
{
public ObservableCollection<string> Entries { get; private set; }
public EntriesViewModel()
{
Entries = new ObservableCollection<string>() { "One", "Two", "Three" };
}
}
To view this I have created a simple EntriesEditor usercontrol that uses a listbox to display the entries and buttons to Add/Remove (Add/Remove commanding left out)
public partial class EntriesEditor : UserControl
{
public EntriesEditor()
{
InitializeComponent();
}
}
<UserControl x:Class="UserControlDefaults.EntriesEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:UserControlDefaults"
VerticalAlignment="Top">
<DockPanel Height="200">
<UniformGrid Columns="2"
DockPanel.Dock="Bottom">
<Button Content="Add" />
<Button Content="Remove" />
</UniformGrid>
<ListBox ItemsSource="{Binding Entries}"
ItemTemplate="{Binding EntryTemplate, RelativeSource={RelativeSource AncestorType={x:Type l:EntriesEditor}}}"/>
</DockPanel>
And is used as so :
<l:EntriesEditor DataContext="{Binding EntriesViewModel}"/>
I now want the EntriesEditor to support the ability to define a custom ItemTemplate. So I add an EntryTemplate property.
public partial class EntriesEditor : UserControl
{
public DataTemplate EntryTemplate
{
get { return (DataTemplate)GetValue(EntryTemplateProperty); }
set { SetValue(EntryTemplateProperty, value); }
}
public static readonly DependencyProperty EntryTemplateProperty =
DependencyProperty.Register("EntryTemplate", typeof(DataTemplate), typeof(EntriesEditor), new PropertyMetadata(null));
public EntriesEditor()
{
InitializeComponent();
}
}
<UserControl x:Class="UserControlDefaults.EntriesEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:UserControlDefaults"
VerticalAlignment="Top">
<DockPanel Height="200">
<UniformGrid Columns="2"
DockPanel.Dock="Bottom">
<Button Content="Add" />
<Button Content="Remove" />
</UniformGrid>
<ListBox ItemsSource="{Binding Entries}"
ItemTemplate="{Binding EntryTemplate, RelativeSource={RelativeSource AncestorType={x:Type l:EntriesEditor}}}"/>
</DockPanel>
Which is used as so:
<l:EntriesEditor DataContext="{Binding EntriesViewModel}">
<l:EntriesEditor.EntryTemplate>
<DataTemplate>
<Border BorderThickness="1"
BorderBrush="Green"
Padding="5">
<ContentPresenter Content="{Binding}"/>
</Border>
</DataTemplate>
</l:EntriesEditor.EntryTemplate>
</l:EntriesEditor>
But now I want to define a default EntryTemplate within the EntriesEditor control, how would I do this?
The only way I can think of, is like this :
<UserControl x:Class="UserControlDefaults.EntriesEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:UserControlDefaults"
VerticalAlignment="Top">
<UserControl.Style>
<Style TargetType="{x:Type l:EntriesEditor}">
<Setter Property="EntryTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderThickness="2"
BorderBrush="Red">
<ContentPresenter Content="{Binding}" />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
<DockPanel Height="200">
<UniformGrid Columns="2"
DockPanel.Dock="Bottom">
<Button Content="Add" />
<Button Content="Remove" />
</UniformGrid>
<ListBox ItemsSource="{Binding Entries}"
ItemTemplate="{Binding EntryTemplate, RelativeSource={RelativeSource AncestorType={x:Type l:EntriesEditor}}}"/>
</DockPanel>
But that doesn't seem right.

Why does the ExtentWidth stay at 10.003?

So I have a ListBox that has a DataTemplate which has a Grid which has a RichTextBox.
For some reason when you type into the RichTextBox it puts each character on a separate line. Digging into this, I find out that the ExtentWidth is equal to 10.003. Why? I have no idea. I was hoping someone could explain to me why and give a nice solution to make it stop doing this.
I did notice that if you set a width on the grid's column, it fixes it, but I don't want a static width on my grid's column.
Below is an example of the problem. I am using .Net 4 and VS 2010.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<ListBox
DockPanel.Dock="Top"
x:Name="TestListBox">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Test}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Column="0" Grid.Row="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Grid.Row="0" Text="Test" />
<RichTextBox
Grid.Column="1" Grid.Row="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<local:Test Name="Test1" />
<local:Test Name="Test2" />
</ListBox>
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class Test
{
public string Name { get; set; }
public Test()
{
}
}
}
So I figured out a workaround. If you trade out the ListBox for a DataGrid and use DataGridTemplateColumns with the Width of the TemplateColumn set to "*" then it seems to work.
<DataGrid
x:Name="TestDataGrid"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
>
<DataGrid.Columns >
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<RichTextBox />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<local:Test Name="Test1" />
<local:Test Name="Test2" />
</DataGrid>
Note I also set the CanUserResizeColumns to False because if you don't and they resize them, the ExtentWidth will snap back to 10.

Stretching the items in a WPF ListView within a ViewBox

I have a frustrating problem that I would much appreciate some help with. I have a ListView within a ViewBox and I can't get the items within the ListView to stretch horizontally.
Here is the XAML:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="Window1">
<Window.Resources>
<local:Inning x:Key="inning">
<local:Inning.Skins>
<local:Skin SkinNumber="1"></local:Skin>
<local:Skin SkinNumber="2"></local:Skin>
<local:Skin SkinNumber="3"></local:Skin>
</local:Inning.Skins>
</local:Inning>
</Window.Resources>
<Grid DataContext="{StaticResource inning}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Background="Black"
Text="SKINS"
Foreground="White"
FontSize="80"
FontWeight="Bold"
HorizontalAlignment="Center"></TextBlock>
<Grid Margin="2"
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewbox>
<ListView Name="lvSkinNumbers"
ItemsSource="{Binding Skins}"
HorizontalAlignment="Stretch"
Background="Black"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Black"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="1">
<ListView Name="lvFirstInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="1"
HorizontalContentAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="2"
HorizontalAlignment="Stretch">
<ListView Name="lvSecondInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="2"
HorizontalAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
</Grid>
</Grid>
</Window>
Here is the code behind with the definitions of the Skin and Inning objects:
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication3
{
public class Inning
{
private ObservableCollection<Skin> _skins = new ObservableCollection<Skin>();
public ObservableCollection<Skin> Skins
{
get { return _skins; }
set { _skins = value; }
}
}
public class Skin : INotifyPropertyChanged
{
public Skin()
{
}
public Skin( int skinNumber, Inning inning )
{
this.SkinNumber = skinNumber;
this.Inning = inning;
}
public Inning Inning { get; set; }
public int SkinNumber { get; set; }
public int SkinCount
{
get { return this.Inning.Skins.Count; }
}
public void Notify( string propertyName )
{
if ( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
The number of skins that can occur in an inning can change, and can be changed by the user, so I've put the ListViews into ViewBoxes so they automatically resize accordingly when the number of skins change. The resulting window can be seen here: http://i52.tinypic.com/244wqpl.jpg
I've tried all sorts of combinations of HorzontalAlignment="Stretch" and HorizontalContentAlignment="Stretch" and tried modifying the ItemsPanel template but I can't for the life of me seem to figure out how to get the ListView to stretch horizontally. Is what I'm trying to do impossible without some code behind to alter the width of the ListView dynamically? Or am I missing something really simple?
Any help that anyone can offer would be most appreciated.
Thanks,
Matthew
Try setting the ItemContainerStyle for your ListView to something like:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You also might need to set <Viewbox Stretch="Fill"/>.
After this, I think you can remove all those other "HorizontalAlignment = Stretch" and "HorizontalContentAlignment = Stretch" setters in your code since it probably won't be necessary anymore.
Unexpectedly setting ScrollViewer.HorizontalScrollBarVisibility="Disabled" also worked for me:
<ListView ItemsSource="{Binding SourceList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">

Removing left and right border sides from listbox datatemplate

Currently I have specified borders for all datatemplated items in my horizontal listbox which is fine because I DO want borders for all individual listboxitems, but I would like to remove the left border from the first item and the right border from the last item. Is this even possible?
Xaml:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="White" BorderThickness="1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Thanks
It is, with a DataTemplateSelector, you just need to implement the logic to select the correct DataTemplate, here's a fulle example with your code. You should be able to just copy / paste the following code in your project. There are comments in there that explain what's going on. Hope it helps!
XAML:
<Window x:Class="StackOverflowTests.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:StackOverflowTests"
Title="Window1"
x:Name="window1"
Width="800"
Height="600">
<Window.Resources>
<!-- Instantiate the DataTemplateSelector -->
<local:ItemDataTemplateSelector x:Key="ItemDataTemplateSelector" />
</Window.Resources>
<!-- Assign the DataTemplateSelector to the ListBox's ItemTemplateSelector -->
<ListBox Margin="8" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource ItemDataTemplateSelector}">
<ListBox.Resources>
<!-- Template without Left border -->
<DataTemplate x:Key="firstItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="0,1,1,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
<!-- Template with all borders -->
<DataTemplate x:Key="regularItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="1,1,1,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
<!-- Template without the Right border -->
<DataTemplate x:Key="lastItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="1,1,0,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Window>
C#:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowTests
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new List<Person>()
{
new Person() { Name = "Jim Morrison" },
new Person() { Name = "Ozzy Osbourne" },
new Person() { Name = "Slash" },
new Person() { Name = "Jimmy Page" }
};
}
}
public class Person
{
public string Name { get; set; }
}
public class ItemDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
// get the ListBoxItem
ListBoxItem listBoxItem = element.TemplatedParent as ListBoxItem;
// get the ListBoxItem's owner ListBox
ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
// get the index of the item in the ListBox
int index = listBox.Items.IndexOf(item);
// based on the index select the template
if (index == 0)
return element.FindResource("firstItemTemplate") as DataTemplate;
else if (index == listBox.Items.Count - 1)
return element.FindResource("lastItemTemplate") as DataTemplate;
else
return element.FindResource("regularItemTemplate") as DataTemplate;
}
}
}

In a horizontal listbox how do I align items to the top?

In a horizontal listbox how do I align items to the top?
I have ran out of ideas of where to stick a VerticalAlignment="Top".
<Window.Resources>
<DataTemplate DataType="{x:Type l:MyType}">
<Grid VerticalAlignment="Top">
<TextBlock VerticalAlignment="Top" Text="{Binding MyValue}" Background="Yellow"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="listBox" ItemsSource="{Binding}" VerticalAlignment="Top" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Content="{Binding}" VerticalAlignment="Top" VerticalContentAlignment="Top"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
using System.Windows;
namespace WpfApplication5 {
public partial class Window1 :Window {
public Window1() {
InitializeComponent();
this.listBox.ItemsSource = new MyType[] {
new MyType{ MyValue = "Tall\nItem" },
new MyType{ MyValue = "I want this aligned to the top" } };
}
}
public class MyType {
public string MyValue { get; set; }
}
}
You need to set the VerticalContentAlignment on the list box, all the other alignments you have in there can be removed as well once that is set.
....
<ListBox Name="listBox" ItemsSource="{Binding}" VerticalContentAlignment="Top" >
....
The default for the VerticalContentAlightment on your ListBox is 'Center', so even though you were setting the VerticalAlignment elsewhere it was just aligning to the top of the ListBoxItems, which were still centered instead of streched or placed at the top. If you set the VerticalContentAlignment to Stretch then you'd see the other VerticalAlignment="Top" declarations work.

Resources