Is there a way to bind to the ItemIndex from within the ItemTemplate of an ItemsControl?
For example:
<ItemsControl ItemsSource="{Binding Path=ItemList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ThisItemsIndex}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you're not using any type of alternating row styles you might be able to hijack the AlternationIndex for this. Set AlternationCount on your ItemsControl to something greater than the max possible count of your items and then use
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(ItemsControl.AlternationIndex)}"
Edit: As bradgonesurfing pointed out in comments, this is not recommended if you're using virtualization, as it will only index the items that are generated and not the entire list.
Here is a method I used to add a bindable index on a collection item. I basically wrap my item in a container that has an index, and have a custom ObservableCollection that accepts the wrapper.
Note that MoveItem is not overridden, but would have to be for a complete implementation.
public class IndexedItemContainerCollection<T> : ObservableCollection<IndexedItemContainer<T>>
{
public IndexedItemContainerCollection()
{
}
public IndexedItemContainerCollection(IEnumerable<IndexedItemContainer<T>> collection)
: base(collection)
{
var index = 0;
foreach (var item in this)
{
item.Index = index;
}
}
protected override void InsertItem(int index, IndexedItemContainer<T> item)
{
item.Index = index;
base.InsertItem(index, item);
foreach (var indexedItem in this.Where(x=>x.Index > index))
{
indexedItem.Index++;
}
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
foreach (var indexedItem in this.Where(x => x.Index > index))
{
indexedItem.Index--;
}
}
}
public class IndexedItemContainer<T>
{
public int Index { get; set; }
public T Item { get; set; }
}
I then extend my wrapper class to get a bindable property that I have control over how the index is displayed:
public class NamedIndexedItemContainer<T> : IndexedItemContainer<T>
{
public string Name
{
get { return string.Format("Item #{0}", Index + 1); }
}
}
Sample Usage
XAML:
<ComboBox ItemsSource="{Binding ItemList}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code:
private IndexedItemContainerCollection<MyItem> _itemList;
public IndexedItemContainerCollection<MyItem> ItemList
{
get { return _itemList; }
set { _itemList= value; OnPropertyChanged(); }
}
ItemList = new IndexedItemContainerCollection<MyItem>();
var newItem = new NamedIndexedItemContainer<MyItem>() { Item = new MyItem() { ... } };
ItemList.Add(newItem);
Of course, any binding with the actual MyItem instance would have to go through the IndexedItemContainer's Item property.
For the record, there is another way to accomplish this: using custom Converter. A little bit more complicated, but you do not have to worry about AlternationCount/Index.
public sealed class ArrayWrapperConverter : IValueConverter
{
private static readonly Type ArrayWrappingHelperType = typeof(ArrayWrappingHelper<>);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
Type valueType = value.GetType();
if (!valueType.IsArray)
{
return DependencyProperty.UnsetValue;
}
Type elementType = valueType.GetElementType();
Type specificType = ArrayWrappingHelperType.MakeGenericType(elementType);
IEnumerable wrappingHelper = (IEnumerable) Activator.CreateInstance(specificType, value);
return wrappingHelper;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class ArrayWrappingHelper<TValue> : IEnumerable
{
private readonly TValue[] _array;
public ArrayWrappingHelper(object array)
{
_array = (TValue[]) array;
}
public IEnumerator GetEnumerator()
{
return _array.Select((item, index) => new ArrayItemWrapper<TValue>(_array, index)).GetEnumerator();
}
}
public class ArrayItemWrapper<TValue>
{
private readonly TValue[] _array;
private readonly int _index;
public int Index
{
get { return _index; }
}
public TValue Value
{
get { return _array[_index]; }
set { _array[_index] = value; }
}
public ArrayItemWrapper(TValue[] array, int index)
{
_array = array;
_index = index;
}
}
Sample usage:
<Window x:Class="WpfArrayBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:WpfArrayBinding.Converters"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<c:ArrayWrapperConverter x:Key="ArrayWrapperConverter" />
<x:Array Type="{x:Type s:String}" x:Key="MyArray">
<s:String>Foo</s:String>
<s:String>Bar</s:String>
<s:String>Baz</s:String>
</x:Array>
</ResourceDictionary>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource MyArray}, Converter={StaticResource ArrayWrapperConverter}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Index}" />
<TextBox Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Related
I have made a simple example of my issue. There is a window having its DataContext bount to MainWindowViewModel class. The window contains a Grid with a Button and a ProgressBar. The Button's command is bount to a ViewModel's RelayCommand that at first executes an increment of an integer property "Percent" from 0 to 100. The progressBar shows the progress just fine! The window contains a TaskBarItemInfo property, and the latter has two properties: a ProgressValue bount to a double property "Count", and a ProgressState bount to a TaskbarItemProgressState property "State", all properties in the in the MainWindowViewModel. The TaskbarItemInfo shows also just fine the increment from 0 to 1.
But in the second part of the initial method the progress becomes indeterminate for 5 seconds. While that shows just fine in the progressBar, it does not in the TaskBar. I have used both a Converter from boolean to TaskbarItemProgressState to the Path of the ProgressState property (IsIndeterminate), and a binding to a separate property State, as shown to the code below.
MainWindow.xaml
<Window x:Class="ProgressState.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:ProgressState"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="400">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<local:BoolToProgressStateConverter x:Key="boolToProgressStateConverter"/>
</Window.Resources>
<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressValue="{Binding Count, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
ProgressState="{Binding State, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
<!--ProgressState="{Binding IsIndeterminate, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, Converter={StaticResource boolToProgressStateConverter}}"-->
</Window.TaskbarItemInfo>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="Start progress" Grid.Row="0" Height="30" Width="100"
Command="{Binding Start}" IsEnabled="{Binding IsEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<ProgressBar Grid.Row="1" Height="20" Margin="10 0"
Value="{Binding Percent, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
IsIndeterminate="{Binding IsIndeterminate, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
</Grid>
</Window>
MainWindowViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Shell;
namespace ProgressState
{
public class MainWindowViewModel : ViewModelBase
{
private double count;
private bool isIndeterminate;
private int percent;
private bool isEnabled = true;
private TaskbarItemProgressState state;
public RelayCommand Start { get; set; }
public MainWindowViewModel()
{
Start = new RelayCommand(OnStart, CanStart);
}
public TaskbarItemProgressState State
{
get
{
if (IsIndeterminate)
{
state = TaskbarItemProgressState.Indeterminate;
}
else
{
state = TaskbarItemProgressState.Normal;
}
return state;
}
}
public bool IsEnabled
{
get
{
return isEnabled;
}
set
{
if (value != isEnabled)
{
isEnabled = value;
RaisePropertyChanged(nameof(IsEnabled));
}
}
}
public bool IsIndeterminate
{
get
{
return isIndeterminate;
}
set
{
if (value != isIndeterminate)
{
isIndeterminate = value;
RaisePropertyChanged(nameof(IsIndeterminate));
RaisePropertyChanged(nameof(State));
}
}
}
public int Percent
{
get
{
return percent;
}
set
{
if (value != percent)
{
percent = value;
RaisePropertyChanged(nameof(Percent));
RaisePropertyChanged(nameof(Count));
}
}
}
public double Count
{
get
{
count = Percent / 100d;
return count;
}
}
private bool CanStart()
{
return true;
}
private async void OnStart()
{
IsEnabled = false;
Percent = 0;
await Task.Run(() =>
{
while (Percent != 100)
{
Thread.Sleep(100);
Percent += 2;
}
IsIndeterminate = true;
Thread.Sleep(5000);
IsIndeterminate = false;
});
IsEnabled = true;
}
}
}
BoolToProgressStateConverter.cs
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Shell;
namespace ProgressState
{
class BoolToProgressStateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolean)
{
if (boolean == true)
{
return TaskbarItemProgressState.Indeterminate;
}
else if (boolean == false)
{
return TaskbarItemProgressState.Normal;
}
else
{
throw new NotImplementedException();
}
}
return TaskbarItemProgressState.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Any ideas?
I have a ListBox containing Name. Now I need to select multiple items from ListBox.
ViewModel.CS
private Person selectedListOfPeople_;
public Person SelectedListOfPeople
{
get
{ return selectedListOfPeople_;}
set
{ this.SetProperty(ref selectedListOfPeople_, value, nameof(SelectedListOfPeople));}
}
private ObservableCollection<Person> listOfPeople_;
public ObservableCollection<Person> ListOfPeople
{
get { return listOfPeople_; }
set
{
this.SetProperty(ref listOfPeople_, value, nameof(ListOfPeople));
}
}
public ShellViewModel()
{
ListOfPeople = new ObservableCollection<Person>
{
new Person("ABC"),new Person("DEF"),new Person("GHI"),new Person("JKL")
};
}
public class Person : Screen
{
private string personName_;
public string PersonName
{
get { return personName_; }
set { this.SetProperty(ref personName_, value, nameof(PersonName)); }
}
public Person(string personName)
{
PersonName = personName;
}
private bool isSelected_;
public bool IsSelected
{
get { return isSelected_; }
set { this.SetProperty(ref isSelected_, value, nameof(IsSelected)); }
}
}
View.XAML
<Grid Width="500" Height="500" Background="LightBlue">
<ListBox x:Name="ListOfPeople" SelectionMode="Multiple" Height="300" Width="300" Margin="120,100,80,100">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding PersonName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
in that SelectedListOfPeople is not called when the second item is selected in ListBox set to Multiple selections. How can I make sure that this event is raised every time the user makes a selection in ListBox?
One way of doing this would be to break from the convention available in that framework and bind the property manually.
But first you would need to update the property for multiselect in the view model
private ObservableCollection<Person> selectedListOfPeople;
public ObservableCollection<Person> SelectedListOfPeople {
get { return selectedListOfPeople; }
set { this.SetProperty(ref selectedListOfPeople, value, nameof(SelectedListOfPeople)); }
}
private ObservableCollection<Person> listOfPeople;
public ObservableCollection<Person> ListOfPeople {
get { return listOfPeople; }
set { this.SetProperty(ref listOfPeople, value, nameof(ListOfPeople)); }
}
public ShellViewModel() {
ListOfPeople = new ObservableCollection<Person> {
new Person("ABC"),
new Person("DEF"),
new Person("GHI"),
new Person("JKL")
};
SelectedListOfPeople = new ObservableCollection<Person>();
}
And then bind to the desired property in the view's XAML
<ListBox x:Name="ListOfPeople" SelectionMode="Multiple"
Height="300" Width="300" Margin="120,100,80,100"
SelectedItems = "{Bining SelectedListOfPeople}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding PersonName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
The convention will bind the items source or the ListBox and the manual binding of the SelectedItems will provided the desired behavior.
I want to use linq grouping as itemssource for checkbox listview
It my data model:
public class GroupItem
{
public string name GroupName { get; set; }
public string boolean GroupItemFlag { get; set; }
}
It view model (data context):
...
IEnumerable<GroupItem> _groupItems;
public IEnumerable<IGrouping<string,GroupItem>> Groups
{
get { return _groupItems.GroupBy(__item=>__item.GroupName); }
}
...
it view:
...
<ListView ItemsSource={Binding Groups}>
<ListView.ItemTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Converter={StaticResource groupingToBooleanConverter}, Mode=TwoWay}"/>
<TextBlock Text="{Binding Key}"/>
</StackPanel>
</ListView.ItemTemplate>
</ListView>
...
groupingToBooleanConverter code:
public class GroupingToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var group = value as IGrouping<string,GroupItem>;
return group.Any(__item => __item.GroupItemFlag);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// problem here
// how to set for all group items GroupItemFlag=(bool)value;
}
}
I don't want create one more class "Group" for this, so i use IGrouping and converter.
May be I chose the wrong way?
You can achieve it even without converter:
<CheckBox IsChecked="{Binding Path=GroupItemFlag, Mode=OneWay}"
Command="{Binding DataContext.ToogleGroupCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"/>
void ToogleGroupCommand_Execute(IGrouping<string, GroupItem> group){
bool newValue = !group.First().GroupItemFlag;
foreach(var item in group) item.GroupItemFlag = newValue;
}
if you are not using MVVM, then just handle Checked and Unchecked events.
However, if you are using MVVM, I highly recommend you to create Group class as ViVi suggests:
public MainViewModel()
{
Groups = _groupItems.GroupBy(i => i.GroupName).Select(i => new GroupViewModel(i.Key, i);
}
public Groups[] Groups {get;}
public class GroupViewModel
{
public GroupViewModel(string name, IEnumerable<GroupItem> items)
{
Items = items;
}
public string Name { get; }
public IEnumerable<GroupItem> Items { get; }
public bool? IsChecked
{
get
{
if (Items.All(i => i.GroupItemFlag)) return true;
if (Items.Any(i => i.GroupItemFlag)) return null;
return false;
}
set
{
foreach (var item in Items)
{
item.GroupItemFlag = value.GetValueOrDefault();
}
}
}
}
I'm working with a treeview and I want to have a multi-level treeview that groups every level of the tree (All Children).
It seems to be working for the first level of items but when the items have children underneath it the grouping style doesn't seem to be applied.
My Xaml Group Style, HierarchicalDataTemplate, TreeView
<DataTemplate x:Key="basicGroupStyle">
<!--Hide the whole grouping header if the group name is empty string-->
<DockPanel LastChildFill="True" >
<Border Background="RoyalBlue"
Margin="0" MinHeight="3" Width="5"
VerticalAlignment="Center" DockPanel.Dock="Left"/>
<TextBlock Text="{Binding Name}"
Foreground="Black"
FontSize="8pt" FontWeight="Bold"
VerticalAlignment="Center"
Margin="5 0 5 0"
DockPanel.Dock="Left"/>
<Border Background="RoyalBlue"
Margin="0" MinHeight="3"
VerticalAlignment="Center"/>
</DockPanel>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type explorer:TreeItem}"
ItemsSource="{Binding TreeViewItemsView}">
<!--Hide the whole grouping header if the group name is empty string-->
<DockPanel LastChildFill="True" >
<TextBlock Text="{Binding Name}"
Foreground="Black"
FontSize="8pt" FontWeight="Bold"
VerticalAlignment="Center"
Margin="5 0 5 0"
DockPanel.Dock="Left"/>
</DockPanel>
</HierarchicalDataTemplate>
<TreeView ItemsSource="{Binding TreeViewItemsView}">
<TreeView.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource basicGroupStyle}" />
</TreeView.GroupStyle>
</TreeView>
Converters/Group Description
public class AssetTypeExplorerValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var v = value as int?;
if (v == null)
return value;
return Convert(v);
}
public static string Convert(int? assetData)
{
if (assetData.HasValue)
{
if (assetData == 1)
return "First Group";
else if (assetData == 2)
return "Second Group";
else if (assetData == 3)
return "Third Group";
else
return "Damn";
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
TreeItem Class
public int level { get; set; }
public string Name { get; set; }
private ICollectionView _treeViewItemsView;
private ObservableCollection<TreeItem> _treeViewItems;
public ObservableCollection<TreeItem> TreeViewItems
{
get { return _treeViewItems ?? (TreeViewItems = new ObservableCollection<TreeItem>()); }
set { _treeViewItems = value; }
}
public ICollectionView TreeViewItemsView
{
get
{
if (_treeViewItemsView == null)
{
_treeViewItemsView = CollectionViewSource.GetDefaultView(TreeViewItems) as ListCollectionView;
}
return _treeViewItemsView;
}
}
private void Initalize()
{
(this.TreeViewItemsView as ListCollectionView).GroupDescriptions.Clear();
(this.TreeViewItemsView as ListCollectionView).GroupDescriptions.Add(new PropertyGroupDescription("level", new AssetTypeExplorerValueConverter()));
}
Collections for the TreeItems (Initalize called on constructor)
private ICollectionView _treeViewItemsView;
private ObservableCollection<TreeItem> _treeViewItems;
public ICollectionView TreeViewItemsView
{
get
{
if (_treeViewItemsView == null)
{
_treeViewItemsView = CollectionViewSource.GetDefaultView(TreeViewItems) as ListCollectionView;
}
return _treeViewItemsView;
}
}
public ObservableCollection<TreeItem> TreeViewItems
{
get { return _treeViewItems ?? (TreeViewItems = new ObservableCollection<TreeItem>()); }
set { _treeViewItems = value; }
}
private void SetupItems()
{
var first = new TreeItem() { Name = "First Level First Item", level = 1 };
var secLevel = new TreeItem() { Name = "Second Level First Item", level = 2 };
first.TreeViewItems.Add(secLevel);
TreeViewItems.Add(first);
TreeViewItems.Add(new TreeItem() { Name = "Second Item", level = 1 });
TreeViewItems.Add(new TreeItem() { Name = "Third Item", level = 1 });
}
private void Initalize()
{
(this.TreeViewItemsView as ListCollectionView).GroupDescriptions.Clear();
(this.TreeViewItemsView as ListCollectionView).GroupDescriptions.Add(new PropertyGroupDescription("level", new AssetTypeExplorerValueConverter()));
SetupItems();
}
Any idea why this might be happening?
Any help will be greatly appreciated! Thanks!
One suggestion;
Assign the HierarchicalDatatemplate to the TreeView.
<HierarchicalDataTemplate x:Key="TestTemplate" .... HierarchicalDataTemplate>
<TreeView ItemTemplate="{StaticResource TestTemplate}" ..... TreeView>
I am trying to successfully TwoWay bind an ObservableCollection to TextBoxes in a DataTemplate. I can get the data to display properly, but I am unable to change the list data through the UI. I have a Model class named 'model' which contains an ObservableCollection named 'List'. The class implements the INotifyPropertyChanged interface. Here is the xaml for the shell. The DataContext for Window1's grid is set to "theGrid.DataContext=model"
<Window x:Class="BindThat.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindThat"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
This is the code for the Model class:
class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<string> _list = new ObservableCollection<string>();
public ObservableCollection<string> List
{
get { return _list; }
set
{
_list = value;
NotifyPropertyChanged("List");
}
}
public Model()
{
List.Add("why");
List.Add("not");
List.Add("these?");
}
}
Could anyone advise if I am going about this the correct way?
You need a property to bind two way, so string is not good for this.
Wrap it in a string object, like this:
public class Model
{
public ObservableCollection<StringObject> List { get; private set; }
public Model()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"},
};
}
}
public class StringObject
{
public string Value { get; set; }
}
and bind to Value property instead of "."
Also, you don't need to notify of a change in observable collection, so until your model has some other propertis of its own, it does not need to have INotifyPropertyChange. If you want your ItemsControl react to changes in the individual StringObjects, then you should add INotifyPropertyChanged to a StringObject.
And yet again, two way binding is default, so you need only
<TextBox Text="{Binding Path=Value}" />
in your binding.
I believe you need to derive your collection items from DependencyObject for TwoWay binding to work. Something like:
public class DependencyString: DependencyObject {
public string Value {
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(DependencyString), new UIPropertyMetadata(""));
public override string ToString() {
return Value;
}
public DependencyString(string s) {
this.Value = s;
}
}
public class Model {
private ObservableCollection<DependencyString> _list = new ObservableCollection<DependencyString>();
public ObservableCollection<DependencyString> List {
get { return _list; }
}
public Model() {
List.Add(new DependencyString("why"));
List.Add(new DependencyString("not"));
List.Add(new DependencyString("these?"));
}
}
...
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
xaml view:
<ItemsControl ItemsSource="{Binding List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
in code behind in the constructor:
DataContext = new ViewModel();
in ViewModel Class:
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<StringObject> _List = new ObservableCollection<StringObject>();
public ObservableCollection<StringObject> List
{
get { return _List; }
set
{
_List = value;
NotifyPropertyChanged("List");
}
}
public ViewModel()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"}
};
}
}
public class StringObject
{
public string Value { get; set; }
}
Be careful with a collection with type string it doesn't work, you have to use an object => StringObject