how to update listitem(observableObject) in Listview(observerableCollection) in WPF - wpf

I have a collection of instruments, and I'd like to configure each of them after I add them to a list
Currently this instrument is defined as a class inherited from ObservableObject, and stored in an ObservableCollection, shown as a Listview with a datatemplate of instrument details.
Now I can add/delete an instrument. but when I try to update instrument details(e.g. names, type). it is not update to instrument class property.
I'm using CommunityToolkit.Mvvm, I tested that it will work if I just put an instrument details in textbox as the direct element in the mainwindow, but not work as listviewitem.
So does it mean that I can't put observableObject under another observableObject/ObserverbaleCollection?
<ListView x:Name="ListView_Instr"
ItemsSource="{Binding InstrumentConfigs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectInstrumentConfig, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Padding="5,5,5,5" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="{Binding Width, ElementName=ListView_Instr}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="CheckBox_InstrChecked" Grid.Column="0"/>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<Label Content="Type"/>
<ComboBox x:Name="ComboBox_InstrType" ItemsSource="{Binding InstrTypes, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="2">
<Label Content="Name"/>
<TextBox x:Name="TextBox_InstrName" Text="{Binding InstrName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="3">
<Label Content="Addr"/>
<TextBox x:Name="TextBox_InstrAddr" Text="{Binding InstrAddr, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="4">
<Label Content="Interface"/>
<ComboBox x:Name="ComboBox_InstrInterface" ItemsSource="{Binding InstrInterfaceTypes, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Any detail I missed when using Observable?
Thanks

The following way should work.
Instrument.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace wpf_binding
{
[INotifyPropertyChanged]
public partial class Instrument
{
[ObservableProperty]
private string name;
[ObservableProperty]
private string description;
partial void OnNameChanged(string value)
{
System.Console.WriteLine($"Name changed: {Name}");
}
partial void OnDescriptionChanged(string value)
{
System.Console.WriteLine($"Description changed: {Description}");
}
}
}
ViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
namespace wpf_binding
{
[INotifyPropertyChanged]
public partial class ViewModel
{
[ObservableProperty]
private ObservableCollection<Instrument> instruments;
[ObservableProperty]
private Instrument selectedItem;
public ViewModel()
{
Instruments = new ObservableCollection<Instrument>()
{
new Instrument() { Name = "Item 1", Description = "Desc 1" },
new Instrument() { Name = "Item 2", Description = "Desc 2" }
};
}
}
}
MainWindow.xaml
<Window x:Class="wpf_binding.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:wpf_binding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Orientation="Vertical">
<ListView ItemsSource="{Binding Instruments}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Name, Mode=TwoWay}"/>
<TextBox Text="{Binding Description, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:"/>
<TextBlock Text="{Binding SelectedItem.Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Description:"/>
<TextBlock Text="{Binding SelectedItem.Description}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace wpf_binding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
}

Related

items of a listview are not being displayed in wpf

I am trying to add items to a listview where each item has two textblocks, but for some reason items are not being displayed .Here is my XAML code .
<Window x:Class="testapp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="524" Width="856">
<Grid Background="Black">
<!--<Image Height="44" HorizontalAlignment="Left" Margin="284,0,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="229" Source="/testapp;component/Images/Picture1.png" />-->
<Grid Height="431" HorizontalAlignment="Left" Margin="0,55,0,0" Name="grid1" VerticalAlignment="Top" Width="266" Background="Black" Opacity="0">
<ListView Height="430" HorizontalAlignment="Left" Margin="3,1,0,0" x:Name="listView1" VerticalAlignment="Top" Width="260" >
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" Foreground="Green" />
<TextBlock Text="{Binding Source}" Foreground="Green" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<!-- <ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>-->
</ListView>
</Grid>
</Grid>
</Window>
And here is my C# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace testapp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<MyItems> items = new List<MyItems>();
items.Add(new MyItems() { Name = "House Party", Source = " DLNA" });
items.Add(new MyItems() { Name = "Outdoor Party", Source = " DLNA" });
listView1.ItemsSource = items;
}
}
public class MyItems
{
public String Name { get; set; }
public String Source { get; set; }
}
}
Even though i am binding textblocks to name and source it is still not displaying.Can anyone help me? Thanks in advance.
Its not Displaying because of Opacity Parameter. You have defined
Opacity="0"
in your xaml
please make it to in range between (0-1)
Old Code
<Grid Height="431" HorizontalAlignment="Left" Margin="0,55,0,0" Name="grid1" VerticalAlignment="Top" Width="266" Background="Black" Opacity="0">
Change to (New Code)
<Grid Height="431" HorizontalAlignment="Left" Margin="0,55,0,0" Name="grid1" VerticalAlignment="Top" Width="266" Background="Black">

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})

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;
}
}
}

WPF TabStop / TabIndex in ItemsControl

I'm dynamically adding WPF ComboBox-es and I want to be able to select the added ComboBoxes with the 'TAB' key. (Dynamically generate TabIndex or something)
As it actually is:
What I want:
How would you do that?
This is the code:
<ItemsControl ItemsSource="{Binding}" Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding Source={StaticResource SomeItems}}" IsSynchronizedWithCurrentItem="False" SelectedItem="{Binding Path=SomeValue, Mode=TwoWay}" DisplayMemberPath="Name" TabIndex="20"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource SomeOtherItems}}" IsSynchronizedWithCurrentItem="False" SelectedItem="{Binding Path=SomeOtherValue, Mode=TwoWay}" DisplayMemberPath="Value" TabIndex="21"/>
<TextBox HorizontalContentAlignment="Stretch" Grid.Column="2" TabIndex="22" LostKeyboardFocus="TextBox_FormatAfterLostFocus">
<TextBox.Text>
<Binding Path="Wert" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks in advance !
Here is a small example. I did this in Silverlight, so I guess you will have to modify it to work in WPF. It is not complete, but it is a starting point. You might want to use the Tag property on your controls to identify which of them need tab index fixing.
XAML:
<UserControl x:Class="SilverlightApplication6.MainPage"
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"
mc:Ignorable="d"
d:DesignWidth="640"
d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<StackPanel>
<ItemsControl ItemsSource="{Binding}"
Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ComboBox Grid.Column="0"
ItemsSource="{Binding Source={StaticResource SomeItems}}"
IsSynchronizedWithCurrentItem="False"
SelectedItem="{Binding Path=SomeValue, Mode=TwoWay}"
DisplayMemberPath="Name"
TabIndex="20" />
<ComboBox Grid.Column="1"
ItemsSource="{Binding Source={StaticResource SomeOtherItems}}"
IsSynchronizedWithCurrentItem="False"
SelectedItem="{Binding Path=SomeOtherValue, Mode=TwoWay}"
DisplayMemberPath="Value"
TabIndex="21" />
<TextBox HorizontalContentAlignment="Stretch"
Grid.Column="2"
TabIndex="22">
<TextBox.Text>
<Binding Path="Wert"
Mode="TwoWay" />
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Fix Tab indexes" Click="Button_Click"
TabIndex="999"
></Button>
</StackPanel>
</Grid>
</UserControl>
And C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
namespace SilverlightApplication6
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.DataContext = new List<int>() { 1, 2, 3, 4, 5 };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var controls = new List<Control>();
GetChildrenOfSpecificType<Control>(this, ref controls, false);
var index = 1;
controls.ForEach(control =>
{
if (control is ComboBox || control is TextBox)
{
control.TabIndex = index;
index++;
}
});
}
private static void GetChildrenOfSpecificType<T>(DependencyObject parent, ref List<T> resultList, bool getSingle) where T : class
{
if (parent == null)
return;
else
{
int cnt = VisualTreeHelper.GetChildrenCount(parent);
if (cnt > 0)
{
for (int i = 0; i < cnt; i++)
{
var d = VisualTreeHelper.GetChild(parent, i);
if (d is T)
{
resultList.Add(d as T);
if (getSingle)
return;
}
GetChildrenOfSpecificType<T>(d, ref resultList, getSingle);
}
}
}
}
}
}

Resources