WPF Binding to child of current item not updating - wpf

I am currently binding to an ObservableCollection using an ICollectionView, myCollectionView. The contents of that collection are being selected from a ComboBox. Each collection item, myCollectionItem, has a VisualBrush, myVisualBrush, as a child and the CurrentItem's brush is displayed in a preview panel.
The collection item also a child object, myItemChild, which contains a number of its own properties that are used to generate a slider. This slider alters properties on the preview panel.
This all works as expected.
When the CurrentItem of the Collectionview is changed the preview panel updates correctly but the slider continues to show the previous CurrentItem's myItemChild.
The change to myItemChild is not being raised, how should I handle this situation?
Its highly probable I have missed something obvious so any pointers appreciated.
Regards
Rob
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Combo Box for selection of item-->
<ComboBox Grid.Row="0" ItemsSource="{Binding myCollectionView, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<StackPanel>
<Rectangle Height="40" Width="40" Fill="{Binding myVisualBrush}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Panel to preview item-->
<ContentControl Grid.Row="1" Content="{Binding myCollectionView/}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<Rectangle Margin="20" Fill="{Binding myVisualBrush}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
<!-- Slider to edit item-->
<ContentControl Grid.Row="2" Content="{Binding myCollectionView/}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<ContentControl Content="{Binding myItemChild}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItemChild}" >
<StackPanel>
<Label Content="{Binding myValueLabel, Mode=OneWay}"/>
<Slider Value="{Binding myValue, Mode=TwoWay}" Maximum="{Binding myValueMax}" Minimum="{Binding myValueMin}"/>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>

I tried to reproduce your problem, but it works without problems.
Here is my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var items = new ObservableCollection<myCollectionItem>{
new myCollectionItem(Brushes.Red, new myCollectionItemChild("Red", 15, 0, 80)),
new myCollectionItem(Brushes.Green, new myCollectionItemChild("Green", 0.7, 0, 1)),
new myCollectionItem(Brushes.Purple, new myCollectionItemChild("Purple", 22,11,33))};
this.DataContext = new Model { myCollectionView = items };
}
}
public class Model
{
public ObservableCollection<myCollectionItem> myCollectionView { get; set; }
}
public class myCollectionItem
{
public myCollectionItem(Brush br, myCollectionItemChild child)
{
this.myVisualBrush = br;
this.myItemChild = child;
}
public Brush myVisualBrush { get; set; }
public myCollectionItemChild myItemChild { get; set; }
}
public class myCollectionItemChild
{
public myCollectionItemChild(string label, double val, double min, double max)
{
this.myValueLabel = label;
this.myValue = val;
this.myValueMin = min;
this.myValueMax = max;
}
public string myValueLabel { get; set; }
public double myValue { get; set; }
public double myValueMax { get; set; }
public double myValueMin { get; set; }
}
Also, you don't need to use control templates. It can be written more clearly:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Combo Box for selection of item-->
<ComboBox Grid.Row="0" ItemsSource="{Binding myCollectionView, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<StackPanel>
<Rectangle Height="40" Width="40" Fill="{Binding myVisualBrush}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Panel to preview item-->
<Rectangle Margin="20" Fill="{Binding myCollectionView/myVisualBrush}" Grid.Row="1" />
<!-- Slider to edit item-->
<StackPanel Grid.Row="2" DataContext="{Binding myCollectionView/myItemChild}">
<Label Content="{Binding myValueLabel, Mode=OneWay}"/>
<Slider Value="{Binding myValue, Mode=TwoWay}" Maximum="{Binding myValueMax}" Minimum="{Binding myValueMin}"/>
</StackPanel>
</Grid>

Related

How can I autoUpdate the content in ListBox

I am working on WPF Windows application. I am using ListBox to view/edit the content. In that I am doing calculation stuff. So I want that If I change the value of one Item it automatic change the calculation without adding extra buttons to regenerate it.
<ListBox ItemsSource="{Binding CustomSalesProducts, Mode=TwoWay}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CanHorizontallyScroll="True" CanVerticallyScroll="True" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="SalesGrid" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<controls:HeaderedContentControl Header="{Binding ProductName, Mode=TwoWay}" Margin="{DynamicResource Margin4}" Style="{DynamicResource HeaderedContentControlStyle}" HorizontalContentAlignment="Right">
</controls:HeaderedContentControl>
<TextBox Text="{Binding OrderQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Quantity" />
<TextBlock Text="{Binding UnitSalePrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Price"/>
<TextBox Text="{Binding Discount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Discount"/>
<TextBlock Text="{Binding TaxAmount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Tax Amount"/>
<TextBlock Text="{Binding LineTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="4" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Total"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Edit:
I tried trigger but it doesnt work in the listbox
like:
<TextBox Text="{Binding OrderQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Quantity" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding RefreshProduct}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Thanks
So this is what I suggest:
Make sure your Model class (e.g. CustomSalesProduct) is implementing INotifyPropertyChanged interface.
When your OrderQty property value changes (e.g. When user types another qty amount in TextBox) then call your method to calculate total in the Property Set. This will automatically refresh your LineTotal value in the View.
EDIT: If you decide to use the MVVM Design Pattern then:
Create a ViewModel class like:
public class CustomSalesViewModel
{
public ObservableCollection<CustomSalesProduct> CustomSalesProducts {get;set;}
public CustomSalesViewModel()
{
//Initialize your collection in constructor
CustomSalesProducts = new ObservableCollection<CustomSalesProduct>();
//Populate list
CustomSalesProducts.Add(new CustomSalesProduct(){....});
//.... Add rest of items
}
}
Then set the DataContext of your View to an instance of your CustomSalesViewModel.
You can do that in the Constructor of the code-behind of your View (XAML) like:
DataContext = new CustomSalesViewModel();
Here the sample class:
public class CustomSalesProduct : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _orderQty;
public int OrderQty
{
get { return _orderQty; }
set
{
_orderQty = value;
OnPropertyChanged("OrderQty");
CalcTotal();
}
}
private double _unitSalePrice;
public double UnitSalePrice
{
get { return _unitSalePrice; }
set
{
_unitSalePrice = value;
OnPropertyChanged("UnitSalePrice");
}
}
private double _discount;
public double Discount
{
get { return _discount; }
set
{
_discount = value;
OnPropertyChanged("Discount");
}
}
private double _taxAmount;
public double TaxAmount
{
get { return _taxAmount; }
set
{
_taxAmount = value;
OnPropertyChanged("TaxAmount");
}
}
private double _lineTotal;
public double LineTotal
{
get { return _lineTotal; }
set
{
_lineTotal = value;
OnPropertyChanged("LineTotal");
}
}
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
OnPropertyChanged("ProductName");
}
}
public void CalcTotal()
{
LineTotal = OrderQty*UnitSalePrice*(1 - Discount);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

Silverlight Listbox scrolling only works if i set the height

i have a listbox and i am trying to make the scrolling work without having to set the height, is this possible? Thanks. Below is the code. The scrolling does not work.
<ListBox Name="EmployeeListBox" Background="Transparent"
SelectionMode="Single"
ItemsSource="{Binding Employees, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold" Text="Name:" />
<TextBlock x:Name="TextBlock1" Grid.Column="1" Grid.Row="0"
Text="{Binding Name}" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and viewmodel:
public class EmployeeDataContext
{
public List<Employee> Employees { get; set; }
public EmployeeDataContext()
{
GetEmployeeList();
}
private void GetEmployeeList()
{
Employees = new List<Employee>();
for (int i = 0; i < 100; ++i)
{
Employees.Add(new Employee() { Name = "Gema Arterton" });
}
}
}
public class Employee
{
public string Name { get; set; }
}
(sorry for my bad english)
It depends too on what control is the listbox inside. If your listbox is inside a Grid, it should take all the space of that grid - if it is inside a stackpanel it will use the least amount os space possible so you will have to use fixed height/width. Try this:
<UserControl...>
<Grid>
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ListBox>
</Grid>
</UserControl/>
The listbox should use all screen size and show vertical scrollbars if there is enough items.
You can set the height in scrollviewer
<ScrollViewer VerticalScrollBarVisibility="Visible" Height="480">
<Grid Margin="0,0,0,50">
</Grid>
</ScrollViewer>

Add Custom control to stackpanel using Viewmodel WPF

I have a customcontrol, need to be added as many times when clicing on a button. This has to achived from MVVM WPF pattern. i have pasted my code here. It will be great if you guys can help on this.
Please help me
<Window x:Class="DOCS_APP_ELEMENT.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usercontrol="clr-namespace:DOCS_APP_ELEMENT"
xmlns:viewModel="clr-namespace:DOCS_APP_ELEMENT.ViewModels"
Title="MainWindow" Height="350" Width="400">
<Grid Margin="10" Name="myGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="0">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Type:" Margin="20,0,4,0"></Label>
<ComboBox Name="cmbQuestionType" Width="300" Style="{Binding ComboBoxStyle}" Margin="0,5,0,5" IsEnabled="False"> </ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Label Content="Question:" Margin="0,0,4,0"></Label>
<TextBox Name="txtQuestion" Width="300" Height="50" Margin="0,2,0,0" AcceptsReturn="True"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5" >
<Label Content="Answer:" Margin="7,0,4,0"></Label>
<TextBox Name="txtAnswer" Style="{StaticResource TextboxStyle}" Margin="0,2,0,0"></TextBox>
</StackPanel>
</StackPanel>
</Border>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="1" Margin="0,10,0,0" >
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="100">
<StackPanel Name="myCustom" Orientation="Vertical" >
**<!--<ADD CUSTOM CONTROl HERE>-->**
</StackPanel>
</ScrollViewer>
</Border>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="2" Margin="0,10,0,0">
<Border.DataContext>
<viewModel:ViewElements/>
</Border.DataContext>
<Button Name="btnAdd" Content="Add" DataContext="{Binding }" Command="{Binding Path=AddInstace}"></Button>
</Border>
</Grid>
I'd do it the following way:
have a ObservableCollection<CustomClass> in your ViewModel. The representation of your CustomClass is a DataTemplate with your above Markup.
Here's a full working example:
<Grid>
<Grid.DataContext>
<local:MyViewModel></local:MyViewModel>
</Grid.DataContext>
<StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="200">
<ItemsControl ItemsSource="{Binding CustomControls}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Green">
<StackPanel>
<TextBlock Text="I am a Custom Control"></TextBlock>
<TextBlock Text="{Binding DisplayValue}"></TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<Button Width="200" Height="50" Command="{Binding AddControlCommand}">Add Control</Button>
<Button Width="200" Height="50" Command="{Binding RemoveControlCommand}">Remove Control</Button>
</StackPanel>
</Grid>
ViewModel:
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class RelayCommand : ICommand
{
... look that up yourself if you don't have a derived command class yet in your project...
}
public class MyViewModel : ViewModel
{
public ICommand AddControlCommand
{
get
{
return new RelayCommand(x=>this.AddControl());
}
}
public ICommand RemoveControlCommand
{
get
{
return new RelayCommand(x => this.RemoveControl());
}
}
private void AddControl()
{
CustomControls.Add(new CustomControl() {DisplayValue = "newControl"});
}
private void RemoveControl()
{
if (CustomControls.Count > 0)
{
CustomControls.Remove(CustomControls.Last());
}
}
private ObservableCollection<CustomControl> _customControls;
public ObservableCollection<CustomControl> CustomControls
{
get
{
if (_customControls == null)
{
_customControls = new ObservableCollection<CustomControl>()
{
new CustomControl() {DisplayValue = "Control1"},
new CustomControl() {DisplayValue = "Control2"},
new CustomControl() {DisplayValue = "Control3"}
};
}
return _customControls;
}
}
}
public class CustomControl : ViewModel
{
public string DisplayValue { get; set; }
}
To use the MVVM pattern you'll need a ViewModel that has a list of data objects which are bound to your custom controls. This controls can be generated by an ItemsControl. As I don't know your data I can just give you a general example.
MainWindow.xaml (View)
<ItemsControl ItemsSource="{Binding DataList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- instead of the TextBlock you would use your control -->
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
MainWindow.xaml.cs (View code-behind)
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
}
MainWindowViewModel.cs (ViewModel)
public class MainWindowViewModel
{
public ObservableCollection<string> DataList { get; set; }
public MainWindowViewModel()
{
DataList = new ObservableCollection<string>
{
"Data 1",
"Data 2",
"Data 3"
};
}
}
The Binding for the Text property has no path as the DataContext here is the string object of DataList. If you use complex objects, you have to use the Path to the property of the object (e.g. Text={Binding Path=myProperty})

Control size of ListBoxItem (with DataTemplate) using RadioButtons

I´m displaying images in a ListBox and i want to control the size (small, medium, big) of the images with RadionButtons. The ItemsSource of the ListBox is bound to a property of a viewmodel. My current solution is to use an Image for each size and bind the Visibility of each Image to the IsChecked property to the corresponding RadioButton. But i wonder if there is a better solution to get it work, especially using bindings and not duplicating items for various sizes. Using animations for the size changes would also be great.
Here´s what i have so far.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Image x:Name="smallImage" Width="80" Height="45" Source="{Binding}" Visibility="{Binding IsChecked, ElementName=SmallSizeChecked, Converter={StaticResource boolToVisibility}}" />
<Image x:Name="mediumImage" Width="160" Height="90" Source="{Binding}" Visibility="{Binding IsChecked, ElementName=MediumSizeChecked, Converter={StaticResource boolToVisibility}}"/>
<Image x:Name="bigImage" Width="320" Height="180" Source="{Binding}" Visibility="{Binding IsChecked, ElementName=BigSizeChecked, Converter={StaticResource boolToVisibility}}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<RadioButton Content="small" Margin="0,0,10,0" GroupName="ItemSize" x:Name="SmallSizeChecked" />
<RadioButton Content="medium" Margin="0,0,10,0" GroupName="ItemSize" x:Name="MediumSizeChecked" IsChecked="True" />
<RadioButton Content="big" GroupName="ItemSize" x:Name="BigSizeChecked"/>
</StackPanel>
</Grid>
This can be easily achieved by using IValueConverters with parameters.
here is more elegant way then converters. i have not compiled it but will work after some fixes.
C#:
public class VM
{
public List<string> Images { get; set; }
public bool SmallSizeChecked { get; set; }
public bool MediumSizeChecked { get; set; }
public bool BigSizeChecked { get; set; }
public int size
{
get
{
if(SmallSizeChecked) return 100;
if(MediumSizeChecked) return 200;
if(BigSizeChecked) return 300;
return 100;
}
}
}
XAML:
<local:VM x:Key="mv" />
<RadioButton x:name="SmallSizeChecked" ischecked="{Binding mv.SmallSizeChecked, Mode=Twoway}"/>
<RadioButton x:name="MediumSizeChecked" ischecked="{Binding mv.MediumSizeChecked, Mode=Twoway/>
<RadioButton x:name="BigSizeChecked" ischecked="{Binding mv.BigSizeChecked, Mode=Twoway}"/>
<ListBox ItemsSource="{Binding mv.Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Image x:Name="img" Source="{Binding}" Width="80" Height="{Binding Path=size,Source={staticResource mv}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm sure you will get the idea and will extend it for Width property too.
Dipak.

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">

Resources