I'm trying to use a dynamically created layout using CollectionView to show a series of properties of a class, all based on a list and I want to make it so one of the properties is a Combobox. How do I know what object the ComboBox needs to refer to?
Here is my CollectionView:
<CollectionView x:Name="taskList">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Task">
<VerticalStackLayout Margin="15">
<Entry Text="{Binding name}" IsReadOnly="True" />
<Entry Text="{Binding departmentsString}" IsReadOnly="True"/>
<HorizontalStackLayout>
<inputs:SfComboBox BackgroundColor="Black" TextColor="Green" DropDownIconColor="Green"/>
<Entry Text="{Binding deadline}" IsReadOnly="True" />
<Entry Text="{Binding author.fullName}" IsReadOnly="True"/>
</HorizontalStackLayout>
<Entry Text="{Binding description}" IsReadOnly="True" />
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
It has its ItemsSource declared like this:
taskList.ItemsSource = tasks;
tasks being:
ObservableCollection<Classes.Task> tasks { get; set; }
Here is the Task class:
public class Task
{
public Task(string name, List<string> departments, Status status, DateOnly deadline, Employee author, string description)
{
this.name = name;
this.departments = departments;
this.status = status;
this.deadline = deadline;
this.author = author;
this.description = description;
}
public string name { get; private set; }
public List<string> departments { get; private set; } = new List<string>();
public string departmentsString
{
get
{
string _ = "";
foreach (var department in departments)
{
_ += department + (department == departments.Last() ? "": ", ");
}
return _;
}
}
public Status status { get; private set; }
public DateOnly deadline { get; private set; }
public Employee? author { get; set; }
public string description { get; private set; }
public List<Employee> employees { get; private set; } = new List<Employee>();
public void AddEmployee(Employee employee)
{
if (departments.Contains(employee.department))
{
employees.Add(employee);
}
}
}
How do I make it so I can determine the instance of the class Task depending on which ComboBox I change?
Here is what the UI looks like:
Trying to bind the combobox to the Status property
You can try to set a data list for property ItemsSource of SfComboBox and bind a field to property SelectedItem of SfComboBox.
Suppose you would bind departments to the ItemsSource of SfComboBox, then we need to add a field (e.g. SelectedItem) to bind to property SelectedItem of SfComboBox:
Then we need to implement interface INotifyPropertyChanged for MyTask.cs and add field SelectedItem.(To prevent conflicts with the Task class in my project, I named it MyTask)
//add SelectedItem here
private string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
The whole code of MyTask
public class MyTask: INotifyPropertyChanged
{
public MyTask(string name, List<string> departments, int status, DateTime deadline, Employee author, string description)
{
this.name = name;
this.departments = departments;
this.status = status;
this.deadline = deadline;
this.author = author;
this.description = description;
}
//add SelectedItem here
private string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
public string name { get; set; }
public List<string> departments { get; private set; } = new List<string>();
public string departmentsString
{
get
{
string _ = "";
foreach (var department in departments)
{
_ += department + (department == departments.Last() ? "" : ", ");
}
return _;
}
}
public int status { get; private set; }
public DateTime deadline { get; private set; }
public Employee? author { get; set; }
public string description { get; private set; }
public List<Employee> employees { get; private set; } = new List<Employee>();
public void AddEmployee(Employee employee)
{
if (departments.Contains(employee.department))
{
employees.Add(employee);
}
}
}
Then we can use like this:
<editors:SfComboBox BackgroundColor="Black" TextColor="Green"
DropDownIconColor="Green"
WidthRequest="250"
ItemsSource="{Binding departments}"
SelectedItem="{Binding SelectedItem}"
/>
Note:
Then if we change the option of SfComboBox , the value of SelectedItem will also update automatically.
Related
I am trying to create a usercontrol to display groups of items from json file on windows iot core.
I have a "Create Group" button. Once pressed, it will create 64 usercontrols with respective details and display in a scrollviewer. susequently i can edit individual items in any of the 64 usercontrol then save the json file.
I have my class for the usercontrol as follow;
I have having an issue on how to create different groups of the 64 items and append all in a same json file and to subsequently display them from selection of the mentioned different groups.
Please help thanks.
Group Class
[DataContract]
public class DecoderGroup
{
[DataMember]
public int groupID{ get; set; }
[DataMember]
public string groupName{ get; set; }
[DataMember]
public int cardAddress { get; set; }
[DataMember]
public bool enabled { get; set; }
[DataMember]
public int z1label { get; set; }
[DataMember]
public int z2label { get; set; }
[DataMember]
public int z3label { get; set; }
[DataMember]
public int z4label { get; set; }
[DataMember]
public bool zone1 { get; set; }
[DataMember]
public bool zone2 { get; set; }
[DataMember]
public bool zone3 { get; set; }
[DataMember]
public bool zone4 { get; set; }
[DataMember]
public List<byte> txData { get; set; }
public DecoderGroup(int id, int address, int z1, int z2, int z3, int z4)
{
groupName = "Group";
zone1 = false;
zone2 = false;
zone3 = false;
zone4 = false;
z1label = z1;
z2label = z2;
z3label = z3;
z4label = z4;
}
}
MainPage.xaml.cs
private void AddGroup_Click(object sender, RoutedEventArgs e)
{
ZonesList_Panel.Children.Clear();
int groupid = 1;
int cardadr;
for (cardadr = 1; cardadr <= MAXCARDS; cardadr++)
{
var z4 = (4 * cardadr);
var z3 = (4 * cardadr) - 1;
var z2 = (4 * cardadr) - 2;
var z1 = (4 * cardadr) - 3;
DecoderGroupUserControl decoderGroupControl = new DecoderGroupUserControl(this, new DecoderGroup(groupid, cardadr, z1, z2, z3, z4));
ZonesList_Panel.Children.Add(decoderGroupControl);
}
}
private async void SaveGroup_Click(object sender, RoutedEventArgs e)
{
await saveGroupsToJSON(getGroups());
}
public async Task saveGroupsToJSON(List<DecoderGroup> groups)
{
var serializer = new DataContractJsonSerializer(typeof(List<DecoderGroup>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(DECODERGROUPS_FILE, CreationCollisionOption.OpenIfExists))
{
serializer.WriteObject(stream, groups);
}
}
public List<DecoderGroup> getGroups()
{
List<DecoderGroup> ret = new List<DecoderGroup>();
foreach (DecoderGroupUserControl u in ZonesList_Panel.Children)
{
//add condition for group ID
ret.Add(u.decoderGroup);
}
return ret;
}
UserControl
public DecoderGroupUserControl(MainPage page, DecoderGroup group)
{
this.InitializeComponent();
mainPage = page;
this.decoderGroup = group;
Z1Name.Text = group.z1label.ToString();
Z2Name.Text = group.z2label.ToString();
Z3Name.Text = group.z3label.ToString();
Z4Name.Text = group.z4label.ToString();
}
It is recommended to use UserControl as the DateTemplate of ListView so that you don’t need to create multiple usercontrol and add them to the page. Then you could read json file and convert json objects to a collection, and you could use the collection as the Itemsource of ListView.
By implementing the INotifyPropertyChanged interface, TwoWay data binding could reflect the UI changes to the collection. Finally, you could write the changed collection to the json file.
Note that you need to download the Newtonsoft.Json to parse the json object via Manage NuGet Packages. Please refer to the following code.
MyUserControl1.xaml:
<UserControl
..>
<Grid>
<!--customize the usercontrol style-->
<StackPanel>
<StackPanel Orientation="Horizontal" >
<TextBox Margin="0,0,20,0" Text="{Binding Name,Mode=TwoWay}" BorderThickness="0"/>
<TextBox Text="{Binding Job,Mode=TwoWay}" BorderThickness="0"/>
</StackPanel>
<TextBox Text="{Binding Age,Mode=TwoWay}" BorderThickness="0"/>
</StackPanel>
</Grid>
</UserControl>
MainPage.xaml:
<Page..>
<Grid>
<StackPanel>
<ListView ItemsSource="{x:Bind Results,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<local:MyUserControl1>
</local:MyUserControl1>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="SaveButton" Content="Save" Click="SaveButton_Click"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs:
namespace WriteJson
{
public sealed partial class MainPage : Page
{
public ObservableCollection<Person> Persons { get; set; }
public ObservableCollection<Person> Results { get; set; }
public string path;
public MainPage()
{
this.InitializeComponent();
CreateJsonFile();
path = ApplicationData.Current.LocalFolder.Path + "\\info.json";
Results = JsonConvert.DeserializeObject<ObservableCollection<Person>>(File.ReadAllText(path));
Debug.WriteLine("bind successfully");
}
public async void CreateJsonFile()
{
//check if info.json exists, if it doesn't exist, create it
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file;
try
{
file = await folder.GetFileAsync("info.json");
}
catch
{
await folder.CreateFileAsync("info.json");
Persons = new ObservableCollection<Person>()
{
new Person(){Name="tom",Job="teacher",Age=24},
new Person(){Name="lily",Job="nurse",Age=20},
new Person(){Name="ming",Job="student",Age=26},
new Person(){Name="kiki",Job="lawyer",Age=28},
new Person(){Name="jack",Job="worker",Age=21},
};
path = ApplicationData.Current.LocalFolder.Path + "\\info.json";
File.WriteAllText(path, JsonConvert.SerializeObject(Persons));
Debug.WriteLine("create a json file successfully");
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
File.WriteAllText(path, JsonConvert.SerializeObject(Results));
Debug.WriteLine("save successfully");
}
}
public class Person:INotifyPropertyChanged
{
private string name;
private string job;
private int age;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
public string Job
{
get { return job; }
set
{
job = value;
RaisePropertyChanged("Job");
}
}
public int Age
{
get { return age; }
set
{
age = value;
RaisePropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyname=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
}
Goal
I intend to update records from WPF application to MySQL.
The is the loaded data:
User is able to edit the data:
On button click, the data should update:
It detects the changes from the UI and shows updated records.
Problem
When looking in my database, I see the column Name has changed but PositionId remained the same:
Why is PositionId not updating? And how can I update it?
Code
Models
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
public override bool Equals(object obj)
{
return obj is Position p && PositionId == p.PositionId;
}
public override int GetHashCode() => PositionId.GetHashCode();
}
View Model
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
OnPropertyChanged();
}
}
private ObservableCollection<Position> _positions;
public ObservableCollection<Position> Positions
{
get { return _positions; }
set
{
_positions = value;
OnPropertyChanged();
}
}
private static string connString = "..Connection String Goes here";
private List<Person> LoadPersonData()
{
string query = "SELECT PersonId, Name, b.PositionId, b.PositionTitle FROM Person a JOIN Position b ON a.PositionId = b.PositionId";
using (MySqlConnection conn = new MySqlConnection(connString))
{
var details = conn.Query<Person, Position, Person>(query, (person, position) =>
{
person.Position = position;
return person;
}, splitOn: "PositionId").ToList();
return details;
}
}
private List<Position> LoadPositionsData()
{
string query = "SELECT PositionId, PositionTitle FROM Position";
using (MySqlConnection conn = new MySqlConnection(connString))
{
var details = conn.Query<Position>(query).ToList();
return details;
}
}
public MainViewModel()
{
People = new ObservableCollection<Person>();
Positions = new ObservableCollection<Position>();
// Add each record to property named People
LoadPersonData().ForEach(record => People.Add(record));
// Add each record to peroperty named Positions
LoadPositionsData().ForEach(record => Positions.Add(record));
Command = new RelayCommand(param => EditData());
}
public ICommand Command { get; }
private void EditData()
{
// Update records in MySQL using dapper
DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);
using(MySqlConnection conn = new MySqlConnection(connString))
{
conn.BulkUpdate(People, x => x.Position);
}
}
#region Prop Changed
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
View
<StackPanel>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedValue="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Save new data" Command="{Binding Command}" />
</StackPanel>
I think I've got it. What I noticed is in the documentation example:
This loop
So I changed my EditData method to the following code:
private void EditData()
{
foreach(var item in People)
{
item.PositionId = item.Position.PositionId;
}
// Update records in MySQL using dapper
DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);
using(MySqlConnection conn = new MySqlConnection(connString))
{
conn.BulkUpdate(People, x => x.Position);
}
}
and also added a Property in the Person model
public int PositionId { get; set; }
I've posted the same question before but it wasn't clear (and contained too many self-induced errors in attempt to fix the code) so re-posting it with more details.
So I have "MainUiWindow.xaml" file which uses databinding like this:
<ItemsControl x:Name="gridSettingsMonster" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding SettingsMonster}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type core:Setting}">
<Grid x:Name="gridMonster">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Label}" IsEnabled="{Binding Enabled}" ToolTip="{Binding Description}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10 5 10 5" FontWeight="{Binding Fontweight}" ></TextBlock>
<ts:ToggleSwitch x:Name="toggleSwitchMonsterAll" IsEnabled="{Binding Enabled}" Grid.Row="0" Grid.Column="1" Command ="{Binding TriggerAction}" IsChecked="{Binding Value}" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5 0 20 2" Foreground="White" UncheckedText="" CheckedText="" UncheckedBorderBrush="#FF333333" CheckedBorderBrush="#FF2D2D30"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
SettingsMonster binding:
SettingsMonster.Add(new Setting(ConfigHelper.Main.Values.Overlay.MonsterWidget.IsVisible, true, "Monster_1", "Monster Widget", "Show/Hide Monsters Widget", new Command(_ =>
{
ConfigHelper.Main.Values.Overlay.MonsterWidget.IsVisible = !ConfigHelper.Main.Values.Overlay.MonsterWidget.IsVisible;
ConfigHelper.Main.Save();
})));
SettingsMonster.Add(new Setting(ConfigHelper.Main.Values.Overlay.MonsterWidget.ShowUnchangedMonsters, ConfigHelper.Main.Values.Overlay.MonsterWidget.IsVisible, "Monster_2", " Show unchanged monsters", "Automatically hide monsters if they are not damaged", new Command(_ =>
{
ConfigHelper.Main.Values.Overlay.MonsterWidget.ShowUnchangedMonsters = !ConfigHelper.Main.Values.Overlay.MonsterWidget.ShowUnchangedMonsters;
ConfigHelper.Main.Save();
})));
And finally, the Setting class:
public class Setting
{
public bool Value { get; set; }
public bool Enabled { get; set; }
public string Name { get; }
public string Label { get; }
public string Description { get; }
public string Checkbox_visibility { get; }
public string Fontweight { get; }
public List<Setting>SubSettings { get; }
public Command TriggerAction { get; }
public Setting(bool value, bool enabled, string name, string label, string description, Command action = null)
{
Value = value;
Enabled = enabled;
Name = name;
Label = label;
Description = description;
SubSettings = new List<Setting>();
TriggerAction = action;
}
}
Problem:
When I run the build and use the "ToggleSwitch" (it's basically a open-source checkbox) to change the value of "ConfigHelper.Main.Values.Overlay.MonsterWidget.IsVisible", it unchecks the UI correctly.
I want this checkbox to control the other checkboxes (i.e. "Monster_2") as well, so that when the main one is turned off, set IsEnabled value for the child checkboxes/textblocks to FALSE.
I got to the stage where if I check off the main one, restart the build, then the child checkboxes/textblocks are all set as IsEnabled=False. However, I want the same to happen in real time (i.e. refresh the UI without having to restart).
Any help would be appreciated.
EDIT 1.
So I have attempted implementing the INotifyPropertyChanged in my Settings class which looks like the following:
public class Setting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _value;
private bool _enabled;
public bool Value
{
get
{
return _value;
}
set
{
if (_value == value)
return;
_value = value;
OnPropertyChanged(nameof(Enabled));
}
}
public bool Enabled
{
get
{
return _enabled;
}
set
{
if (_enabled == value)
return;
_enabled = value;
OnPropertyChanged(nameof(Enabled));
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public string Name { get; }
public string Label { get; }
public string Description { get; }
public string Checkbox_visibility { get; }
public string Fontweight { get; }
public List<Setting>SubSettings { get; }
public Command TriggerAction { get; }
public Setting(bool value, bool enabled, string name, string label, string description, Command action = null)
{
Value = value;
Enabled = enabled;
Name = name;
Label = label;
Description = description;
SubSettings = new List<Setting>();
TriggerAction = action;
}
But my UI is still not refreshing yet... any help?
My viewmodel was referencing incorrect variable in the first place.
I have implemented INotifyPropertyChange in my Setting object as below, and also added a command to be run when the checkbox is triggered.
public class Setting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private bool _value;
private bool _enabled;
public bool Value
{
get
{
return _value;
}
set
{
if (_value == value)
return;
_value = value;
OnPropertyChanged(nameof(Value));
}
}
public bool Enabled
{
get
{
return _enabled;
}
set
{
if (_enabled == value)
return;
_enabled = value;
OnPropertyChanged(nameof(Enabled));
}
}
public string Name { get; }
public string Label { get; }
public string Description { get; }
public string Fontweight { get; }
public List<Setting>SubSettings { get; }
public Command TriggerAction { get; }
public Setting(bool _value, bool _enabled, string name, string label, string description, Command action = null)
{
Value = _value;
Enabled = _enabled;
Name = name;
Label = label;
Description = description;
SubSettings = new List<Setting>();
TriggerAction = action;
}
}
Hello I am using MVVM in Xamarin Forms. I am trying to Bind my Listview's SeletedItem to my ViewModel. I binded it and worked fine. but when i have implemented INotifyPropertyChanged to update the view for some other component it stopped working.
I want it to work even INotifyPropertyChanged is implemented in my ViewModel. I am figuring out the problem why its happened. I searched on internet and Xamarin's documentation and could not find the reason.
My View
<ListView ItemsSource="{Binding PersonsList}"
CachingStrategy="RecycleElement"
HasUnevenRows="True"
SelectionMode="None"
SelectedItem="{Binding SelectedPerson}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5">
<Label Text="{Binding FullName}"
FontSize="Medium"
TextColor="Orange"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel()
{
PersonsList = new ObservableCollection<User>
{
new User(){ UserId = 1, FullName = "John" },
new User(){ UserId = 2, FullName = "Alex" },
new User(){ UserId = 3, FullName = "Ellen" },
new User(){ UserId = 4, FullName = "Grace" }
};
}
public ObservableCollection<User> PersonsList { get; set; }
private User _selectedPerson { get; set; }
public User SelectedPerson
{
get { return _selectedPerson; }
set
{
if (_selectedPerson != value)
{
_selectedPerson = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
public class User
{
public int UserId { get; set; }
public string FullName { get; set; }
}
Hope to get some useful advice.
Change your SelectedPerson property to call OnPropertyChanged().
private User _selectedPerson { get; set; }
public User SelectedPerson
{
get { return _selectedPerson; }
set
{
if (_selectedPerson != value)
{
_selectedPerson = value;
OnPropertyChanged();
}
}
}
I have a ComboBox and a DataGrid in my XAML:
<ComboBox SelectedItem="{Binding SelectedFamily}" ItemsSource="{Binding FamilyList}" IsSynchronizedWithCurrentItem="True"/>
<DataGrid ItemsSource="{Binding FamilyInfoGrid}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" />
</DataGrid.Columns>
</DataGrid>
This is my viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ReadOnlyObservableCollection<Family> _familyList;
public ReadOnlyObservableCollection<Family> FamilyList
{
get { return _familyList; }
}
private Family _selectedFamily;
public Family SelectedFamily
{
get
{
return _selectedFamily;
}
set
{
_familyInfoGrid.Clear();
_familyInfoGrid.Add(value.Kid);
_familyInfoGrid.Add(value.Parent2);
_familyInfoGrid.Add(value.Parent1);
RaisePropertyChangedEvent();
}
}
private ObservableCollection<Person> _familyInfoGrid = new ObservableCollection<Person>();
public ObservableCollection<Person> FamilyInfoGrid
{
get { return _familyInfoGrid; }
set { _familyInfoGrid = value; RaisePropertyChangedEvent(); }
}
public MainWindowViewModel()
{
var fam1 = new Family("Smith", new Person("Jim", 31), new Person("Eve", 29), new Person("Tom", 2));
var fam2 = new Family("Miller", new Person("Joe", 35), new Person("Sue", 33), new Person("Kim", 8));
_familyList = new ReadOnlyObservableCollection<Family>(new ObservableCollection<Family>() { fam1, fam2 } );
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
which means the DataGrid always gets updated to the currently selected item of the combobox - as it should.
This my model:
public class Family
{
public string Name { get; set; }
public Person Kid { get; set; }
public Person Parent1 { get; set; }
public Person Parent2 { get; set; }
public Family(string name, Person parent1, Person parent2, Person kid)
{
Name = name; Parent1 = parent1; Parent2 = parent2; Kid = kid;
}
public override string ToString()
{
return Name;
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name; Age = age;
}
}
The problem is that changes that are made within the DataGrid are stored in the _familyList, but I want the _familyList to be immutable and only be able to edit (and use) the current fields of the DataGrid temporarily (so Mode=OneWay is not an option either).
Thanks to #mm8 I managed to find a solution. The currently selected item of the combobox has to be a "deep copied":
public Family SelectedFamily
{
set
{
var fam = new Family(
new string(value.Name.ToCharArray()),
new Person(value.Parent1.Name, value.Parent1.Age),
new Person(value.Parent2.Name, value.Parent2.Age),
new Person(value.Kid.Name, value.Kid.Age));
_familyInfoGrid.Clear();
_familyInfoGrid.Add(fam.Kid);
_familyInfoGrid.Add(fam.Parent2);
_familyInfoGrid.Add(fam.Parent1);
RaisePropertyChangedEvent();
}
}