I imagine that this is going to be flagged as a dduplicate but I am kind of stuck and not sure how to interpret other answers, or really know what I am doing wrong. Essentially my end problem is this:
I have a class that contains a field (a number) that can be changed. This class will be in an observable list that should contain a total of all the numbers from all elements in the observable collection. The total number should automatically change when the field of any member of the list is changed by the user. It is actually very similar to the problem posed here: Updating calculated total when a property on the underlying item in a list is changed - wpf. However, when I followed that solution and created a textbox that was bound to the total, the total was never changed. It seems also that the OnPropertyChanged event for the collection is never called to indicate that a property was changed in the underlying list. I think the problem is related to the fact that total_dose is never actually set but just has a get (from the link I provided). I am not sure how to implement the solutions I see on other posts. Would appreciate some guidance
CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Specialized;
// https://stackoverflow.com/questions/51331010/updating-calculated-total-when-a-property-on-the-underlying-item-in-a-list-is-ch
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<string> sub_list { get; set; }
public MainWindow()
{
InitializeComponent();
structure temp_struct = new structure("Bladder");
structure_info_list.ItemsSource = temp_struct.fx_list;
this.DataContext = temp_struct;
}
}
public class structure : INotifyPropertyChanged
{
public structure(string name)
{
this.name = name;
fx_list = new ObservableCollection<fraction>();
fraction fx1 = new fraction(3);
fraction fx2 = new fraction(4);
fraction fx3 = new fraction(5);
fx_list.Add(fx1);
fx_list.Add(fx2);
fx_list.Add(fx3);
MessageBox.Show("Total: " + total_dose);
fx_list[0].fx_dose = 50;
MessageBox.Show("Total: " + total_dose);
}
private void fractions_changed_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
((fraction)item).PropertyChanged += fx_Changed;
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
((fraction)item).PropertyChanged -= fx_Changed;
}
}
}
void fx_Changed(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(fraction.fx_dose))
{
OnPropertyChanged(nameof(fx_list));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
MessageBox.Show("A property in the underlying list was changed: " + propertyName);
}
/*
public void calc_total_sum()
{
int total_sum_temp = 0;
foreach (fraction fx in fx_list)
{
total_sum_temp += fx.fx_dose;
}
total_sum = total_sum_temp;
}
*/
public string name { get; set; }
public ObservableCollection<fraction> fx_list { get; set; }
//public int total_sum { get; set; }
public int total_dose
{
get { return fx_list.Sum(x => x.fx_dose); }
set { }
}
}
public class fraction : INotifyPropertyChanged
{
private int _fx_dose;
public int fx_dose
{
get { return _fx_dose; }
set
{
_fx_dose = value;
this.calc_eq();
this.OnPropertyChanged("fx_dose");
//MessageBox.Show("FX DOSE PROP");
}
}
private int _eq;
public int eq
{
get { return _eq; }
set { _eq = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
public fraction(int fx_dose)
{
this.fx_dose = fx_dose;
this.eq = fx_dose*2;
}
public void calc_eq()
{
this.eq = this.fx_dose * 2;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<ItemsControl Name="structure_info_list">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Fraction "/>
<TextBox Text="{Binding Path=fx_dose, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Total: "/>
<TextBlock Text="{Binding Path=total_dose, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</StackPanel>
</Window>
Edit: I found the solution by looking at section 3 of this link
https://www.codeproject.com/Articles/28098/A-WPF-Pie-Chart-with-Data-Binding-Support#three
There are a couple if items.
First, you are setting the datacontext and the itemsource after you populate the list - this causes the notifications to not reach your binding. it is better to set these up in your XAML file.
Next, your property total_does does not call OnPropertyChanged("total_does").
Also, the datacontext is connected to the observable collection within structure. total_does is outside of the observable collection so your window will not see total_does or its notifications.
I made adjustmnts to show what I mean:
CS file:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ObservableCollection<string> sub_list { get; set; }
private structure temp_struct;
public MainWindow()
{
InitializeComponent();
temp_struct = new structure("Bladder");
}
public structure Temp_Struct
{
get => temp_struct;
set
{
temp_struct = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string memberName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
}
}
public class structure : INotifyPropertyChanged
{
private ObservableCollection<fraction> fxlist = new ObservableCollection<fraction>();
public structure(string name)
{
this.name = name;
fx_list = new ObservableCollection<fraction>();
fraction fx1 = new fraction(3);
fraction fx2 = new fraction(4);
fraction fx3 = new fraction(5);
fx_list.Add(fx1);
fx_list.Add(fx2);
fx_list.Add(fx3);
MessageBox.Show("Total: " + total_dose);
OnPropertyChanged("total_dose");
fx_list[0].fx_dose = 50;
MessageBox.Show("Total: " + total_dose);
OnPropertyChanged("total_dose");
}
private void fractions_changed_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
((fraction)item).PropertyChanged += fx_Changed;
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
((fraction)item).PropertyChanged -= fx_Changed;
}
}
}
void fx_Changed(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(fraction.fx_dose))
{
OnPropertyChanged(nameof(fx_list));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new propertyChangedEventArgs(propertyName));
MessageBox.Show("A property in the underlying list was changed: " + propertyName);
}
/*
public void calc_total_sum()
{
int total_sum_temp = 0;
foreach (fraction fx in fx_list)
{
total_sum_temp += fx.fx_dose;
}
total_sum = total_sum_temp;
}
*/
public string name { get; set; }
public ObservableCollection<fraction> fx_list
{
get => fxlist;
set
{
fxlist = value;
OnPropertyChanged();
}
}
//public int total_sum { get; set; }
public int total_dose
{
get { return fx_list.Sum(x => x.fx_dose); }
}
}
public class fraction : INotifyPropertyChanged
{
private int _fx_dose;
public int fx_dose
{
get { return _fx_dose; }
set
{
_fx_dose = value;
this.calc_eq();
this.OnPropertyChanged("fx_dose");
//MessageBox.Show("FX DOSE PROP");
}
}
private int _eq;
public int eq
{
get { return _eq; }
set { _eq = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
public fraction(int fx_dose)
{
this.fx_dose = fx_dose;
this.eq = fx_dose * 2;
}
public void calc_eq()
{
this.eq = this.fx_dose * 2;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML file:
<Window
x:Name="WinMain"
x:Class="DeluxMeasureStudies.Windows.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:DeluxMeasureStudies.Windows"
Title="MainWindow"
Width="800"
Height="450"
Foreground="White"
DataContext="{Binding Temp_Struct, ElementName=WinMain}"
Background="{StaticResource Normal.Window.Background}"
>
<StackPanel
Orientation="Vertical"
>
<ItemsControl Name="structure_info_list"
DataContext="{Binding Path=Temp_Struct, ElementName=WinMain}"
ItemsSource="{Binding Path=fx_list}">
<ItemsControl.ItemTemplate>
<DataTemplate
DataType="local:fraction"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Fraction "/>
<TextBox Text="{Binding Path=fx_dose, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Total: "/>
<TextBlock Text="{Binding Path=total_dose, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</StackPanel>
Related
How can I search for a row by a column and later select that row in a XamDataGrid.
I've tried iterating over the DataSource, but the type of the elements isn't very helpful, it has only a HasData bool property exposed.
Try to use XamDataGrid.FieldLayouts.DataPresenter.Records collection and check for the required cell. When the record is found it can be selected by setting record.IsSelected = true;
Something like that:
using System;
using System.Windows;
using System.Windows.Media;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Infragistics.Windows.DataPresenter;
namespace IGFindRow
{
public partial class MainWindow : Window
{
public MainWindow()
{
_cars = Cars;
InitializeComponent();
}
#region Code fragment from samples provided by Infragistics
public ObservableCollection<Car> _cars = null;
public ObservableCollection<Car> Cars
{
get
{
if (this._cars == null)
{
this._cars = new ObservableCollection<Car>();
this._cars.Add(new Car("Dodge", "Ram", Colors.Blue, 22050.00, 153));
this._cars.Add(new Car("Ford", "Explorer", Colors.Green, 27175.00, 96));
this._cars.Add(new Car("BMW", "Z4", Colors.Silver, 35600.00, 42));
this._cars.Add(new Car("Toyota", "Camry", Colors.Black, 20790.99, 131));
}
return _cars;
}
}
public class Car : INotifyPropertyChanged
{
string m_make;
string m_model;
Color m_color;
double m_baseprice;
int m_milage;
public Car(string make, string model, Color color, double baseprice, int milage)
{
this.Make = make;
this.Model = model;
this.Color = color;
this.BasePrice = baseprice;
this.Milage = milage;
}
public string Make
{
get { return m_make; }
set
{
if (m_make != value)
{
m_make = value;
NotifyPropertyChanged("Make");
}
}
}
public string Model
{
get { return m_model; }
set
{
if (m_model != value)
{
m_model = value;
NotifyPropertyChanged("Model");
}
}
}
public Color Color
{
get { return m_color; }
set
{
if (m_color != value)
{
m_color = value;
NotifyPropertyChanged("Color");
}
}
}
public double BasePrice
{
get { return m_baseprice; }
set
{
if (m_baseprice != value)
{
m_baseprice = value;
NotifyPropertyChanged("BasePrice");
}
}
}
public int Milage
{
get { return m_milage; }
set
{
if (m_milage != value)
{
m_milage = value;
NotifyPropertyChanged("Milage");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
#endregion
private void Search_Click(object sender, RoutedEventArgs e)
{
// Enumerate records
foreach (var it in dataGrid.FieldLayouts.DataPresenter.Records)
{
if (it is DataRecord record)
{
// Check the current column value
if (record.Cells["Make"].Value.ToString().ToUpper() == Maker.Text.ToUpper())
{
record.IsSelected = true;
break;
}
}
}
}
}
}
The XAML:
<Window x:Class="IGFindRow.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:ig="http://infragistics.com/DataPresenter"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Name="dgTest">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="5,5,5,5">
<Label Name="ColumnName" Padding="5,5,0,5">Maker:</Label>
<TextBox Name="Maker" Padding="5,5,5,5" Margin="3,0,20,0">Ford</TextBox>
<Button Name="Search" Padding="5,5,5,5" Click="Search_Click">Press to search</Button>
</StackPanel>
<ig:XamDataGrid x:Name="dataGrid" Grid.Row="1"
IsGroupByAreaExpanded="False"
GroupByAreaLocation="None"
Theme="Generic"
DataSource="{Binding ElementName=dgTest, Path=Cars}">
<ig:XamDataGrid.FieldLayoutSettings>
<ig:FieldLayoutSettings SelectionTypeRecord="Single" />
</ig:XamDataGrid.FieldLayoutSettings>
<ig:XamDataGrid.ViewSettings>
<ig:GridViewSettings/>
</ig:XamDataGrid.ViewSettings>
<ig:XamDataGrid.FieldSettings>
<ig:FieldSettings CellClickAction="SelectRecord" AllowRecordFiltering="True"/>
</ig:XamDataGrid.FieldSettings>
</ig:XamDataGrid>
</Grid>
</Window>
Help me please!!!
I have 3 UserControls
I select user on List Users UC listbox
Then send message from SendMessage UC to Database
when i send message to Db it must refresh my chat listBox in Correspondence UC, but problem is in my ChatWrapper.
PropertyChanged in ChatWrapper is always null, and I can't refresh my ListBox in Correspondence UC with new message
List Users:
public IEnumerable<EmployeesDb> getListNames
{
get { return Db.Instance.EmployeesDbs.ToList(); }
}
static EmployeesDb m_selectedUser;
public static EmployeesDb selectedUser
{
get { return m_selectedUser; }
set
{
if (value != null)
m_selectedUser = value;
Correspondence correspondence = new Correspondence();
correspondence.CorrespondenceChat();
}
}
}
Send Message ( I try to refresh -> SendInfo.FirstOrDefault().RefreshGUI();)
public static DependencyProperty SendInfoProperty =
DependencyProperty.Register(
"SendInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(SendMessage));
public IEnumerable<ChatWrapper> SendInfo
{
get { return GetValue(SendInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(SendInfoProperty, value); }
}
void SendMessageCommandExecute()
{
//...
SendInfo.FirstOrDefault().RefreshGUI();
//...
}
ChatWrapper
public class ChatWrapper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void FirePropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
public void RefreshGUI()
{
FirePropertyChanged("message");
}
public ChatDb chatDb { get; set; }
public string message
{
get
{
return (chatDb != null) ? string.Format("{0} {1}.{2} / {3} / {4}\n{5}",
chatDb.FromEmployeesDb.surname,
chatDb.FromEmployeesDb.name[0],
chatDb.FromEmployeesDb.middleName[0],
chatDb.messageDateTime,
chatDb.computerName,
chatDb.message) : null;
}
}
Correspondence
//...
public partial class Correspondence : UserControl, INotifyPropertyChanged
{
public static DependencyProperty GetCorrespondenceInfoProperty =
DependencyProperty.Register(
"GetCorrespondenceInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(Correspondence),
new PropertyMetadata(OnChanged));
public IEnumerable<ChatWrapper> GetCorrespondenceInfo
{
get { return GetValue(GetCorrespondenceInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(GetCorrespondenceInfoProperty, value); }
}
static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var me = d as Correspondence;
me.chat = me.GetCorrespondenceInfo;
}
ICollectionView m_CollectionView;
public static IEnumerable<ChatWrapper> m_chat;
public IEnumerable<ChatWrapper> chat
{
get { return m_chat; }
set
{
m_chat = value;
if (ListUsers.selectedUser != null)
CorrespondenceChat();
FirePropertyChanged("chat");
}
}
public void CorrespondenceChat()
{
if (m_chat == null)
return;
m_CollectionView = CollectionViewSource.GetDefaultView(m_chat);
//...
FirePropertyChanged("chat");
}
XAML of Correspondence (refresh
<Grid>
<ListBox x:Name="correspondenceListBox" ItemsSource="{Binding chat, RelativeSource={RelativeSource AncestorType={x:Type local:Correspondence}}}"
Height="auto" Grid.Row="0" Grid.Column="1" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding message}" TextWrapping="Wrap" Width="auto"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I tried to write
public event PropertyChangedEventHandler PropertyChanged = delegate { };
PropertyChanged is no longer null, but it's still not updated
I have a textbox inside of a listbox that I would like to update the ObservableCollection when the textbox loses focus. I tried using my collections CollectionChanged event as described in this post here to try to solve the problem. Right now the only way to update the collection is if I add or remove an item from the listbox. Am I going about this the wrong way? What am I missing for the textbox to update the collection?
MainWindow.xaml
<ListBox ItemsSource="{Binding DataLogList,Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding DataLogLabel}" Margin="5"/>
<TextBox Text="{Binding DataLogName,Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="5" Width="150"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainViewModel.cs
public MainViewModel()
{
DataLogList = new ObservableCollection<DataLogContent>();
DataLogList.CollectionChanged += (s, e) =>
{
if (e.NewItems != null)
{
foreach (DataLogContent item in e.NewItems)
{
item.PropertyChanged += item_PropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (DataLogContent item in e.OldItems)
{
item.PropertyChanged -= item_PropertyChanged;
}
}
};
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChanged();
}
DataLogContent.cs
public class DataLogContent:ViewModelBase
{
private string dataLogLabel;
public string DataLogLabel
{
get { return this.dataLogLabel; }
set
{
this.dataLogLabel = value;
NotifyPropertyChanged();
}
}
private string dataLogName;
public string DataLogName
{
get { return this.dataLogName; }
set
{
this.dataLogLabel = value;
NotifyPropertyChanged();
}
}
}
I have it working based on this. My guess is that you may be over complicating the adding/removing logic of an item inside the ObservableCollection. There's no need to monitor the property changed event, as each object will already raise the event whenever a property within it changes.
Here's what I have:
namespace WpfApplication1
{
public class MainViewModel : ViewModelBase
{
public ObservableCollection<DataLogContent> DataLogList { get; private set; }
public MainViewModel()
{
DataLogList = new ObservableCollection<DataLogContent>();
DataLogList.Add(new DataLogContent
{
DataLogLabel = "Label",
DataLogName = "Name"
});
DataLogList.Add(new DataLogContent
{
DataLogLabel = "Label2",
DataLogName = "Name2"
});
}
}
public class DataLogContent : ViewModelBase
{
private string dataLogLabel;
public string DataLogLabel
{
get { return this.dataLogLabel; }
set
{
this.dataLogLabel = value;
OnPropertyChanged("DataLogLabel");
}
}
private string dataLogName;
public string DataLogName
{
get { return this.dataLogName; }
set
{
this.dataLogName = value;
OnPropertyChanged("DataLogName");
}
}
}
}
Simple ViewModelBase:
namespace WpfApplication1
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
}
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">
<ListBox ItemsSource="{Binding DataLogList,Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding DataLogLabel}" Margin="5"/>
<TextBox Text="{Binding DataLogName,Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="5" Width="150"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
I have recently discovered indexed properties. This looks like the perfect solution to the scenario in which the data I am working with would best be expressed in a collection, yet still needs to be implemented as a property that can be used in XAML databinding. I started with just a test of creating indexed properties, and I had no problems there, but I just don't seem to be able to get the binding to work.
Can anyone point out where I'm going wrong?
Here is the test class with a nested class to create the indexed property:
public class TestListProperty
{
public readonly ListProperty ListData;
//---------------------------
public class ListProperty : INotifyPropertyChanged
{
private List<string> m_data;
internal ListProperty()
{
m_data = new List<string>();
}
public string this[int index]
{
get
{
if ( m_data.Count > index )
{
return m_data[index];
}
else
{
return "Element not set for " + index.ToString();
}
}
set
{
if ( m_data.Count > index )
{
m_data[index] = value;
}
else
{
m_data.Insert( index, value );
}
OnPropertyChanged( "Item[]" );
Console.WriteLine( value );
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged( string propertyName )
{
PropertyChangedEventHandler handler = PropertyChanged;
if ( handler != null ) handler( this, new PropertyChangedEventArgs( propertyName ) );
}
}
//---------------------------
public TestListProperty()
{
ListData = new ListProperty();
}
}
Here is the XAML:
<Window x:Class="TestIndexedProperties.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>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Width="200" Grid.Row="0" Text="{Binding Path=ListData[0], Mode=TwoWay}" />
<TextBox Width="200" Grid.Row="1" Text="{Binding Path=ListData[1], Mode=TwoWay}" />
<TextBox Width="200" Grid.Row="2" Text="{Binding Path=ListData[2], Mode=TwoWay}" />
</Grid>
</Window>
And here is the Window code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestListProperty test = new TestListProperty();
this.DataContext = test;
test.ListData[0] = "ABC";
test.ListData[1] = "Pleeez 2 wurk?";
test.ListData[2] = "Oh well";
}
}
Thanks for any and all help!
There's no mechanism in your code to indicate to the front-end when the list has changed, i.e. ListProperty is implementing INotifyPropertyChanged instead of INotifyCollectionChanged. The easiest fix would be to change m_data to type ObservableCollection and bind your XAML controls to that instead, although that probably defeats the purpose of what you're trying to do in the first place. The "proper" way is to subscribe to the CollectionChanged event and propagate the messages through:
public class TestListProperty
{
public ListProperty ListData { get; private set; }
//---------------------------
public class ListProperty : INotifyCollectionChanged
{
private ObservableCollection<string> m_data;
internal ListProperty()
{
m_data = new ObservableCollection<string>();
m_data.CollectionChanged += (s, e) =>
{
if (CollectionChanged != null)
CollectionChanged(s, e);
};
}
public string this[int index]
{
get
{
if (m_data.Count > index)
{
return m_data[index];
}
else
{
return "Element not set for " + index.ToString();
}
}
set
{
if (m_data.Count > index)
{
m_data[index] = value;
}
else
{
m_data.Insert(index, value);
}
Console.WriteLine(value);
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
//---------------------------
public TestListProperty()
{
ListData = new ListProperty();
}
}
I have two listbox define below:
<ListBox x:Name="RemoteListBox" HorizontalAlignment="Right" Margin="0,88.5,8,0"
Width="382.5"
HorizontalContentAlignment="Stretch"
ItemsSource ="{Binding RemoteItemsList}"
SelectedIndex="0">
</ListBox>
<ListBox x:Name="LibraryListBox"
Margin="4.5,88.5,437,0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding LibraryItemsList}"
SelectedIndex="0">
</ListBox>
My viewmodel
private ObservableCollection<MotionTitleItem> _remoteItemsList;
public ObservableCollection<MotionTitleItem> RemoteItemsList
{
get { return _remoteItemsList; }
set
{
_remoteItemsList = value;
NotifyPropertyChanged("RemoteItemsList");
}
}
private ObservableCollection<MotionTitleItem> _libraryItemsList
public ObservableCollection<MotionTitleItem> LibraryItemsList
{
get { return _libraryItemsList; }
set
{
_libraryItemsList = value;
NotifyPropertyChanged("LibraryItemsList");
}
}
I bind two ListBox ItemSource with a ObserverableCollection define below:
var listMotion = new ObservableCollection<MotionTitleItem>();
foreach (MotionInfo info in listMotionInfo)
{
var motionTitleItem = new MotionTitleItem();
listMotion.Add(motionTitleItem);
}
viewModel.RemoteItemsList = listMotion;
viewModel.LibraryItemsList = listMotion;
MotionTitleItem is a custom user control.
My problem is only the first ListBox with ItemSource binding with RemoteListItem displays the Item in UI, the other doest not.
If I bind two ListBox ItemSource with 2 ObserverableCollection, the problem solved:
var listMotion = new ObservableCollection<MotionTitleItem>();
var listMotion2 = new ObservableCollection<MotionTitleItem>();
foreach (MotionInfo info in listMotionInfo)
{
var motionTitleItem = new MotionTitleItem();
listMotion.Add(motionTitleItem);
var motionTitleItem2 = new MotionTitleItem();
listMotion2.Add(motionTitleItem2);
}
viewModel.RemoteItemsList = listMotion;
viewModel.LibraryItemsList = listMotion2;
Could someone explain to me where is the point of the first scenario problem?
I don't know why you used two temporary list for this. You can directly add items to your Observable collection. Try this :
foreach (MotionInfo info in listMotionInfo)
{
viewModel.RemoteItemsList.Add(info);
viewModel.LibraryItemsList.Add(info);
}
Below, I tried to create the solution for you.
I assumed the model MotionTitleItem.
public class MotionTitleItem
{
string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
try
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (null == eventHandler)
return;
else
{
var e = new PropertyChangedEventArgs(propertyName);
eventHandler(this, e);
}
}
catch (Exception)
{
throw;
}
}
}
My view model for this application is:
public class MotionTitleItemViewModel : INotifyPropertyChanged
{
ObservableCollection<MotionTitleItem> _remoteItemsList = new ObservableCollection<MotionTitleItem>();
public ObservableCollection<MotionTitleItem> RemoteItemsList
{
get { return _remoteItemsList; }
set { _remoteItemsList = value; }
}
ObservableCollection<MotionTitleItem> _libraryItemsList = new ObservableCollection<MotionTitleItem>();
public ObservableCollection<MotionTitleItem> LibraryItemsList
{
get { return _libraryItemsList; }
set { _libraryItemsList = value; }
}
public MotionTitleItemViewModel()
{
MotionTitleItem motion;
for (int i = 0; i < 10; i++)
{
motion = new MotionTitleItem();
motion.Name = "Name " + i.ToString();
this.LibraryItemsList.Add(motion);
this.RemoteItemsList.Add(motion);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
try
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (null == eventHandler)
return;
else
{
var e = new PropertyChangedEventArgs(propertyName);
eventHandler(this, e);
}
}
catch (Exception)
{
throw;
}
} }
My View is :
<Window x:Class="WPFExperiments.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>
<ListBox x:Name="RemoteListBox" HorizontalAlignment="Right" Margin="0,0.5,8,0"
Width="382.5"
HorizontalContentAlignment="Stretch"
ItemsSource ="{Binding RemoteItemsList}"
SelectedIndex="0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="LibraryListBox"
Margin="4.5,0.5,437,0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding LibraryItemsList}"
SelectedIndex="0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
In code behind of this window i set DataContext to view model.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MotionTitleItemViewModel();
}}
This code is working for me.
Here is screenshot of the output.
Vote this answer if you find it useful.
Have fun!