WPF Sorting an ObservableCollection de-selects a ComboBox - c#

I have a ComboBox where a user can select what JobType they are working on. The ComboBox has a list of AllJobTypes. The problem stems from when a user adds a new JobType, I add the JobType to the AllJobTypes ObservableCollection, then sort it. When the sorting happens the ComboBox get's de-selected and not really sure why. The JobConfig.SelectedJobType.Name never changes in this process. Is there a way to sort an observable collection where it doesn't break the ComboBox?
public class JobTypeList : ObservableCollection<JobType> {
public void SortJobTypes() {
var sortableList = new List<JobType>(this);
sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
//this works but it creates a bug in the select for JobTypes
for (int i = 0; i < sortableList.Count; i++) {
this.Move(this.IndexOf(sortableList[i]), i);
}
}
And in the XAML
<ComboBox Grid.Column="0" SelectionChanged="JobTypeComboBox_SelectionChanged"
Name="JobTypeComboBox"
ItemsSource="{Binding Path=AllJobTypes}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding
Path=JobConfig.SelectedJobType.Name}" />

Instead of sorting the collection in the view model, you should bind the ComboBox's ItemsSource to a CollectionViewSource, where you can specify a SortDescription:
<Window ...
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
...>
<Window.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding AllJobTypes}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
...
<ComboBox ItemsSource="{Binding Source={StaticResource cvs}}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding JobConfig.SelectedJobType.Name}"/>
...
</Window>
For further information see How to: Sort and Group Data Using a View in XAML

Here's a version using ItemsSource/SelectedItem. Note that you can add a new item to the list and sort it without losing the currently selected item in the view.
The window
<Window
x:Class="SortingAList.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:SortingAList"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
Text="{Binding NewJobType, Delay=1000}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="200" />
<ComboBox
Grid.Row="1"
ItemsSource="{Binding JobTypes}"
SelectedItem="{Binding SelectedJobType}"
DisplayMemberPath="Name"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="200" />
</Grid>
</Window>
The code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify([CallerMemberName]string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public class ViewModel : Notifier
{
private JobType _selectedJobType;
private string _newJobType;
public JobTypeList JobTypes { get; private set; }
public JobType SelectedJobType { get => _selectedJobType; set { _selectedJobType = value; Notify(); } }
public string NewJobType { get => _newJobType; set { _newJobType = value; Notify(); AddNewJobType(value); } }
public ViewModel()
{
JobTypes = new JobTypeList();
JobTypes.Add(new JobType { Name = "Butcher" });
JobTypes.Add(new JobType { Name = "Baker" });
JobTypes.Add(new JobType { Name = "LED maker" });
}
private void AddNewJobType(string name)
{
if(JobTypes.Any(x => x.Name == name)) return;
JobTypes.Add(new JobType { Name = name });
JobTypes.SortJobTypes();
}
}
public class JobType : Notifier
{
private string _name;
public string Name { get => _name; set { _name = value; Notify(); } }
}
Using your JobTypesList
public class JobTypeList : ObservableCollection<JobType>
{
public void SortJobTypes()
{
var sortableList = new List<JobType>(this);
sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
//this works but it creates a bug in the select for JobTypes
for(int i = 0; i < sortableList.Count; i++)
{
this.Move(this.IndexOf(sortableList[i]), i);
}
}
}

Related

WPF selectAll/unselectAll rows of a DataGrid with MVVM

I have a WPF DataGrid, the ItemsSource property is bound to the viewmodel.
On click on a Button a method is doing work on the selected rows of the DataGrid.
When the work is done, I would like to unselectAll the DataGrid's rows from the viewmodel, how can I achieve this in a MVVM way?
XAML :
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding ObCol_View}"
SelectedItem="{Binding SelectedItem}
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<GalaCmd:EventToCommand Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
<Button Content="Apply" Command="{Binding PerformCommand}"/>
The viewmodel
public class ViewModelprices_manager<T> : WindowViewModel
{
public ViewModelprices_manager()
{
SelectionChangedCommand = new RelayCommand<IList>
(items =>
{
SelectedItems = items;
}
);
myObCol_View = new ObservableCollection<View_prices_manager<T>>();
}
public IList SelectedItems { get; set; }
private readonly ObservableCollection<View_prices_manager<T>> myObCol_View;
public ObservableCollection<View_prices_manager<T>> ObCol_View_Parametric { get { return myObCol_View; } }
public RelayCommand<IList> SelectionChangedCommand
{
get;
private set;
}
private RelayCommand myPerformCommand;
public RelayCommand PerformCommand
{
get
{
if (myPerformCommand == null)
myPerformCommand = new RelayCommand(PerformCommandAction);
return myPerformCommand;
}
}
private void PerformCommandAction()
{
perform();
}
public void perform()
{
foreach (View_prices_manager<T> item in SelectedItems)
{
item.Price *= ratio;
}
//DataGrid.UnselectAll <-- Here I want to unselect all to reset the selection in SelectedItems
}
}
The topic is reopened, I would like to see your solution
A simplified example.
If you need any additional details, then write which ones and I will add or explain them.
The example uses the BaseInpc and RelayCommand classes.
Collection item class:
using Simplified;
namespace ClearSelectedItems
{
public class Product : BaseInpc
{
private string _title;
private decimal _price;
public string Title { get => _title; set => Set(ref _title, value); }
public decimal Price { get => _price; set => Set(ref _price, value); }
}
}
ViewModel:
using Simplified;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
namespace ClearSelectedItems
{
public class ProductsViewModel
{
public IList SelectedProducts{ get; set; }
public decimal Ratio { get; set; }
private ICommand _performCommand;
public ICommand PerformCommand => _performCommand
?? (_performCommand = new RelayCommand(PerformExecute));
private void PerformExecute(object parameter)
{
foreach (var product in SelectedProducts.Cast<Product>())
{
product.Price *= Ratio;
}
// Clearing selection
SelectedProducts.Clear();
}
// An example of filling a collection
public List<Product> Products { get; }
= new List<Product>()
{
new Product() { Title = "IPhone", Price=1500},
new Product() { Title = "Samsung", Price=1200},
new Product() { Title = "Nokia", Price=1000},
new Product() { Title = "LG", Price=500}
};
}
}
Class of static handlers.
Used for simplicity, so as not to create Behavior.
using System;
using System.Windows.Controls.Primitives;
namespace ClearSelectedItems
{
public static class Handlers
{
public static EventHandler OnDataGridInitialized { get; } = (sender, e) =>
{
MultiSelector selector = (MultiSelector)sender;
ProductsViewModel viewModel = (ProductsViewModel)selector.DataContext;
viewModel.SelectedProducts = selector.SelectedItems;
};
}
}
Window XAML:
<Window x:Class="ClearSelectedItems.ExampleWindow"
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:ClearSelectedItems"
mc:Ignorable="d"
Title="ExampleWindow" Height="450" Width="400">
<Window.DataContext>
<local:ProductsViewModel/>
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Products}"
Initialized="{x:Static local:Handlers.OnDataGridInitialized}"/>
<TextBox Text="{Binding Ratio}" VerticalAlignment="Bottom" HorizontalAlignment="Left"
MinWidth="100" Margin="5" Padding="15 5"/>
<Button Content="Apply Ratio" Command="{Binding PerformCommand}"
Margin="5" Padding="15 5"
HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Grid>
</Window>
To clear the selection the MVVM way.
Bind to the SelectedItem property to the view model:
<DataGrid SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
And set the SelectItem to null in your code:
viewModel.SelectItem = null;
Of course, your viewModel class must implement INotifyPropertyChanged properly.

TextBox with ListView Two Binding using Mvvm

<Stackpanel>
<TextBox x:Name="txtid" Width="90" Text={Binding Name} Height="25"/>
<TextBox x:Name="txtname" Width="90" Text={Binding Age} Height="25" Margin="0 10 0 10"/>
<Button Command={Binding AddCommand} Content="Add"/>
<ListView ItemsSource={Binding StudentList}/>
</Stackpanel>
ViewModel
public class StudentViewModel : INotifyPropertyChanged
{
public StudentViewModel()
{
_studentList = new ObservableCollection<StudentDetails>();
LoadCommand();
}
private ObservableCollection<StudentDetails> _studentList;
public ObservableCollection<StudentDetails> StudentList
{
get { return _studentList; }
set
{
_studentList = value;
OnPropertyChanged("StudentList");
}
}
public StudentDetails SelectedItems { get; set; }
private string _name;
private int _age;
public string Name
{
get { return _name;}
set { _name = value; OnPropertyChanged("Name")}
}
public string Age
{
get { return _age;}
set { _age = value; OnPropertyChanged("Age")}
}
public ICommand AddCommand { get; set; }
public void LoadCommand()
{
AddCommand = new CustomCommand(Add, CanAdd);
}
private bool CanAdd(object obj)
{
return true;
}
private void Add(object obj)
{
StudentList.Add(new StudentDetails { Name = Name, Age = Age });
}}
Model
public class StudentDetails : INotifyPropertyChanged
{
private string _name;
private int _age;
public string Name
{
get { return _name;}
set { _name = value; OnPropertyChanged("Name")}
}
public string Age
{
get { return _age;}
set { _age = value; OnPropertyChanged("Age")}
}}
I have two textbox and a listview like above. how to do two way binding using MVVM?? which means the entered textbox value should add to listview and if i select the values in the listview then the selected value should bind to the same textbox so that i can update the values. how to do it??
I have tried to run your code. It has lots of errors. Anyways from your question I think you want to have a listview and when user selects a particular list item, the corresponding age and name is displayed in text boxes and if user want to update the data, you want to add it to the list. First create a list view with data template that binds to the class file properties.
Now also create a StudentDetails object and bind the SelectedItem of the list view to it.
When user selects a list item, you get SelectionChanged event. During this time update the property of 2 text boxes to display the selected list items data in them.
Now in the add button event handler, update the list data for corresponding selected item. Make sure you bind the listitemssource to an ObservableCollection
I would have different view models for an individual student and for the student list. Name and Age properties don't actually belong to the list.
I used MVVM Light syntax for the example:
StudentViewModel
public class StudentViewModel : ViewModelBase
{
private string _name;
private int _age;
public string Name
{
get { return _name; }
set { Set<string>(ref _name, value); }
}
public int Age
{
get { return _age; }
set { Set<int>(ref _age, value); }
}
}
StudentView.xaml
<UserControl x:Class="MasterDetailExample.Views.StudentView"
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:local="clr-namespace:MasterDetailExample.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:MasterDetailExample.ViewModel"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<WrapPanel HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBlock Text="Name: "/>
<TextBox Text="{Binding Name}" Width="150"/>
<TextBlock Text="Age: "/>
<TextBox Text="{Binding Age}" Width="20"/>
</WrapPanel>
</UserControl>
Now StudentsViewModel represents the student list:
public class StudentsViewModel : ViewModelBase
{
private ObservableCollection<StudentViewModel> _studentList;
private StudentViewModel _selectedStudent;
public StudentsViewModel()
{
StudentList = new ObservableCollection<StudentViewModel>();
StudentList.Add(new StudentViewModel { Name = "Joe", Age = 21 });
StudentList.Add(new StudentViewModel { Name = "Jane", Age = 19 });
}
public ObservableCollection<StudentViewModel> StudentList
{
get { return _studentList; }
private set { _studentList = value; }
}
public StudentViewModel SelectedStudent
{
get { return _selectedStudent; }
set { Set<StudentViewModel>(ref _selectedStudent, value); }
}
}
** List view, StudentsView**
<UserControl x:Class="MasterDetailExample.Views.StudentsView"
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:views="clr-namespace:MasterDetailExample.Views"
xmlns:vm="clr-namespace:MasterDetailExample.ViewModel"
d:DesignHeight="300"
d:DesignWidth="500"
mc:Ignorable="d">
<UserControl.Resources>
<vm:StudentsViewModel x:Key="StudentsVm" />
</UserControl.Resources>
<DockPanel DataContext="{StaticResource StudentsVm}">
<ListView DockPanel.Dock="Left" Width="100" ItemsSource="{Binding StudentList}" SelectedItem="{Binding SelectedStudent}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Separator />
<views:StudentView DockPanel.Dock="Right" DataContext="{Binding SelectedStudent}"/>
</DockPanel>
</UserControl>
Directly setting the DataContext kind of smells,
<views:StudentView DockPanel.Dock="Right" DataContext="{Binding SelectedStudent}"/>
In more complicated example, you would either create a DependencyProperty for the SelectedStudent, or implement some messaging logic to communicate between different view models.

Multiple ListBoxes SelectedItem Binding to same property

I have two listboxes that both contain the same type of items and I have both their SelectedItem Property bound to the same Property on my ViewModel. I expected that, when I selected some item in List A, the Selection in List B disappears. But that is not the case. I need an ugly workaround to make it work how I expect it. Any hints how to make it work without this workaround?
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem}" ></ListBox>
<ListBox Grid.Column="1" ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem}" ></ListBox>
<Button Grid.Row="1" Content="NULL" Grid.ColumnSpan="2" Click="ButtonBase_OnClick"></Button>
</Grid>
</Window>
C#
public partial class MainWindow : INotifyPropertyChanged
{
private Item _selectedItem;
public ObservableCollection<Item> Items1 { get; private set; }
public ObservableCollection<Item> Items2 { get; private set; }
public Item SelectedItem
{
get { return _selectedItem; }
set
{
// Uncomment this to make it work
//_selectedItem = null;
//OnPropertyChanged();
_selectedItem = value;
OnPropertyChanged();
}
}
public MainWindow()
{
Items1 = new ObservableCollection<Item>();
Items2 = new ObservableCollection<Item>();
Items1.Add(new Item("A1"));
Items1.Add(new Item("A2"));
Items2.Add(new Item("B1"));
Items2.Add(new Item("B2"));
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SelectedItem = null;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
public Item(string name)
{
Name = name;
}
string Name { get; set; }
}
Why not binding them (TwoWay binding) to two different Properties (e.g. SelectedItem1 and SelectedItem2), Then:
public Item SelectedItem1
{
get { return _selectedItem1; }
set
{
_selectedItem1 = value;
OnPropertyChanged();
if(value!=null) //Here is the trick.
SelectedItem2=null;
OnPropertyChanged("SelectedItem");
}
}
EDIT: Then for the rest of your application code you can have a SelectedItem property like below. Also don't forget to add OnPropertyChanged("SelectedItem") in setter of both SelectedItem1 and SelectedItem2
public Item SelectedItem
{
get{return SelectedItem1 != null ? SelectedItem1 : SelectedItem2}
}
If it's an ugly workaround you want then tap the SelectionChanged event:
<ListBox x:Name="ListBoxA" ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem}" SelectionChanged="ListBox_SelectionChanged"></ListBox>
<ListBox x:Name="ListBoxB" Grid.Column="1" ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem}" SelectionChanged="ListBox_SelectionChanged" ></ListBox>
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender == this.ListBoxA)
this.ListBoxB.UnselectAll();
else
this.ListBoxA.UnselectAll();
}
Better yet, use an attached behaviour to do the same thing.

How to bind to a nested class wpf

I have a view model called FieldViewModel:
public class FieldViewModel: INotifyPropertyChanged
{
private Field m_field;
public Field FieldData
{
get { return m_field; }
set
{
m_field = value;
NotifyPropertyChanged("FieldData");
}
}
and the Field class:
public class Field : INotifyPropertyChanged
{
public List<string> SqlTypes
{
get { return Enum.GetNames(typeof(SqlDbType)).ToList(); }
}
private string m_FieldName = null;
public string FieldName
{
get { return m_FieldName; }
set
{
m_FieldName = value;
NotifyPropertyChanged("FieldName");
}
}
private string m_FieldType = null;
public string FieldType
{
get { return m_FieldType; }
set
{
m_FieldType = value;
NotifyPropertyChanged("FieldType");
}
}
private bool m_NullAllow = false;
public bool NullAllow
{
get { return m_NullAllow; }
set
{
m_NullAllow = value;
NotifyPropertyChanged("NullAllowsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
The Xaml:
<ComboBox ItemsSource="{Binding FieldData.SqlTypes}" SelectionChanged="cmbField_Selected" Width="20" Height="20" Margin="5,5,0,5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<TextBox Width="120" Height="20" Text="{Binding FieldType}" Margin="0,5" VerticalAlignment="Top" HorizontalAlignment="Left"/>
now in the xaml i want to bind the variables of the Field Class but i can't see them
I tried some thing but nothing works, if i put the variables of the Field Class inside the FieldViewModel it works good.
Thank for your help.
Here is an example which works for me:
<Window x:Class="DataGridExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="czesciTable" ItemsSource="{Binding model.list}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Viewmodel:
public class ViewModel
{
public Model model { get; set; }
public ViewModel()
{
model = new Model();
}
}
And model:
public class Model
{
public List<String> list { get; set; }
public Model()
{
list = new List<string>();
list.Add("Item1");
list.Add("Item2");
list.Add("Item3");
}
}
Result:
Or ComboBox:
<ComboBox ItemsSource="{Binding model.list}" VerticalAlignment="Top"/>
The code in the question was mainly correct. It was only missing the initialization of the Field Class.

C# wpf listbox not updating from ObservableCollection

I'm trying to get the databinding I need to work with a ListBox.
I've parsed some data from a text file to a ObservableCollection<ViewModel> but the data isn't updating in the ListBox.
Here's some information:
The data which is written to from the parser:
class MainData
{
private static ObservableCollection<GroupViewModel> groupModelList = new ObservableCollection<GroupViewModel>();
public static ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
What GroupViewModel holds (not everything but it's all the same):
class GroupViewModel : INotifyPropertyChanged
{
private GroupModel groupModel;
public event PropertyChangedEventHandler PropertyChanged;
public GroupViewModel()
{
groupModel = new GroupModel();
}
public string Name
{
get { return groupModel.name; }
set
{
if (groupModel.name != value)
{
groupModel.name = value;
InvokePropertyChanged("Name");
}
}
}
...
}
And what GroupModel Holds:
class GroupModel
{
public string name { get; set; }
}
This is how the parser adds new items to the GroupModelView:
if (split[0] == "group")
{
currentGroup = new GroupViewModel();
currentGroup.Name = split[1];
MainData.GroupModelList.Add(currentGroup);
}
I created a ListBox in my WPF application with these XAML options:
<Window x:Class="SoundManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SoundManager.ViewModels"
xmlns:vm2="clr-namespace:SoundManager.Code"
Title="MainWindow" Height="720" Width="1280">
<Window.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
<vm2:MainData x:Key="MainData" />
</Window.Resources>
<ListBox Grid.Row="2" Height="484" HorizontalAlignment="Left" Margin="12,0,0,0" Name="lbFoundItems" VerticalAlignment="Top" Width="201" ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList/Name}" />
but for some reason the data isn't updating in the UI (new items aren't added visibly in the UI).
I've been just getting started with the MVVM pattern and databinding and I can't figure out what I'm doing wrong.
Thanks in advance!
GroupModelList/Name is not a valid property path here. Setting it like that does not make the ListBox show the Name property of the data items in the GroupModelList collection.
You would instead have to set the ListBox's DisplayMemberPath property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}"
DisplayMemberPath="Name"/>
or set the ItemTemplate property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Moreover, the GroupModelList property should not be static:
class MainData
{
private ObservableCollection<GroupViewModel> groupModelList =
new ObservableCollection<GroupViewModel>();
public ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
Then you might have MainData as a property in your view model, and bind the ListBox like this:
<ListBox ItemsSource="{Binding Source={StaticResource MainViewModel},
Path=MainData.GroupModelList}" .../>

Categories