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"} );
Related
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;
}
}
I have this code snipper
DockPanel Margin="3"
HorizontalAlignment="Stretch" >
<Label
Content="Port #1"
DockPanel.Dock="Left"/>
<ComboBox
x:Name="Port1"
ItemsSource="{Binding PortList}"
SelectedItem="{Binding Port1Selected}">
</ComboBox>
</DockPanel>
<DockPanel Margin="3"
HorizontalAlignment="Stretch" >
<Label
Content="Port #2"
DockPanel.Dock="Left"/>
<ComboBox
ItemsSource="{Binding PortList}"
SelectedItem="{Binding Port2Selected}"/>
</DockPanel>
as you can see there are two DockPanels which are basically the same besides their SelectedItem property.
at the moment I have 8 those which I duplicated, but it seems like too much copy and paste and prone to errors, and in the future, I might have more port, so I will need to create a new DockPanel.
or even have more complex control that I will need to "duplicate".
I was trying to make custom control contains <DockPanel> /// </DockPanel > part, but i don't know to achieve the same functionality and having the same bindings
my question is, how can i achieve this without reputations.
i was looking here and here prism-example for some ideas
UPDATE
My item controller
<ItemsControl ItemsSource="{Binding PortList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch"
Margin="3">
<Label
Content="{Binding PortName}"
DockPanel.Dock="Left"/>
<ComboBox
ItemsSource="{Binding PortList}"
SelectedItem="{Binding SelectedPort}"/>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
my port model
public class PortModel : BindableBase
{
public string PortName { get; set; }
public List<string> PortList { get; set; }
// public string SelectedPort { get; set; }
private string _selectedPort;
public string SelectedPort
{
get { return _selectedPort; }
set { SetProperty(ref _selectedPort, value); }
}
}
and my main list in in the view model
#region List Properties
private ObservableCollection<PortModel> portsList;
public ObservableCollection<PortModel> PortList
{
get { return portsList; }
set { SetProperty(ref portsList, value); }
}
and adding items to the list
private void LoadSerialPorts()
{
portsList.Clear();
for (int i = 0; i < Constants.NUMBER_OF_COMS; i++)
{
PortList.Add(new PortModel
{
PortList = serialPortHandler.GetSerialPortPort().ToList(),
PortName = $"Port #{i}",
});
}
}
But now the selected port does not fire
this is xaml part
<ItemsControl x:Name="EventsTop">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,1,0,0">
<Button Content="{Binding Name}" Template="{DynamicResource ButtonFirst}" Height="50" Margin="15,0,0,0" Padding="10,0,15,0" FontSize="19" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome" BorderThickness="5,0,0,0" BorderBrush="#8CC152" Background="#2980B9" HorizontalContentAlignment="Left" Foreground="Black" Click="TabOpen" Tag="{Binding Id}"></Button>
<StackPanel Background="#2980B9" Margin="15,0,0,5" Visibility="Collapsed" AllowDrop="True" Tag="{Binding Id}" Drop="RowDrop">
<Border BorderThickness="5,0,0,0" BorderBrush="#8CC152">
<StackPanel>
<DockPanel LastChildFill="False">
<Label DockPanel.Dock="Left" Width="140" Content="Date" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="190" Content="Event" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="100" Content="Select" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
</DockPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="150">
<ItemsControl ItemsSource="{Binding Details}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="False">
<Label Content="{Binding Date}" DockPanel.Dock="Left" Width="140" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label Content="{Binding EventName}" DockPanel.Dock="Left" Width="165" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Border Width="97">
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding Checked}"></CheckBox>
</Border>
<Button Width="25" DockPanel.Dock="Left" Content="" BorderThickness="0" Background="Transparent" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome"></Button>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this is xaml.cs
private void WindowLoaded(object sender, RoutedEventArgs e)
{
EventHelper eventHelper = new EventHelper();
TopEvents = eventHelper.GetSports(EventHelper.EventGroup.Top);
foreach (Sport item in TopEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsTop.ItemsSource = TopEvents;
AllEvents = eventHelper.GetSports(EventHelper.EventGroup.All);
foreach (Sport item in AllEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsAll.ItemsSource = AllEvents;
Sport.ItemsSource = eventHelper.GetSports(EventHelper.EventGroup.All);
}
private void RowMouseDown(object sender, MouseButtonEventArgs e)
{
DockPanel currentRow = (DockPanel) sender;
int rowOffset = Convert.ToInt32(currentRow.Tag);
DragDrop.DoDragDrop(currentRow,rowOffset,DragDropEffects.Copy);
}
private void RowDrop(object sender, DragEventArgs e)
{
int rowOffset = (int) e.Data.GetData(typeof (int));
AllEvents[0].Name = "1";
}
Also my model in collection
class Sport : INotifyPropertyChanged
{
private int _id;
private string _name = string.Empty;
private ObservableCollection<Details> _details = new ObservableCollection<Details>();
public int Id
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Content");
}
}
public ObservableCollection<Details> Details
{
get { return _details; }
set { _details = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
}
So when I am changing property its throwing MessageBox but not updating GUI.
I xaml.cs I am calling methods GetEvents thats
return ObservableCollection
I want to change Name in Sport which is in ObservableCollaction<Sport> AllEvents
You can see it in RowDrop method in xaml.cs
In debugging I notice that AllEvents[0].Name was changed but view was not updating
UPDATE
Part of ObservabelCollection declaration
public MainPage()
{
InitializeComponent();
AllEvents = new ObservableCollection<Sport>();
TopEvents = new ObservableCollection<Sport>();
EventsTop.ItemsSource = TopEvents;
EventsAll.ItemsSource = AllEvents;
}
private ObservableCollection<Sport> AllEvents;
private ObservableCollection<Sport> TopEvents;
UPDATE SECOND
I caught that when I am using window activated event it is working
I found solution.
So, ObservableCollection is working very well but,It needs to be refreshed
for appearing in view and for it we need to use
CollectionViewSource.GetDefaultView(ObservableCollection).Refresh()
method for it
I think it will help someone
The problem with property named passed to NotifyPropertyChanged method. The name of parameter should be property name. Please change the Name property as
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
Use CallerMemberNameAttribute to avoid having to get the name correct and allowing refactoring:
private void NotifyPropertyChanged([CallerMemberName] string info = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged(); //now no need to specify
}
}
Every property setter should notify property change, so:
public IEnumerable<Details> Details //note IEnumerable, no calling code needs to know its concrete type
{
get { return _details; }
set
{
_details = value;
NotifyPropertyChanged();
}
}
And with an observable range collection you could do this:
private readonly ObservableRangeCollection<Details> _details = new ObservableRangeCollection<Details>();
public IEnumerable<Details> Details
{
get { return _details; }
set { _details.Replace(value); }
}
From MSDN.
Occurs when an item is added, removed, changed, moved, or the entire
list is refreshed.
The changed does not mean when child properties are changed, but when you change the item at any index.
So when you modify a collection item you will need to notify the binding that the property was changed. From your window's viewmodel after you have modified the item in the collection, you would notify that the collection was changed.
NotifyPropertyChanged("AllEvents");
I've got stuck in this situation, even my best friend "Google" is not helpful.
I have nine TabItems. I am able to display data. This is my screen.
Here is my approach :
<UserControl.Resources>
<DataTemplate x:Key="ListItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Width="250" Text="{Binding Path=HmiText}" HorizontalAlignment="Right" Height="28" Margin="10,10,0,0" FontSize="12" FontFamily="Calibri" FontWeight="Bold" VerticalAlignment="Center"></TextBlock>
<ComboBox Width="115" ItemsSource="{Binding ComboBoxSourceItem}" DisplayMemberPath="OptionsText"
SelectedValuePath="OptionsValue" SelectedValue="{Binding DefaultValue}" Height="23" Margin="10,5,0,5" HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</DataTemplate>
</UserControl.Resources>
<TabControl Grid.Row="1" Grid.ColumnSpan="2" TabStripPlacement="Top" VerticalContentAlignment="Stretch" BorderThickness="1" BorderBrush="#005399" Background="White">
<TabItem Header="Unit Configuration" Width="auto">
<ListBox Name="UnitConfigurationlist" Margin="5" HorizontalContentAlignment="Stretch" ItemsSource="{Binding UnitConfigurationItemSource}" ItemTemplate="{StaticResource ListItem}" >
</ListBox>
</TabItem>
<TabItem Header="Programmable Features" Selector.IsSelected="True" Width="auto">
<ListBox Name="list" Margin="5" HorizontalContentAlignment="Stretch" ItemsSource="{Binding SelectedProgrammableFeature}" ItemTemplate="{StaticResource ListItem}" >
</ListBox>
</TabItem>
<TabItem Header="Main Menu Configuration" Width="auto" >
<ListBox Name="MainMenuConfigurationlist" Margin="5" HorizontalContentAlignment="Stretch" ItemsSource="{Binding MainMenuConfigurationItemSource}" ItemTemplate="{StaticResource ListItem}" >
</ListBox>
</TabItem>
<TabItem Header="Cycle Sentry Setup" Width="auto">
<ListBox Name="CycleSentrySetuplist" Margin="5" HorizontalContentAlignment="Stretch" ItemsSource="{Binding CycleSentrySetuplistItemSource}" ItemTemplate="{StaticResource ListItem}" >
</ListBox>
</TabItem>
<TabItem Header="Language Setup" Width="auto">
</TabItem>
</TabControl>
Here is my view model :
public class UnitConfigurationViewModel : ViewModelBase2
{
private IOptiSetPlusService optiSetPlusService;
public ObservableCollection<ProgrammableFeatures> ProgrammableFeaturesItemSource
{
get;
private set;
}
public ObservableCollection<ProgrammableFeatures> UnitConfigurationItemSource
{
get;
private set;
}
public UnitConfigurationViewModel(IOptiSetPlusService os)
{
optiSetPlusService = os;
InitializeUnitConfiguration();
}
void InitializeUnitConfiguration()
{
GetControlDependencyID();
//here I am reading the xml file and filling the collection.
this.ProgrammableFeaturesItemSource = GetCurrentProgrammableFeaturesItemSource(optiSetPlusService.GetProgrammableFeaturesList(ControlDependency.ControlDependencyId.ToString(), "programmableFeatures"));
this.UnitConfigurationItemSource = GetCurrentProgrammableFeaturesItemSource(optiSetPlusService.GetProgrammableFeaturesList(ControlDependency.ControlDependencyId.ToString(), "unitConfiguration"));
}
}
and finally here is my model:
public class ProgrammableFeatures
{
string toolTip;
public string ToolTip
{
get
{
return toolTip;
}
set
{
toolTip = value;
}
}
string hmiText;
public string HmiText
{
get
{
return hmiText;
}
set
{
hmiText = value;
}
}
string defaultValue;
public string DefaultValue
{
get
{
return defaultValue;
}
set
{
defaultValue = value;
}
}
//this collection will be shown in combobox.
ObservableCollection<GdtAvailableOptions> comboBoxSourceItem;
public ObservableCollection<GdtAvailableOptions> ComboBoxSourceItem
{
get
{
return comboBoxSourceItem;
}
set
{
comboBoxSourceItem = value;
}
}
}
public class GdtAvailableOptions
{
private string optionsValue;
public string OptionsValue
{
get
{
return optionsValue;
}
set
{
optionsValue = value;
}
}
private string optionsText;
public string OptionsText
{
get
{
return optionsText;
}
set
{
optionsText = value;
}
}
}
Now my problem is once the combo-box selection changed, it should affect some other parameters across all of the tabs(ex: rail option should be enabled). I don't how to do this. Please guide me. even any link provided will be also helpful.
<ComboBox Width="115" ItemsSource="{Binding ComboBoxSourceItem}" DisplayMemberPath="OptionsText"
SelectedValuePath="OptionsValue" SelectedValue="{Binding DefaultValue}" Height="23" Margin="10,5,0,5" HorizontalAlignment="Left"/>
You should have a public property for DefaultValue in UnitConfigurationViewModel
When it is changed you should have a call to the set
In the set manipulate what you need to
You may have a problem with that one property shared by several collections
And see the comment from dev hedgehog
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.