Custom Layout Control and the Logical Tree - c#

I've been working on a control for a while that can be used to repeat a layout in our application. It's working great but the problem is that the controls inside the layout control aren't participating in the logical tree.
It's a custom control with a left header, a right header a body and a standard footer.
Generic.xaml:
<Style TargetType="{x:Type local:FrameControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FrameControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Border VerticalAlignment="Top" Height="50" Background="LightGray"/>
<DockPanel Background="{TemplateBinding HeaderColor}">
<ItemsControl ItemsSource="{TemplateBinding HeaderLeftContent}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel DockPanel.Dock="Left" FlowDirection="LeftToRight" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl ItemsSource="{TemplateBinding HeaderRightContent}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel DockPanel.Dock="Right" Height="40"
Margin="5, 5, 5, 0" VerticalAlignment="Top"
Orientation="Horizontal" FlowDirection="RightToLeft"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
<Border BorderThickness="1" BorderBrush="Black" Margin="0,49,0,0" Background="White">
<ItemsControl ItemsSource="{TemplateBinding BodyContent}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel Background="White" Margin="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
<Border VerticalAlignment="Bottom" Height="0" Background="LightGray"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
FrameControl.cs
public class FrameControl : Control
{
public Brush HeaderColor
{
get { return (Brush)GetValue(HeaderColorProperty); }
set { SetValue(HeaderColorProperty, value); }
}
public static readonly DependencyProperty HeaderColorProperty =
DependencyProperty.Register("HeaderColor", typeof(Brush), typeof(FrameControl), new UIPropertyMetadata(null));
public Collection<Object> HeaderLeftContent
{
get { return (Collection<Object>)GetValue(HeaderLeftContentProperty); }
set { SetValue(HeaderLeftContentProperty, value); }
}
public static readonly DependencyProperty HeaderLeftContentProperty =
DependencyProperty.Register("HeaderLeftContent", typeof(Collection<Object>), typeof(FrameControl),
new UIPropertyMetadata(null));
public Collection<Object> HeaderRightContent
{
get { return (Collection<Object>)GetValue(HeaderRightContentProperty); }
set { SetValue(HeaderRightContentProperty, value); }
}
public static readonly DependencyProperty HeaderRightContentProperty =
DependencyProperty.Register("HeaderRightContent", typeof(Collection<Object>), typeof(FrameControl),
new UIPropertyMetadata(null));
public Collection<Object> BodyContent
{
get { return (Collection<Object>)GetValue(BodyContentProperty); }
set { SetValue(BodyContentProperty, value); }
}
public static readonly DependencyProperty BodyContentProperty =
DependencyProperty.Register("BodyContent", typeof(Collection<Object>), typeof(FrameControl),
new UIPropertyMetadata(null));
static FrameControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FrameControl), new FrameworkPropertyMetadata(typeof(FrameControl)));
}
public FrameControl()
{
SetValue(HeaderLeftContentProperty, new Collection<Object>());
SetValue(HeaderRightContentProperty, new Collection<Object>());
SetValue(BodyContentProperty, new Collection<Object>());
}
}
Usage example
<fc:FrameControl>
<fc:FrameControl.HeaderLeftContent>
<Label Content="Left content of the header"/>
</fc:FrameControl.HeaderLeftContent>
<fc:FrameControl.HeaderRightContent>
<Button>
<TextBlock Text="Example button"/>
</Button>
<ComboBox SelectedIndex="0">
<ComboBoxItem Content="Filter A"/>
<ComboBoxItem Content="Filter B"/>
<ComboBoxItem Content="Filter C"/>
</ComboBox>
</fc:FrameControl.HeaderRightContent>
<fc:FrameControl.BodyContent>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="name"/>
<TextBox Grid.Row="0" Grid.Column="2"/>
<Label Grid.Row="1" Grid.Column="0" Content="Type name"/>
<TextBox Grid.Row="1" Grid.Column="2" />
<Label Grid.Row="2" Grid.Column="0" Content="Keyword"/>
<TextBox Grid.Row="2" Grid.Column="2"/>
<Label Grid.Row="3" Grid.Column="0" Content="Parameter"/>
<ComboBox Grid.Row="4" Grid.Column="0" SelectedIndex="0">
<ComboBoxItem Content="Width"/>
<ComboBoxItem Content="Height"/>
</ComboBox>
<ComboBox Grid.Row="4" Grid.Column="1" SelectedIndex="0">
<ComboBoxItem Content="..."/>
<ComboBoxItem Content="="/>
<ComboBoxItem Content="<"/>
<ComboBoxItem Content=">"/>
</ComboBox>
<TextBox Grid.Row="4" Grid.Column="2"/>
</Grid>
</fc:FrameControl.BodyContent>
</fc:FrameControl>
I've tried using different type of properties to hold the elements for the different region like UIElementCollection but that made no difference. I also tried manually adding the controls to the logical tree, but I did not succeed in that. Am I missing something?

You say that your actual problem is that you cannot bind using ElementName with this control. Elements that are inside the FrameControl cannot be approached this way. However, that problem has absolutely nothing to do with the visual tree. Those elements are in the visual tree, but you just can't access them because they were defined inside a ControlTemplate.
You can use the ControlTemplate.FindName method to 'find' the elements for you. However, you need to have an element that the ControlTemplate has been applied to and a named element in the ControlTemplate:
// Assuming that your DockPanel in the ControlTemplate was named DockPanel
DockPanel dockPanel =
frameControl.Template.FindName("DockPanel", frameControl) as DockPanel;
if (dockPanel != null) // You must check for null
{
DoSomethingHereWith(dockPanel);
}
See the linked page for more help.

Related

How to add multiple elements to the wrappanel?

I am trying to insert several StackPanel inside the WrapPanel as you can see the XAML below:
Only the TextBlock inside the StackPanel will be modified so as not to repeat the Title and Text.
<Window x:Class="ambient_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ambient_test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer CanContentScroll="True">
<WrapPanel x:Name="wrappanel">
<StackPanel x:Name="panel1" Width="120" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#FF38C59F"></StackPanel>
<TextBlock Grid.Row="2" Text="Title 1" Foreground="LightGray" VerticalAlignment="Top" TextAlignment="Center"/>
<TextBlock Grid.Row="1" Text="Text 1" Foreground="#FF747474" TextAlignment="Center" Margin="0 15 0 0"/>
</Grid>
</StackPanel>
<StackPanel x:Name="panel2" Width="120" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#FF38C59F"></StackPanel>
<TextBlock Grid.Row="2" Text="Title 2" Foreground="LightGray" VerticalAlignment="Top" TextAlignment="Center"/>
<TextBlock Grid.Row="1" Text="Text 2" Foreground="#FF747474" TextAlignment="Center" Margin="0 15 0 0"/>
</Grid>
</StackPanel>
</WrapPanel>
</ScrollViewer>
</Grid>
</Window>
I created a class called Info to change the Title and the Text. In the method constructor I have a for loop that will add 10 contents to the List.
public MainWindow()
{
InitializeComponent();
List<Info> infos = new List<Info>();
for (int i = 0; i < 10; i++)
{
infos.Add(new Info()
{
Title = $"title {i}",
Text = $"text {i}"
});
}
}
public class Info
{
public string Title { get; set; }
public string Text { get; set; }
}
For example, my List has 100 records, so I need to add 100 StackPanel inside my WrapPanel following the same Title and Text as the List.
Is there any way to do this? For example using Binding?
You would use an ItemsControl that uses a WrapPanel as its ItemsPanel, and defines the layout of the items by an appropriate DataTemplate:
<ItemsControl x:Name="itemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Title}" .../>
<TextBlock Grid.Row="1" Text="{Binding Text}" .../>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and assign the List<Info> to its ItemsSource property:
itemsControl.ItemsSource = infos;

WPF ListView - detect ListVewItem when selected item is not clicked

I'm trying to get an item in ListView in WPF when user doesn't select an item by clicking on it but the user taps on it.
Is there a way to achieve this in WPF ListView.
ListView dataSource is filled by a dynamic collection in code-behind.
I'm getting a null object when user taps on it.
Item selectedItem = (Item)lv_CartItems.SelectedItem;
this is how my data-template looks like for listview.
<ListView.ItemTemplate>
<DataTemplate>
<Viewbox>
<Grid Width="230" Height="110" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width=".1*" />
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1"
Grid.Row="0" Grid.Column="0"
Grid.ColumnSpan="6" Grid.RowSpan="3" >
</Border>
<Viewbox Grid.Row="0" >
<Image Name="img_ItemImage"
Source="{Binding Image, Mode=TwoWay }"
Width="20" Height=" 25" />
</Viewbox>
<Viewbox Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Top" >
<TextBlock Name="lbl_ItemName" TextWrapping="Wrap" Width="180" Foreground="Gray"
Text="{Binding Name , Mode=TwoWay }" Tag="{Binding SKU_No,Mode=TwoWay}" >
</TextBlock>
</Viewbox>
<Viewbox Grid.Row="1" Margin="10,0" VerticalAlignment="Top" >
<TextBlock Foreground="Gray" >Qty:</TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Margin="0,0" VerticalAlignment="Top" >
<StackPanel Orientation="Horizontal" >
<Button Name="btn_Minus" FontWeight="ExtraBold" Padding="0" Width="12"
Resources="{StaticResource cartitembutton}" Click="btn_Minus_Click" >
<Image Source="/Resources\Icons\minus.png" ></Image>
</Button>
<Border BorderThickness="1" Margin="2,0" Width="13" CornerRadius="2" BorderBrush="LightGray" >
<TextBlock Name="lbl_Quantity" FontWeight="Bold" Foreground="Gray"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Quantity , Mode=TwoWay }">
</TextBlock>
</Border>
<Button Name="btn_Increment" FontWeight="ExtraBold" Width="12"
Resources="{StaticResource cartitembutton}"
Padding="0"
Click="btn_Increment_Click">
<Image Source="/Resources\Icons\union_10.png" ></Image>
</Button>
</StackPanel>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="2" Margin="5,0"
HorizontalAlignment="Left" Grid.ColumnSpan="3" >
<TextBlock Name="lbl_Price" FontWeight="DemiBold"
Text="{Binding Price , Mode=TwoWay}" ></TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3"
VerticalAlignment="Top" Margin="0,0" >
<TextBlock Name="lbl_Appearence"
Text="{Binding Appearance , Mode=TwoWay }"
TextWrapping="Wrap" Foreground="Gray" Width="210" >
</TextBlock>
</Viewbox>
<Viewbox Grid.Column="5" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="2,2"
>
<Button Name="btn_DeleteItem"
Click="btn_DeleteItem_Click"
Resources="{StaticResource cartitembutton}" >
<Image Source="/Resources/Icons/delete.png" ></Image>
</Button>
</Viewbox>
</Grid>
</Viewbox>
</DataTemplate>
</ListView.ItemTemplate>
You should use data binding to retrieve the selected item. The source property will be automatically updated when the ListView.SelectedItem changes.
Touch is handled the same way as click is handled. The framework converts touch events to mouse events.
I noticed that you wrapped every element into a ViewBox. That is not necessary and will only degrade performance. When the elements are hosted inside a Grid they will resize automatically by default. Exceptions are elements that need an initial size like Shape or Image. But since the item containers won't resize themselves, the content of the containers won't resize too.
To force all elements to occupy max horizontal space you can set a Style that targets the item container:
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<Item>),
typeof(MainWindow),
new PropertyMetadata(default(ObservableCollection<Item>)));
public ObservableCollection<Item> Items
{
get => (ObservableCollection<Item>) GetValue(MainWindow.ItemsProperty);
set => SetValue(MainWindow.ItemsProperty, value);
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
"SelectedItem",
typeof(Item),
typeof(MainWindow),
new PropertyMetadata(default(Item), MainWindow.OnSelectedItemChanged));
public Item SelectedItem
{
get => (Item) GetValue(MainWindow.SelectedItemProperty);
set => SetValue(MainWindow.SelectedItemProperty, value);
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Items = new ObservableCollection<Item>();
// Initialize data source
CreateItems();
}
// Property changed callback of the SelectedItem property
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Reference to instance members in a class scope
var _this = d as MainWindow;
if (e.NewValue is ItemCollection selectedItem)
{
// Handle currently selected item
}
}
}
MainWindow.xaml
<Window>
<ListView ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}" />
</Window>

Problems getting spreadsheet grid look with Viewbox and ListView in UWP Project

I'm trying to create a 'spreadsheet like' grid with column headers showing information from a set of player objects with the cell contents maximised in size. To do this I have a UWP app with a ListView bound to the collection of Player objects. The ListView.ItemsPanel uses a UniformGridPanel obtained from here
The UniformGridPanel has a Rows property bound to the player count which can vary, and one Column. The UniformGridPanel is used to show ALL players, giving each player item equal height, so no player is partially displayed and there is no scrolling. The UniformGridPanel is working fine.
The ListView.ItemTemplate is a Gridof a fixed number of columns representing data about each player.
My problems are:
The info in each 'Cell' is not big enough. I would like each 'cell' content maximised, and the grid layout to remain spreadsheet like, i.e. square, with rows and columns aligned.
I don't know how to put Headings on each of the Columns that would align with the cells in each column.
So the display looks like this below. This is the closest to what I want, but for the size of the cell contents and the column headers.
As you can see there is plenty of room to make the numbers and letters bigger. I've tried many variations using Viewbox in the XAML. For example, wrapping the ListView in a Viewbox produces this which is no good as the columns are misaligned.
If I put Viewbox around each Bordercontrol that defines a 'cell' I still get problems with the layout too.
I've tagged this post with WPF despite my problem being in a UWP context in case someone has had a similar experience in that domain that could be applied here.
CODE BEHIND:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
ObservableCollection<Player> players = new ObservableCollection<Player>();
players.Add(new Player("John", "Smith",1,99,11,1));
players.Add(new Player("Fred", "Blogs", 19, 25, 11, 1));
players.Add(new Player("Andrew", "Sykes", 19, 25, 11, 2));
players.Add(new Player("Dean", "Andrews", 19, 179, 11, 1));
this.DataContext = players;
}
}
XAML:
<Grid x:Name="root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid x:Name="grdPlayers" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!--<Viewbox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" StretchDirection="Both" Stretch="Fill">-->
<ListView x:Name="lvwPlayers"
Padding="0"
BorderBrush="Red"
BorderThickness="5"
ItemsSource="{Binding}"
SelectionMode="None"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollMode="Disabled"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<local:UniformGrid x:Name="pnlPlayer" Rows="{Binding Count}" Columns="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="grdPlayerRow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Black" BorderThickness="1" Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding Initials}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Border>
<Border Grid.Column="1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding HighScore}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Border>
<Border Grid.Column="2" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding LowScore}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Border>
<Border Grid.Column="3" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding Average}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Border>
<Border Grid.Column="4" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding TonCount}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--</Viewbox>-->
</Grid>
</Grid>
PLAYER CODE:
public class Player : INotifyPropertyChanged
{
public Player(string firstName, string lastName, int low, int high, int avg, int tonCount)
{
FirstName = firstName;
LastName = lastName;
LowScore = low;
HighScore = high;
Average = avg;
TonCount = tonCount;
}
protected void OnPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
public event PropertyChangedEventHandler PropertyChanged;
private int _tonCount;
public int TonCount
{
get { return _tonCount; }
set
{
_tonCount = value;
OnPropertyChanged();
}
}
private double _average;
public double Average
{
get { return _average; }
set
{
_average = value;
OnPropertyChanged();
}
}
private int _lowScore;
public int LowScore
{
get { return _lowScore; }
set
{
_lowScore = value;
OnPropertyChanged();
}
}
private int _highScore;
public int HighScore
{
get { return _highScore; }
set
{
_highScore = value;
OnPropertyChanged();
}
}
public string FullName
{
get { return $"{FirstName} {LastName}"; }
}
private string _firstName = string.Empty;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged();
OnPropertyChanged("Initials");
}
}
private string _lastName = string.Empty;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged();
OnPropertyChanged("Initials");
}
}
public string Initials
{
get
{
return FirstName.Substring(0, 1) + LastName.Substring(0, 1).ToUpper();
}
}
public override string ToString()
{
return FullName;
}
}
UPDATE:
#Grace Feng, your answer to use the lvwPlayers loaded event before assignment of the DataContext gives the following output which is NOT what I wanted. To begin with the rows are of different heights for no reason??:
Base on your description, I think there is no need to use the UniformGridPanel, you can simply customize a Grid to make its Height equals its Width. For example, create a TemplatedControl like this:
public sealed class SquareGrid : Grid
{
public SquareGrid()
{
this.Loaded += SquareGrid_Loaded;
}
private void SquareGrid_Loaded(object sender, RoutedEventArgs e)
{
this.Height = this.ActualWidth;
}
}
And for the question of "I don't know how to put Headings on each of the Columns that would align with the cells in each column.", I think you can set them as the Header of ListView. But it's very wise of you to use ViewBox to make the Text fill the size. Here comes the demo:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="900">
<ListView x:Name="lvwPlayers"
Padding="0"
BorderBrush="Red"
BorderThickness="5"
ItemsSource="{Binding}"
SelectionMode="None"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollMode="Disabled"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<!--<ListView.ItemsPanel>
<ItemsPanelTemplate>
<local:UniformGrid x:Name="pnlPlayer" Rows="{Binding Count}" Columns="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>-->
<ListView.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<local:SquareGrid BorderBrush="Black" BorderThickness="1">
<Viewbox>
<TextBlock Text="Initials" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid BorderBrush="Black" BorderThickness="1" Grid.Column="1">
<Viewbox>
<TextBlock Text="HighScore" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid BorderBrush="Black" BorderThickness="1" Grid.Column="2">
<Viewbox>
<TextBlock Text="LowScore" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid BorderBrush="Black" BorderThickness="1" Grid.Column="3">
<Viewbox>
<TextBlock Text="Average" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid BorderBrush="Black" BorderThickness="1" Grid.Column="4">
<Viewbox>
<TextBlock Text="TonCount" />
</Viewbox>
</local:SquareGrid>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="grdPlayerRow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<local:SquareGrid Grid.Column="0" BorderBrush="Black" BorderThickness="1" Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding Initials}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid Grid.Column="1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding HighScore}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid Grid.Column="2" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding LowScore}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid Grid.Column="3" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding Average}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Viewbox>
</local:SquareGrid>
<local:SquareGrid Grid.Column="4" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding TonCount}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Viewbox>
</local:SquareGrid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Rendering Image:

How to attach trigger on ControlTemplate items?

We are trying to bind Trigger on Stackpanel loaded while using RadMessageBox control. Example :
<!-- CustomMessageBox Template -->
<ControlTemplate x:Key="MessageBoxTemplate" TargetType="messageBox:RadMessageBoxControl">
<Border Padding="12" Background="{StaticResource PhoneChromeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentControl x:Name="PART_Title" Grid.Row="0"
HorizontalContentAlignment="Left"
FontSize="{StaticResource PhoneFontSizeLarge}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"
Margin="{StaticResource PhoneMargin}"/>
<ContentControl HorizontalContentAlignment="Left" Grid.Row="1"
VerticalContentAlignment="Top" Margin="{StaticResource PhoneMargin}"
x:Name="PART_Message"/>
<CheckBox x:Name="PART_CheckBox" Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"/>
<ContentControl x:Name="PART_ButtonsContainer" Grid.Row="3"
HorizontalContentAlignment="Stretch" Margin="12,0" Width="440">
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel x:Name="PART_ButtonsPanel"
Orientation="Vertical" HorizontalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding DataContext.CustomMessageBoxStackPanelLoadedCommand}" CommandParameter="{Binding PART_ButtonsPanel}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Border>
</ControlTemplate>
The ControlTemplate is inside Page.Resources.
We are unable to Trigger stackpanel loaded event. Code behind file :
private bool _CustomMessageBoxStackPanelLoadedCommandCanExecute = true;
bool CustomMessageBoxStackPanelLoadedCommandCanExecute
{
get
{
return _CustomMessageBoxStackPanelLoadedCommandCanExecute;
}
set
{
if (_CustomMessageBoxStackPanelLoadedCommandCanExecute == value)
{
return;
}
_CustomMessageBoxStackPanelLoadedCommandCanExecute = value;
RaisePropertyChanged("CustomMessageBoxStackPanelLoadedCommandCanExecute");
if (_CustomMessageBoxStackPanelLoadedCommand != null)
_CustomMessageBoxStackPanelLoadedCommand.RaiseCanExecuteChanged();
}
}
private RelayCommand<string> _CustomMessageBoxStackPanelLoadedCommand;
public ICommand CustomMessageBoxStackPanelLoadedCommand
{
get
{
if (_CustomMessageBoxStackPanelLoadedCommand == null)
{
_CustomMessageBoxStackPanelLoadedCommand = new RelayCommand<string>(CustomMessageBoxStackPanelLoaded, (data) => CustomMessageBoxStackPanelLoadedCommandCanExecute);
}
return _CustomMessageBoxStackPanelLoadedCommand;
}
}
private void CustomMessageBoxStackPanelLoaded(object obj)
{
System.Diagnostics.Debug.WriteLine("Hello");
}
The problem is with your CommandBinding. your binding is:
Command="{Binding DataContext.CustomMessageBoxStackPanelLoadedCommand}"
that means that in the DataContext class of your control there is any object name DataContext which has CustomMessageBoxStackPanelLoadedCommand as a property in it. But that is not the case. Such type of syntax is used in RelativeSource Binding.
But here change your binding as:
Command="{Binding CustomMessageBoxStackPanelLoadedCommand}"
So binding engine will now find this command directly inside your DataContext. (I hope there is not any problem with giving datacontext to your views.)
also not sure why you have written
CommandParameter="{Binding PART_ButtonsPanel}"
like this. what are you trying to do there?

Binding ObservableCollection of List

In my Windows Phone application I have a listBox with ContentItems binding :
private ObservableCollection<ContentItemViewModel> _contentItems;
public ObservableCollection<ContentItemViewModel> ContentItems
{
get { return _contentItems; }
}
<ListBox x:Name="ContentListBox" Margin="0,117,12,0" VirtualizingStackPanel.VirtualizationMode="Standard" Logic1:TiltEffect.IsTiltEnabled="True" ItemsSource="{Binding ContentItems}" Tap="ContentListBox_Tap" MinHeight="656" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="vertical" >
<Grid Height="{Binding ItemHeight}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" x:Name="itemIco1" Width="Auto" Height="Auto" HorizontalAlignment="Left" Source="{Binding IconURL}" Stretch="Fill" CacheMode="BitmapCache" VerticalAlignment="Top" Margin="5,5,5,5" Visibility="Visible"/>
<ListBox IsHitTestVisible="False" Grid.Column="1" VerticalAlignment="Center" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<TextBlock x:Name="ContentCategoryTitle" Height="70" Text="{Binding ContentTitle}" Width="460" Margin="5,34,0,0" TextTrimming="WordEllipsis" TextWrapping="NoWrap" FontSize="28" FontFamily="Segoe WP Semibold" Foreground="#FFF7F7F7" VerticalAlignment="Bottom" />
</ListBox>
</Grid>
<Rectangle Fill="#FF585858" Height="1" Margin="0,0,0,0" Width="460" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Is it possible to binding not an ObservableCollection<ContentItemViewModel>, but - ObservableCollection<List<ContentItemViewModel>> ?
Yes that is possible, if you have a collection of collections. Or why not this?
ObservableCollection<ObservableCollection<ContentItemViewModel>>
If you want your UI to be notified of collection changes to your sub collections.
Update
For example:
View Model
public ObservableCollection<ObservableCollection<ContentItemViewModel>> ContentItems
{
get { return _contentItems; }
set { _contentItems = value; // Notify of property change here, this allows you to change the ContentItems reference after view model construction }
}
public MyViewModel()
{
// Populate content items
this.ContentItems = new ObservableCollection
{
new ObservableCollection { new ContentItemViewModel() },
new ObservableCollection { new ContentItemViewModel(), new ContentItemViewModel() }
};
}
View
<ListBox ItemsSource="{Binding ContentItems}" ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding MyContentItemViewModelProperty}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>

Categories