List of groups of data with multiple interactive UI elements - wpf

I'm currently working on a WPF application, it's my first so I'm learning as I go.
The basics are fine but I've hit a problem with what I'm trying to do at the moment. There seem to be tons of ways that might work but I'm pretty sure that I'm going down the path of making it harder than it needs to be.
What I need is some guidance on the simplest way to implement a piece of UI.
I'm using c# 4, wpf and the MVVM pattern.
The data I'm wanting to display looks like this:
obj1 ----< obj2 ----< obj3
i.e. a single obj1 has many obj2's which has many (specifically 1-3) obj3's
On my screen, I want to see a list/datagrid type view of the obj2 elements with the associated obj3 elements nested underneath.
The obj2 elements need to show a few text values and a check box (that will fire the appropriate command in the view model when toggled).
The obj3 elements need to show an image, possibly some text and be clickable (again, firing the appropriate command to the view model).
At first I looked at creating a custom control for the obj3 element, a custom control based on ItemsControl for a list of obj3 elements, another custom control for the obj2 element and finally another custom control to display a list of obj2 elements.
However, a little way in, I've got the feeling that I've massively over-complicated it.
Can I just use user controls? Do I even need them at all or can I just use the regular List control with a template?
Some pointers would be very welcome.
Thanks.

I spent quite some time getting this working as I wanted so thought I'd share...
The answer turned out to be a nested list control with data templates and associated view models for the items in each list. The list control is so much more flexible in WPF than Winforms that you can do pretty much anything with it.
I've found many different things that have helped on many different sites but the core details came from Dr.WPF: http://drwpf.com/blog/category/collections/
I won't post all the code as it's rather long. The core of it though is to first setup the list view in the user control:
<ListView Name="list" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" Focusable="False"
IsSynchronizedWithCurrentItem="True" ItemsPanel="{StaticResource VerticalItemsPanel}"
ItemContainerStyle="{StaticResource Obj2ContainerStyle}" ItemsSource="{Binding Obj2List}">
</ListView>
The keys things here are the ItemsPanel and ItemContainerStyle. These define the properties of the panel that contains all list items and the style for each list item respectively.
They are contained in the resources for the user control.
<ItemsPanelTemplate x:Key="VerticalItemsPanel">
<StackPanel Orientation="Vertical" Focusable="False" HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
<Style x:Key="Obj2ContainerStyle" TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Enabled}" Value="False">
<Setter Property="Background" Value="LightSalmon"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Enabled}" Value="True">
<Setter Property="Background" Value="PaleGreen"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
I also have the data templates. The first tells the system to use the second for displaying objects of type Obj2ViewModel
<DataTemplate DataType="{x:Type src:Obj2ViewModel}">
<ContentControl x:Name="Obj2Host" Focusable="False" Content="{Binding}"
ContentTemplate="{StaticResource Obj2ViewTemplate}" />
</DataTemplate>
<DataTemplate x:Key="Obj2ViewTemplate">
<DataTemplate.Resources>
<ItemsPanelTemplate x:Key="HorizontalItemsPanel">
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style x:Key="Obj3ContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Rectangle StrokeThickness="1" Stroke="#FF000000" Margin="0" />
<ContentPresenter x:Name="ContentHost" Margin="{TemplateBinding Padding}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="LabelStyle" TargetType="{x:Type Label}">
<Setter Property="Padding" Value="0,0,4,0" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style x:Key="DataStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Padding" Value="0,0,4,0" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</DataTemplate.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{StaticResource Label1Text}" Style="{StaticResource LabelStyle}"/>
<Label Grid.Row="1" Grid.Column="0" Content="{StaticResource Label2Text}" Style="{StaticResource LabelStyle}"/>
<Label Grid.Row="2" Grid.Column="0" Content="{StaticResource Label3Text}" Style="{StaticResource LabelStyle}"/>
<Label Grid.Row="3" Grid.Column="0" Content="{StaticResource Label4Text}" Style="{StaticResource LabelStyle}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Property1}" Style="{StaticResource DataStyle}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Property2}" Style="{StaticResource DataStyle}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Property3}" Style="{StaticResource DataStyle}"/>
<TextBlock Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding Property4}" Style="{StaticResource DataStyle}"/>
<ListView Grid.Row="0" Grid.RowSpan="3" Grid.Column="2" Name="Obj3List"
HorizontalAlignment="Right" VerticalAlignment="Center"
IsSynchronizedWithCurrentItem="True"
ItemsPanel="{StaticResource HorizontalItemsPanel}"
ItemsSource="{Binding Obj3s}"
ItemContainerStyle="{StaticResource Obj3ContainerStyle}"
BorderThickness="0"
Background="Transparent">
</ListView>
<CheckBox Grid.Row="0" Grid.RowSpan="4" Grid.Column="3" VerticalAlignment="Center"
IsChecked="{Binding Property5}" IsEnabled="{Binding NotExpired}" >
</CheckBox>
<Image Grid.Row="0" Grid.RowSpan="4" Grid.Column="4" Source="{StaticResource DeleteIcon}" Stretch="None">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<cmd:EventToCommand PassEventArgsToCommand="False" Command="{Binding DeleteObj2Command, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</Grid>
</DataTemplate>
The second data template above contains another list view. This one containing Obj3 items.
The style for these is specified within the DataTemplate's resources section.
Finally, the User control's resources also contains the data templates for the Obj3 elements:
<DataTemplate DataType="{x:Type src:Obj3ViewModel}">
<ContentControl x:Name="Obj3Host" Focusable="False" Content="{Binding}"
ContentTemplate="{StaticResource Obj3ViewTemplate}" />
</DataTemplate>
<DataTemplate x:Key="Obj3ViewTemplate">
<Image Source="{Binding Image}" Stretch="None">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp">
<cmd:EventToCommand PassEventArgsToCommand="False" Command="{Binding ToggleEnabledCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</DataTemplate>
Note that the EventToCommand stuff is thanks to the MVVM Light toolkit. It's not a standard .NET thing.

Related

How to achieve the complex UI using ItemControl in silverlight

I am working on creating a complex report which almost looks like shown in here image
For this I have create a collection where I will store all the descriptions and its corresponding ratings.
This collection is then I am binding to a ItemControl. The collection will be fetched from database depending on criteria's.
Now my problem is how to fragment or separate single ItemControl to look like shown in image. Should I use multiple collections which will be then bind to different ItemControl ? Can I use multiple datagrids?
I am out of ideas... Any suggestions / examples much appreciated.
Definitely do-able. Treat each block (such as Mathmatics, Arts Education etc) as an item, and you're then just dealing with an ItemsCollection. Create a style for how to present each item in that collection, and another style for how to present each property in your block (which will also feature a collection of something.
An example I have of something similar, was blocks which consisted of a heading, and a varied number of checkboxes each with a description. There could be a varying number of these blocks too.
In my view, that displayed these blocks, my xaml looked something like this:
<ScrollViewer VerticalScrollBarVisibility="Visible" MaxHeight="100">
<ItemsControl ItemsSource="{Binding FeatureFlags, Mode=TwoWay}" Style="{StaticResource FlagGroupsAndFlagsItemsControlStyle}" />
</ScrollViewer>
I then had a style for that ItemsControl defined in a resource dictionary somewhere ...
<Style x:Key="FlagGroupsAndFlagsItemsControlStyle" TargetType="ItemsControl">
<Setter Property="Width" Value="Auto" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid x:Name="FlagListGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" IsReadOnly="True" Text="{Binding Name}" />
<ListBox Grid.Row="1" Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" ItemsSource="{Binding Flags}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource FlagsListBoxItemsStyle}" Background="{Binding IsOptional, Converter={StaticResource YNToOptionalBackgroundColour}}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
And then a style for the items within that templates ListBox ...
<Style x:Key="FlagsListBoxItemsStyle" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<toolkit:WrapPanel Width="{Binding Width, ElementName=FlagListGrid, Mode=OneWay}" Orientation="Horizontal" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Width="20" Height="18" VerticalAlignment="Top" IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Width="180" MinHeight="18" VerticalAlignment="Center" Text="{Binding Description}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Unfortunately I can't show you an image of what the finished result looks like, but I hope these pointers could set you on track for your very similar problem

Can the WPF Datagrid control be made to behave like the WinForms DataGridControl in regard to column layout?

Basically, I'd like to make the WPF DataGrid control layout its columns exactly the way the WinForms DataGridView does
And more specifically, here is the behaviour I'm looking for:
The grid control should take up the space it's given (i.e. however much space is available in its parent control for it to use). Here I am referring just to the control, and not to the columns.
The columns created (whether automatically or manually) may or may not take up all this space.
If there is extra space left over after the columns are created, the last column should not be expanded to fill this space
If there is extra space left over after the columns are created, an empty column with nothing in it should not be created to fill this extra space
From what I can tell, in WPF the last two bullet points seem to be mutually exclusive and you must choose one or the other. Has anyone found a way to do both? I've searched quite a bit and haven't found quite what I'm looking for, however all the posts I'm finding tend to be a couple years old so I'm hoping someone has figured this thing out by now.
EDIT: sa_ddam213, here's a quick xaml project I put together to test your suggestion.
<Window x:Class="DataGridFix.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridFix"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="data"
ObjectType="{x:Type local:TestObject}"
MethodName="GetTestData" />
</Window.Resources>
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="150" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource data}}" />
</StackPanel>
</Window>
And here's the code behind:
namespace DataGridFix
{
public class TestObject
{
public int Id { get; set; }
public string Name { get; set; }
public static List<TestObject> GetTestData()
{
var items = new List<TestObject>();
items.Add(new TestObject() { Id = 1, Name = "Joe" });
items.Add(new TestObject() { Id = 2, Name = "Matt" });
items.Add(new TestObject() { Id = 3, Name = "Hal" });
return items;
}
}
}
Really the only noteable thing I see from your suggestion is to set HorizontalAlignment to Left. I did that, and tried setting the ColumnWidth to the various settings but had the same problem with each (except * of course... technically I can mess that one up to but I won't go into that).
If you use your mouse to expand any of the columns, and then decrease the column size then the empty filler column appears. The only other difference I noted from your post was that you put your DataGrids in a StackPanel since you had more than one of them. I tried that just for the heck of it but same result. If you see any other difference between what I'm doing and what you suggested please let me know.
In order to get the behavior you want, you probably have to modify the control template of the DataGrid.
Take a look at the code. I have gotten pretty close to the WinForms DataGridView look i think.
To remove the extra column you have to remove the filler column from the DataGridColumnHeadersPresenter. I have just commented it out. The rest is just the default template.
The other modification is to the template of the DataGrid.
By setting HorizontalAlignment="Left" on the ScrollContentPresenter, the rows no longer take up all the width of the control.
Those are the only 2 changes I have made to the default templates.
<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<!-- Remove this filler column -->
<!--<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False" />-->
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Padding="{TemplateBinding Padding}">
<ScrollViewer Focusable="false" Name="DG_ScrollViewer">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Command="{x:Static DataGrid.SelectAllCommand}"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type DataGrid}, ResourceId=DataGridSelectAllButtonStyle}}"
Focusable="false"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" />
<DataGridColumnHeadersPresenter Grid.Column="1" Name="PART_ColumnHeadersPresenter"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>
<!-- Set HorizontalAlignment="Left" to have the rows only take up the width they need and not fill the entire width of the DataGrid -->
<ScrollContentPresenter HorizontalAlignment="Left" x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar Grid.Column="1"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
UPDATE
It does indeed look like there is a difference between .NET 4 and .NET 4.5.
I develop on a Windows 8 machine with Visual Studio 2012, so as a test I tried targeting .NET 4 to see if I could replicate the wrong behavior. But it still worked fine.
To be sure, I tried running the app on a different machine with only .NET 4 installed, and here the empty rows showed up when making a column bigger and then smaller again.
The issue seems to be that the DataGridRows are not behaving properly. When running on a machine with only .NET 4 installed, they keep their current size when making the column smaller. On .NET 4.5 they resize as expected.
The new solution to get the behavior you need, is actually much simpler than the previous one.
By simply setting the HorizontalAlignment on the DataGridRows to left, and removing the filler column, it works on both .NET 4 and .NET 4.5. And there is no longer a need to replace the entire template of the DataGrid.
<Style TargetType="DataGridRow">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style TargetType="DataGridColumnHeadersPresenter">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
There ar plenty of layout options for Columns in WPF, its just a matter of choosing what you want to be displayed.
Pixel
SizeToCells
SizeToHeader
Auto
Proportional(*)
And if you set the HorizontalAlignment to Left the DataGrid will resize to fit its contents based on the ColumnWidth you picked.
Here is a example of some of the avaliable column settings
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="100" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToCells" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToHeader" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="*" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
</StackPanel>

WPF Metro Styling ListBox

I'm having a problem styling my ListBox selection background.
I used ListView originally, it was less problematic with styling but moving this to Silverlight app, I found out that it doesn't have ListView so I just used ListBox.
I want my app to be easily ported in Silverlight and also in Windows Phone so I used ListBox and now am having a trouble with the style.
My Metro app has Dark theme and I have custom ListBoxItem but am not sure why when I clicked on it, it looks like this
originally when using ListView, it looks like this
adding background color in my custom ItemsTemplate make it look this
How do I get rid of that White background and also restyle the item when hovered because it looks like this
this my XAML for listbox so far
<ListBox Grid.Row="3" Name="lvSubmeters" VerticalAlignment="Top" HorizontalAlignment="Stretch" SelectedItem="{Binding Path=SelectedListViewItem, Mode=TwoWay}" SelectedIndex="{Binding Path=SelectedListViewIndex, Mode=TwoWay}" ItemTemplate="{StaticResource SubmeterItems}" ItemsSource="{Binding Path=Store.AllItems}" Background="{x:Null}" Foreground="White">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
and also if you want my custom ItemsTemplate
<DataTemplate x:Name="SubmeterItems">
<Grid VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="166"/>
<ColumnDefinition Width="100*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding MeterID}" FontSize="24" FontWeight="Bold" Foreground="#FF429AA3" />
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding Fullname}" FontSize="16" />
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Height="95">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text=" " FontSize="24" FontWeight="Bold" Foreground="#FF429AA3" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="29*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Grid.Column="0" VerticalAlignment="Top" Text="Last Reading:" FontSize="14" Margin="0,0,5,5" />
<TextBlock Grid.Column="0" VerticalAlignment="Top" Text="New Reading:" FontSize="14" Margin="0,0,5,5" />
<TextBlock Grid.Column="0" VerticalAlignment="Top" Text="KW/H Used:" FontSize="14" Margin="0,0,5,5" />
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Grid.Column="1" VerticalAlignment="Top" Text="{Binding LastReading}" FontSize="14" Margin="0,0,5,5" FontWeight="Bold" />
<TextBlock Grid.Column="1" VerticalAlignment="Top" Text="{Binding NewReading}" FontSize="14" Margin="0,0,5,5" FontWeight="Bold" />
<TextBlock Grid.Column="1" VerticalAlignment="Top" Text="{Binding KwHUsed}" FontSize="14" Margin="0,0,5,5" FontWeight="Bold" />
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Background="Black">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding KwhUsedAmount}" FontSize="20" FontWeight="Bold" Foreground="Red" TextAlignment="Right" Margin="0,0,5,0" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="+" FontSize="20" Foreground="#99FF0000" TextAlignment="Right" Margin="0,0,5,0"/>
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding AdditionalCharges}" FontSize="20" Foreground="#99FF0000" TextAlignment="Right" Margin="0,0,5,0"/>
</StackPanel>
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding TotalAmount}" FontSize="34" FontWeight="Bold" Foreground="Green" TextAlignment="Right" Margin="0,0,5,0" />
</StackPanel>
</Grid>
</DataTemplate>
I tried taking this to Blend 4 but I can't find the way to do it.
It looks to me like your screenshots from above don't quite match the XAML that you've provided, however I think I can answer your questions.
For the white border around each item - From the way that the border changes to Purple when the item is selected, you can tell that it is part of the ItemContainer (because that is what gets selected). The ItemContainer will contain the Content of each ListBoxItem. The Content is rendered using the DataTemple (or ItemTemplate property). In your DataTemplate, you have a margin of 5 around the topmost grid, meaning that when the Content is rendered using that DataTemplate, there will be a margin around the outside of the content. That seems to be what you're getting with your white border around each item, so I'm pretty sure that's what is going on. Get rid of the margin in the top most grid of the DataTemplate, and the white border will be gone. If this isn't correct, please provide more complete XAML example (including the parent XAML element of the Listbox) because the xaml above, on its own, does not yield a white border when the Window background is Black.
Changing the colors/style of the ListBoxItem is pretty easy, and there are several ways to do it. The easiest way (in my opinion) is to add a trigger to the ItemContainerStyle that adjusts the background color on MouseOver. Here is a simple example, extending your existing ItemContainerStyle (triggers can be much more advanced, if you wish):
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="5" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Green"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Hope that helps.

Giving a button in a textbox functionality, without using a custom control but overiding the textbox template

I implemented a button into the textbox overriding the template with the code below:
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBoxBase}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border"
CornerRadius="2"
Padding="2"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1">
<Grid x:Name="LayoutGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" />
<Button x:Name="XButton"
Grid.Column="2"
Width="25"
Height="25"
Content="X"
Visibility="Visible"
BorderBrush="Transparent"
Command="{Binding ButtonClick}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The button should delete all the content in the textbox when hit. For this I would like to use the command "Command="{Binding ButtonClick}""
Can the binding be done without creating a custom control and library ?
Or how can the binding or the functionality be done ?
I am using the MVVM pattern is there a way to for example use a ViewModel and create a property to bind that to ?
For bindings in the style like that I use AttachedCommandBehaviors "ACB"
http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/
I use it for detecting clicks on items in a style that I want to detect clicks on. I haven't used it for buttons but I would think it should work just as well.
I am binding to the style's ancestor who's type is User Control this may need to be changed for your application.
You'll need to add the xmlns:acb namespace in your xaml as well see the link for details.
<ControlTemplate TargetType="{x:Type ListViewItem}">
<StackPanel x:Name="IconBorder"
acb:CommandBehavior.Event="PreviewMouseUp"
acb:CommandBehavior.Command="{Binding Path=DataContext.SelectedListItemSingleClickCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
acb:CommandBehavior.CommandParameter="{Binding}">
<DockPanel LastChildFill="False" HorizontalAlignment="Left" Width="{Binding Width, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}">
<TextBlock Text="{Binding Title}"
DockPanel.Dock="Left"
x:Name="ListItemText" />
<Image x:Name="ActionIcon"
Source="{Binding Path=DataContext.SelectedListActionIcon, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
DockPanel.Dock="Right"
acb:CommandBehavior.Event="PreviewMouseUp"
acb:CommandBehavior.Command="{Binding Path=DataContext.SelectedListActionIconClickCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
acb:CommandBehavior.CommandParameter="{Binding}">
</Image>
</DockPanel>
<ContentPresenter DockPanel.Dock="Left" />
</StackPanel>
</ControlTemplate>

Using HierarcicalDataTemplates in conjunction with TreeViewItem control templates

I am having some difficulty figuring out how to template the following TreeView item layout:
I have several items, SearchList, which contains a collection of Search, which contains a collection of DataSet (sort of, but that is beside the point). What I am having difficulty with is styling each node level the way I want. I am using MVVM, and the TreeViews ItemsSource property is set to an ObservableCollection of SearchListViewModels which in turn contain my objects all the way down the object tree.
I can successfully style the SearchList HierarchicalDataTemplate to display them correctly. Where I get hung up is on SearchTerm nodes styling. I want the DataSets to be represented in a wrap panel or uniform grid (I haven't decided yet) to the right of the SearchTerm content area. I have modified a TreeViewItem control template to behave this way I think), however if I set it in the ItemContainerStyle property of the Search HierarchicalDataTemplate, it does nothing. All that gets displayed is the content for the Search.
My Altered TreeViewItem Template
<Style TargetType="{x:Type TreeViewItem}" x:Key="AlteredTreeViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
MinWidth="19" />
<ColumnDefinition Width="0.414*" />
<ColumnDefinition Width="0.586*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Bd" HorizontalAlignment="Stretch"
Grid.Column="1" Grid.ColumnSpan="1" Background="#7F058956">
<ContentPresenter x:Name="PART_Header" Margin="10,0" />
</Border>
<WrapPanel x:Name="ItemsHost"
Grid.Column="2" IsItemsHost="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My Search Hierarchical Data Template
<HierarchicalDataTemplate DataType="{x:Type local:SearchViewModel}" ItemsSource="{Binding MySearch.Custodians}" ItemContainerStyle="{StaticResource AlteredTreeViewItem}">
<TextBlock Text="{Binding MySearch.SearchName}" Foreground="Black" FontFamily="Arial" FontSize="16"/>
</HierarchicalDataTemplate>
Surely it is possible to both style differently and have child items laid out differently? How can this be achieved?
It seems that you are pretty close to what you're after. I tried to recreate your scenario based on the code you posted and I noted some problems with it (which of course are based on my interpretation of the code you posted)
You are missing the ContentSource="Header" part of the ContentPresenter
I think you are applying the ItemContainerStyle at the wrong HierarchicalDataTemplate level. It should be specified on the parent in order to affect the children (in your case SearchListViewModel).
The default Template for TreeViewItem lays out the ContentPresenter in an Auto sized ColumnDefinition so the WrapPanel won't succesfully wrap unless you modify the ItemContainerStyle for the parent as well. I changed it to a UniformGrid in my sample below
With the changes from above and a few other things I got a result that looks like this which hopefully is pretty close to what you're after
I uploaded the sample solution here: https://www.dropbox.com/s/4v2t8imikkagueb/TreeViewAltered.zip?dl=0
And here is the Xaml code for it (too much code to post it all..)
<Window.Resources>
<!-- DataSet-->
<HierarchicalDataTemplate DataType="{x:Type data:DataSet}">
<Border BorderThickness="3"
BorderBrush="Gray"
Background="Green">
<TextBlock Text="{Binding Path=Tables[0].TableName}"
Margin="5"/>
</Border>
</HierarchicalDataTemplate>
<!-- SearchViewModel -->
<HierarchicalDataTemplate DataType="{x:Type viewModel:SearchViewModel}"
ItemsSource="{Binding DataSets}">
<TextBlock Text="{Binding DisplayName}"
Foreground="Black"
FontFamily="Arial"
FontSize="16"/>
</HierarchicalDataTemplate>
<!-- SearchListViewModel -->
<HierarchicalDataTemplate DataType="{x:Type viewModel:SearchListViewModel}"
ItemsSource="{Binding SearchList}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="19" />
<ColumnDefinition Width="0.414*" />
<ColumnDefinition Width="0.586*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Bd"
HorizontalAlignment="Stretch"
Grid.Column="1"
Grid.ColumnSpan="1"
Background="#7F058956">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<UniformGrid x:Name="ItemsHost"
Grid.Column="2"
Columns="3"
IsItemsHost="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding DisplayName}"
FontSize="20"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding SearchListViewModels}" />
</Grid>
Something I learnt a long time ago when trying to create a similar interface was that you are better using a ListBox than a TreeView.
Why?
If you only have one level of expansion (as it appears from your sample) you will a lot more control of the layout as you have a single DataTemplate to style.
It is lot easier to customize a ListBox than a TreeView as you do not have be concerned with the GridViewColumnHeader and GridViewColumnPresenters etc.
To get the expansion part (which is why you initially selected a TreeView), simply use a Grid with two rows defined and an Expander in the second row bound to the IsChecked property of a ToggleButton. See the example that I pulled from my Log Viewer.
<DataTemplate>
<Grid Margin="0,0,0,3" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" SharedSizeGroup="SSG_TimeIcon"/>
<ColumnDefinition Width="120" SharedSizeGroup="SSG_Time"/>
<ColumnDefinition Width="30" SharedSizeGroup="SSG_LevelIcon"/>
<ColumnDefinition Width="70" SharedSizeGroup="SSG_Level"/>
<ColumnDefinition Width="*" SharedSizeGroup="SSG_Message"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ProgramTime -->
<Rectangle Grid.Column="0" Grid.Row="0" Margin="0,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{StaticResource Icon_Timer}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0" VerticalAlignme="Top" HorizoalAlignme="Stretch" Text="{Binding Path=TimeStamp, Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding Path=ProgramTime}"/>
<!-- Level -->
<Rectangle Grid.Column="2" Grid.Row="0" Margin="10,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{Binding Path=Level, Converter={StaticResource MappingConverterNinjaLogLevelEnumToBrushResource}}"/>
<TextBlock Grid.Column="3" Grid.Row="0" Margin="5,0,0,0" Text="{Binding Path=LevelFriendlyName}" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
<!-- Message -->
<StackPanel Grid.Column="4" Grid.Row="0" Margin="10,0,0,0" Orieation="Horizoal" >
<TextBlock Margin="0,0,0,0" Text="{Binding Path=LogMessage}" TextWrapping="Wrap" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
<ToggleButton x:Name="ExpandExceptiooggleButton" VerticalAlignme="Top" Margin="5,0,0,0" IsChecked="False"
Coe="Show Details" Tag="Hide Details" Style="{StaticResource TextButtonStyle}"
Foreground="{StaticResource BlueBrush}" Background="{StaticResource RedBrush}"
Visibility="{Binding Path=HasException, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
<Expander IsExpanded="{Binding Path=IsChecked, ElemeName=ExpandExceptiooggleButton}" Style="{StaticResource CoeExpanderStyle}"
Margin="10,0,0,0" Grid.Column="4" Grid.Row="1">
<Border BorderBrush="{StaticResource DarkGreyBrush}" BorderThickness="1,0,0,0">
<TextBlock Text="{Binding Path=Exception}" Margin="5,0,0,0"/>
</Border>
</Expander>
</Grid>
</DataTemplate>
Can you see how much easier it is to define a header and expandable body. If you do have a need for nested data, add a Level property your view model (you are using MVVM aren't you?!) and then create a IValueConverter that returns a Margin (i.e. Thickness) to fake the indent.

Resources