I have an object consisting of a string and a string of arrays. I'm binding the string to a comboBox but what I want to also do is bind the array of that object to a listview and have it change dynamically depending on combox box value. The values in the array aren't populating, only the dataType of the array. I'm not married to using a listview but I thought it would be easiest.
Model -
namespace DataBinding_WPF.Model
{
public class ExampleModel { }
public class Example : INotifyPropertyChanged
{
private string _name;
private string[] _ids;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
RaisePropertyChanged("Name");
}
}
}
public string[] IDs
{
get => _ids;
set
{
if (_ids != value)
{
_ids = value;
RaisePropertyChanged("IDs");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(property));
}
}
}
ViewModel -
namespace DataBinding_WPF.ViewModel
{
public class ExampleViewModel
{
public ObservableCollection<Example> Examples
{
get;
set;
}
public void LoadExample()
{
ObservableCollection<Example> examples = new ObservableCollection<Example>();
examples.Add(new Example { Name = "Mark", IDs = new string[] { "123", "456" }});
examples.Add(new Example { Name = "Sally", IDs = new string[] { "789","101112" }});
Examples = examples;
}
}
}
XAML -
<Grid>
<StackPanel HorizontalAlignment = "Left" >
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=Examples}"
SelectedItem="{Binding Path=Name, Mode=TwoWay}"
DisplayMemberPath="Name"/>
<ListView x:Name="myListView"
ItemsSource="{Binding Path=Examples}"
SelectedValue="{Binding Path=IDs}"
Height="200" Margin="0,50,0,0"
Width="Auto"
VerticalAlignment="Top"
Background="AliceBlue">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox
Name="myCheckBox"
IsChecked="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListViewItem}}"
Margin="5, 0"/>
<TextBlock Text= "{Binding Path=IDs}" FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
If you want to show in ListBox the Ids of selected item in comboBox.
You need to add SelectedItem property to your VM which also must implement INotifyPropertyChanged interface.
namespace DataBinding_WPF.ViewModel
{
public class ExampleViewModel : INotifyPropertyChanged
{
public ObservableCollection<Example> Examples
{
get;
set;
}
// SelectedItem in the ComboBox
// SelectedItem.Ids will be ItemsSource for the ListBox
private Example _selectedItem;
public Example SelectedItem
{
get => _selectedItem;
set {
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
}
}
// SelectedId in ListView
private string _selectedId;
public string SelectedId
{
get => _selectedId;
set {
_selectedId= value;
OnPropertyChanged(nameof(SelectedId));
}
}
public void LoadExample()
{
ObservableCollection<Example> examples = new ObservableCollection<Example>();
examples.Add(new Example { Name = "Mark", IDs = new string[] { "123", "456" }});
examples.Add(new Example { Name = "Sally", IDs = new string[] { "789","101112" }});
Examples = examples;
}
}
}
XAML
<Grid>
<StackPanel HorizontalAlignment = "Left" >
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=Examples}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Name"/>
<ListView x:Name="myListView"
ItemsSource="{Binding SelectedItem.Ids}"
SelectedItem="{Binding SelectedId}"
Height="200" Margin="0,50,0,0"
Width="Auto"
VerticalAlignment="Top"
Background="AliceBlue">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox
Name="myCheckBox"
IsChecked="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListViewItem}}"
Margin="5, 0"/>
<TextBlock Text= "{Binding}" FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
There could several solutions to your case but most common and proprietary way is to use ValueConverter.
For this example i'll assume you have grid in the Window control. You need to add static resource:
<Window.Resources>
<local:ArrayValueConverter x:Key="arrayConverter"/>
</Window.Resources>
Then in your ListView's DataTemplate add Converter:
<TextBlock Text= "{Binding Path=IDs, Converter={StaticResource arrayConverter}}" FontWeight="Bold" />
ValueConverter himself:
public class ArrayValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string[] arr) {
return string.Join(',', arr);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Related
I have a dictionary, there's a int property in MyClass
public Dictionary<MyClass, BitmapImage> MyList = new Dictionary<MyClass, BitmapImage>();
I want listbox to show images from this collection (like ItemsSource?) and to show each property in textboxes near each image, is that possible?
Code behind example:
C#:
public MainWindow()
{
InitializeComponent();
PopulatePeople();
}
private void PopulatePeople()
{
List<Person> persons = new List<Person>();
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(), ImageAddress = "Images/peach.jpg" });
}
listBox.ItemsSource = persons;
}
XAML:
<ListBox Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MVVM example:
Download the example from OneDrive.
It is better to use DataTemplate in ListBox to show Image and TextBox.
Model:
public class Person
{
public int IDPerson { get; set; }
public string Name { get; set; }
public string ImageAddress { get; set; }
}
You can fill your collection in constructor of ViewModel:
ViewModel:
public class YourViewModel:INotifyPropertyChanged
{
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return persons; }
set
{
persons = value;
OnPropertyChanged("Persons");
}
}
public YourViewModel()
{
FillThePersons();
}
private void FillThePersons()
{
for (int i = 0; i < 10; i++)
{
persons.Add(new Person() { Name = "Bob " + i.ToString(),
ImageAddress="Images/peach.jpg" });// Images is the name folder in your project
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=ImageAddress}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The following example one image to all items:
<ListBox ItemsSource="{Binding Persons}">
<ListBox.Resources>
<BitmapImage x:Key="AnImage" UriSource="Images/yourImage.png" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource AnImage}" Width="50" Height="50"/>
<TextBox Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Result:
This is an example exhibiting the behaviour I'm having trouble with. I have a datagrid which is bound to an observable collection of records in a viewmodel. In the datagrid I have a DataGridTemplateColumn holding a combobox which is populated from a list in the viewmodel. The datagrid also contains text columns. There are some textboxes at the bottom of the window to show the record contents.
<Window x:Class="Customer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Customer"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:SelectedRowConverter x:Key="selectedRowConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow,
Converter={StaticResource selectedRowConverter}, Mode=TwoWay}"
CanUserAddRows="True" Grid.Row="0" SelectionChanged="dgCustomers_SelectionChanged">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Country">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cmbCountry" ItemsSource="{Binding DataContext.countries,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
DisplayMemberPath="name" SelectedValuePath="name" Margin="5"
SelectedItem="{Binding DataContext.SelectedCountry,
RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" SelectionChanged="cmbCountry_SelectionChanged" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding name}" Width="1*"/>
<DataGridTextColumn Header="Phone" Binding="{Binding phone}" Width="1*"/>
</DataGrid.Columns>
</DataGrid>
<Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<BulletDecorator Grid.Column="0">
<BulletDecorator.Bullet>
<Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="1">
<BulletDecorator.Bullet>
<Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="2">
<BulletDecorator.Bullet>
<Label Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
</BulletDecorator>
</Grid>
</Grid>
</Window>
Initially there are no records so the datagrid is empty and shows just one line containing the combobox. If the user enters data into the text columns first then a record is added to the collection and the combobox value can be added to the record. However, if the user selects the combobox value first, then the value disappears when another column is selected. How do I get the combobox data added to the record if it is selected first?
Codebehind:
public partial class MainWindow : Window
{
public GridModel gridModel { get; set; }
public MainWindow()
{
InitializeComponent();
gridModel = new GridModel();
//dgCustomers.DataContext = gridModel;
this.DataContext = gridModel;
}
private void cmbCountry_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox c = sender as ComboBox;
Debug.Print("ComboBox selection changed, index is " + c.SelectedIndex + ", selected item is " + c.SelectedItem);
}
}
The Record class:
public class Record : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private string _phone;
public string phone
{
get { return _phone; }
set
{
_phone = value;
OnPropertyChanged("phone");
}
}
private int _countryCode;
public int countryCode
{
get { return _countryCode; }
set
{
_countryCode = value;
OnPropertyChanged("countryCode");
}
}
}
Country class:
public class Country : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _id;
public int id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("id");
}
}
private int _code;
public int code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged("code");
}
}
public override string ToString()
{
return _name;
}
}
GridModel:
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> customers { get; set; }
public List<Country> countries { get; set; }
public GridModel()
{
customers = new ObservableCollection<Record>();
countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
}
private Country _selectedCountry;
public Country SelectedCountry
{
get
{
return _selectedCountry;
}
set
{
_selectedCountry = value;
_selectedRow.countryCode = _selectedCountry.code;
OnPropertyChanged("SelectedRow");
}
}
private Record _selectedRow;
public Record SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
Debug.Print("Datagrid selection changed");
OnPropertyChanged("SelectedRow");
}
}
}
Converters:
class Converters
{
}
public class SelectedRowConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Record)
return value;
return new Customer.Record();
}
}
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
The behaviour you are seeing is as expected. The reason behind it is that the ComboBox ItemsSource as well as SelectedItem both are bound to Properties of the Window's DataContext while the other columns are bound to your DataGrid's ItemsSource. Hence when you modify the columns other than the dropdown the data is added to the observable collection.
What you can do is after a value is selected from the drop down you need to add a record yourself (possibly by calling a function from your SelectedCountry property)
EDIT
Based on your code I made a working model making as little changes as possible to your existing code. I could not use the converter as I did not have the details of the class Customer
Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Button HorizontalAlignment="Right" Content="Add User" Margin="0,2,2,2" Command="{Binding AddUserCommand}"/>
<DataGrid x:Name="dgCustomers"
AutoGenerateColumns="False"
ItemsSource="{Binding customers}"
SelectedItem="{Binding SelectedRow}"
SelectionUnit="FullRow"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Country">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Focusable="False"
ItemsSource="{Binding DataContext.countries, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="name"
SelectedValuePath="code"
SelectedValue="{Binding countryCode, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding name, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
<DataGridTextColumn Header="Phone" Binding="{Binding phone, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<BulletDecorator Grid.Column="0">
<BulletDecorator.Bullet>
<Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="1">
<BulletDecorator.Bullet>
<Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="2">
<BulletDecorator.Bullet>
<Label Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
</BulletDecorator>
</Grid>
</Grid>
Your GridModel class
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> customers { get; set; }
public ObservableCollection<Country> countries
{
get;
private set;
}
public GridModel()
{
customers = new ObservableCollection<Record> { };
AddUserCommand = new RelayCommand(AddNewUser);
countries = new ObservableCollection<Country>
{
new Country { id = 1, name = "England", code = 44 },
new Country { id = 2, name = "Germany", code = 49 },
new Country { id = 3, name = "US", code = 1},
new Country { id = 4, name = "Canada", code = 11 }
};
}
private void AddNewUser()
{
customers.Add(new Record());
}
public ICommand AddUserCommand { get; set; }
private Record _selectedRow;
public Record SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
Debug.Print("Datagrid selection changed");
OnPropertyChanged("SelectedRow");
}
}
}
I have used MVVMLight toolkit which contains RelayCommand. You can also define your own ICommand implementation and use it instead of the toolkit
EDIT 2
Fixed the bug introduced by me which would prevent the combobox from displaying the Country if the data comes from the data base. The improved code does not require any converter either
I am creating a application using WPF. I am able to bind Checkboxlist by using the below code:
XAML:
<DataTemplate x:Key="defaultTemplate">
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Checked}" Value="true">
<Setter TargetName="lstcheckbox" Property="ContentTemplate" Value="{DynamicResource Template1}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="Template1">
<TextBox Width="100"></TextBox>
</DataTemplate>
<local:SomeTemplateSelector x:Key="SomeTemplateSelector" DefaultTemplate="{StaticResource defaultTemplate}"
Template1="{StaticResource Template1}">
</local:SomeTemplateSelector>
</Window.Resources>
<Grid>
<dxd:DockLayoutManager x:Name="dockManager" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible">
<dxd:LayoutGroup Caption="Review Checklist" Orientation="Vertical" ShowCaption="True" GroupBorderStyle="Group" CaptionAlignMode="AlignInGroup">
<dxd:LayoutControlItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="8,5,5,5">Safety Equipment</TextBlock>
<!--<ListView x:Name="lstSafEquip" Grid.Column="0" Grid.Row="1" BorderThickness="0" Margin="5,5,5,5" SelectionMode="Multiple" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource SomeTemplateSelector}" ItemTemplate="{StaticResource ItemDataTemplate}" IsSynchronizedWithCurrentItem="true" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="200" />-->
<ListView x:Name="lstSafEquip" Grid.Column="0" Grid.Row="1" BorderThickness="0" Margin="5,5,5,5" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource SomeTemplateSelector}" IsSynchronizedWithCurrentItem="true" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="200" />
</Grid>
</dxd:LayoutControlItem>
</dxd:LayoutGroup>
</dxd:DockLayoutManager>
</Grid>
CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
BindCheckBoxs(lstSafEquip);
}
private void BindCheckBoxs(ListView lst)
{
try
{
ObservableCollection<Data> Source = new ObservableCollection<Data> { new Data(1, "Apple"), new Data(2, "Mango"), new Data(3, "Others"), new Data(3, "Banana"), new Data(3, "Grapes") };
lst.ItemsSource = Source;
}
catch (Exception ex)
{
throw new Exception(ex.Message.ToString());
}
}
}
public class SomeTemplateSelector : DataTemplateSelector
{
public DataTemplate Template1 { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Data _Item = (Data)item;
if (_Item.Name == "Others")
{
return Template1;
}
return DefaultTemplate;
}
}
DATA.CS:
public class Data
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
public Data(int _ID, string _Name)
{
ID = _ID;
Name = _Name;
Checked = false;
}
}
PROBLEM:
I want to add textbox just below the checkboxlist item Others when user will checked this item.
By using the above code trigger enter textbox for each item when i checked.
please let me know where i am going wrong. I will try my best to provide more info if needed.
From what i understood from your post and comments:
Add a TextBox as Barptad suggested but use a BooleanToVisibilityCollapsed Converter as stated below:
BooleanToVisibilityCollapsedConverter.cs:
public class BooleanToVisibilityCollapsedConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return ((bool) value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture) {
//never used
return null;
}
}
XAML:
<DataTemplate x:Key="defaultTemplate">
<StackPanel>
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<TextBox Visibility="{Binding IsChecked, ElementName=lstcheckbox}"/>
</StackPanel>
</DataTemplate>
If you use Visibility.Hidden the textbox is not visible but still takes place.
After merging all suggestions and comments. I reached on the solution.
XAML:
Added textbox in default template
<DataTemplate x:Key="defaultTemplate">
<StackPanel>
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Unchecked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<TextBox Name="txtOther" Visibility="{Binding Path=Visible}" Text="test" Width="100"></TextBox>
</StackPanel>
</DataTemplate>
XAML.CS:
private void lstcheckbox_Checked_1(object sender, RoutedEventArgs e)
{
Data some = (sender as CheckEdit).DataContext as Data;
if (some.Name.Contains("Others"))
{
some.Visible = ToggleVisibility(some.Visible);
}
}
private Visibility ToggleVisibility(Visibility visibility)
{
return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
DATA.CS:
Added visible property and inherited with INotifyPropertyChanged to track change in property in Data.cs
public class Data : INotifyPropertyChanged
{
private Visibility _visible;
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
//public Visibility Visible { get; set; }
public Visibility Visible
{
get { return this._visible; }
set
{
this._visible = value;
RaisePropertyChanged("Visible");
}
}
public Data(int _ID, string _Name,Visibility _visible)
{
ID = _ID;
Name = _Name;
Visible = _visible;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
SomeTemplateSelector.CS
public class SomeTemplateSelector : DataTemplateSelector
{
public DataTemplate OthersTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return DefaultTemplate;
}
}
Hope this will help others.
Correct Me If I am worng!
U want to show textbox only under "Others" item when it is checked.
For the scenario mentioned above u need to have two different templates that changes on the value of item.
For e.g if the item is "Others" it should have template that have a text box just below check box which is only shown when checkbox is checked otherwise collapsed.
for the second type of items it should have different template even if it is checked no textbox should be displayed under it.
(Step 1) Create a Template Selector
public class ValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate OthersTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
DataTemplate template = DefaultTemplate;
CheckList data = item as CheckList;
if (data.Description.Contains("Others"))
{
template = OthersTemplate;
}
return template;
}
}
Reference assembly in your file.
(Step 3) Create two templates as given below
<Window.Resources>
<DataTemplate x:Key="DefaultTemplate">
<StackPanel>
<CheckBox IsChecked="{Binding Path=IsChecked}" Content="{Binding Path=Description}"></CheckBox>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="OthersTemplate">
<DataTemplate.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="false">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataTemplate.Resources>
<StackPanel>
<CheckBox IsChecked="{Binding Path=IsChecked}" Content="{Binding Path=Description}"></CheckBox>
<TextBox x:Name="othersTextBox">
</TextBox>
</StackPanel>
</DataTemplate>
<local:ValueDataTemplateSelector x:Key="CheckListTemplateSelector"
DefaultTemplate="{StaticResource DefaultTemplate}"
OthersTemplate="{StaticResource OthersTemplate}"/>
</Window.Resources>
How can I add items to listbox when item source is a list.
XAML:
<ListBox Grid.Row="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Source={StaticResource viewModel}, Path=CultureEvents}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source={StaticResource viewModel}, Path=ItemTitle}" Height="30" HorizontalAlignment="Left" Margin="116,364,0,0" VerticalAlignment="Top" Width="334" Foreground="White" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In viewmodel I have list:
public List<CultureEvent> CultureEvents { get; set; }
And property:
public string ItemTitle
{
get
{
return ?;
}
set
{
? = value;
OnPropertyChanged(new PropertyChangedEventArgs("ItemTitle"));
}
}
But I don't know what to put into property.
private string _itemTitle
public string ItemTitle
{
get
{
return _itemTitle;
}
set
{
_itemTitle = value;
OnPropertyChanged(new PropertyChangedEventArgs("ItemTitle"));
}
You would generate The list something like this,
CultureEvents = new List<CultureEvent>();
CultureEvents.Add(new CultureEvent{Title = "Yourvalue"} );
Please note:
The two types in the lists are simplified a lot for this example and must be kept separate.
The use of an int as connection between the types can not be changed.
The problem:
Given the code below, how do I get the ComboBox marked with ??? to:
Display the ColorDefs.Name as its content.
Set SelectedItem to the one where Models.DisplayColorNumber is equal to ColorDefs.ColorNumber.
Update the Models.DisplayColorNumber updated if the selection is changed.
In code-behind
public List<ModelData> Models { get; }
public List<DisplayColorDefinition> ColorDefs { get; }
DataContext=this;
XAML:
<ListBox ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox ??? />
<TextBlock Text="{Binding Models, Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ModelData type:
public class ModelData
{
private string name;
private int displayColorNumber;
public string Name
{
get { return name; }
set { name = value; }
}
public int DisplayColorNumber
{
get { return displayColorNumber; }
set { displayColorNumber = value; }
}
}
DisplayColorDefinition type:
public class DisplayColorDefinition
{
private int colorNumber;
private string name;
private Color displayColor;
public int ColorNumber
{
get { return colorNumber; }
set { colorNumber= value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public Color DisplayColor
{
get { return displayColor; }
set { displayColor = value; }
}
}
Use the SelectedValue and SelectedValuePath :
<ListBox ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=DataContext.ColorDefs, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
SelectedValue="{Binding Path=DisplayColorNumber}"
SelectedValuePath="ColorNumber"
/>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding DisplayColorNumber}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
SelectedValue will be the property on the Model object, and SelectedValuePath will indicate which property of the DisplayColorDefinition to use for the binding.