Add Custom control to stackpanel using Viewmodel WPF - 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})

Related

How to get textbox text which has placed in datagrid column header in wpf

I want to have a datagrid with textbox in each colunmns header to filter datagrid.
I tried read this and this . here's my datagrid:
<DataGrid x:Name="CustomersDataGrid" BorderThickness="1"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserResizeColumns="True"
CanUserResizeRows="False"
CanUserDeleteRows="False"
SelectionMode="Single"
SelectionUnit="FullRow"
DataContext="{Binding ElementName=UI}"
ItemsSource="{Binding}"
IsReadOnly="True" MouseDoubleClick="CustomersDataGrid_MouseDoubleClick">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Customer.Name}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center" HorizontalContentAlignment="Center" HorizontalAlignment="Center" Content="Name" Style="{StaticResource GridTitleLabel}"/>
<TextBox Name="NameTextBox" MinWidth="100" TextChanged="SearchTextBox_TextChanged" Text="{Binding FilterString, ElementName=UI, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
and this is filterstring in codebehind:
private string _filterString;
public string FilterString
{
get { return _filterString; }
set
{
_filterString = value;
NotifyPropertyChanged("FilterString");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
but a cant get textBox text , it doesn't bind correctly and filterstring value is always null
As i understand, you does not implement INotifyPropertyChanged on your Window.
Let me show you little example:
XAML:
<Window x:Class="FilterSample.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"
Name="window"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn>
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<Grid Width="120">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0"
Content="Some header"
HorizontalAlignment="Center"/>
<TextBox Grid.Row="1"
Text="{Binding Filter, ElementName=window, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Codebehind:
public partial class MainWindow : INotifyPropertyChanged
{
private string _filter;
public MainWindow()
{
InitializeComponent();
}
public string Filter
{
get { return _filter; }
set
{
if (_filter == value) return;
_filter = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
When you write "Binding" the property "Filter" must be in the DataContext.
However, the textbox does not have direct access to DataContext why is within the GridView.
So, do something like this:
<TextBox Text="{Binding DataContext.Filter, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>

UserControl default value for dependency property of type DataTemplate

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

WPF Binding to child of current item not updating

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>

Stretching the items in a WPF ListView within a ViewBox

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

Removing left and right border sides from listbox datatemplate

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

Resources