I have the following XAML for a list of data items:
<phone:LongListSelector x:Name="Port_SummaryList" ItemsSource="{Binding PortList}" ItemTemplate="{StaticResource PortfolioDataTemplate}"/>
The template is defined as this:
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="PortfolioDataTemplate">
<Grid d:DesignHeight="91.5" d:DesignWidth="439.875" Height="82">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="31*"/>
<ColumnDefinition Width="19*"/>
<ColumnDefinition Width="19*"/>
<ColumnDefinition Width="19*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15*"/>
<RowDefinition Height="15*"/>
<RowDefinition Height="15*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="PortfolioName" HorizontalAlignment="Left" Height="92" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top" Width="155" Grid.RowSpan="2" Margin="0,0,0,-10"/>
<TextBlock x:Name="NAV" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Height="31" TextWrapping="Wrap" Text="{Binding NAV, StringFormat='{}{0:C}'}" VerticalAlignment="Top" Width="95" Margin="0,-1,0,0"/>
<TextBlock x:Name="CostBasis" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" Height="30" TextWrapping="Wrap" Text="{Binding Cost,StringFormat='{}{0:C}'}" VerticalAlignment="Top" Width="95" />
</Grid>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
and in my ViewModel I have this:
private TrulyObservableCollection<PortfolioModel> _PortList;
public TrulyObservableCollection<PortfolioModel> PortList
{
get { return _PortList; }
set
{
_PortList = value;
_PortList.CollectionChanged += _PortList_CollectionChanged;
RaisePropertyChanged("PortList");
}
}
void _PortList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("PortList");
}
The class "TrulyObservableCollection<>" is from this SO post.
The class "PortfolioModel" is defined as this:
public class PortfolioModel : INotifyPropertyChanged
{
public string Name { get; set; }
public DateTime Created { get; set; }
public int Id { get; set; }
public TrulyObservableCollection<CashModel> Cashflow;
public TrulyObservableCollection<HoldingModel> Positions;
public float Cost
{
get
{
float total_cost = 0.0f;
foreach (HoldingModel holding in Positions)
{
total_cost += holding.Share * holding.CostBasis;
}
return total_cost;
}
private set { ;}
}
//Numbers that are calculated with other variables, listed here for databinding purposes
public float NAV
{
get
{
float acc = 0.0f;
foreach (HoldingModel hm in Positions)
{
acc += hm.CurrentPrice * hm.Share;
}
foreach (CashModel cm in Cashflow)
{
acc += cm.Amount;
}
return acc;
}
set { ;}
}
public float DailyPercent { get; set; }
public float OverallPercent { get; set; }
public PortfolioModel()
{
Cashflow = new TrulyObservableCollection<CashModel>();
Cashflow.CollectionChanged += Cashflow_CollectionChanged;
Positions = new TrulyObservableCollection<HoldingModel>();
Positions.CollectionChanged += Positions_CollectionChanged;
}
void Positions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Positions");
NotifyPropertyChanged("NAV");
}
void Cashflow_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Cashflow");
NotifyPropertyChanged("NAV");
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The class "HoldingModel" is defined as this:
public class HoldingModel : INotifyPropertyChanged
{
private string _Ticker;
public string Ticker
{
get { return _Ticker; }
set { if (value != _Ticker) { _Ticker = value; NotifyPropertyChanged("Ticker"); } }
}
private string _CompanyName;
public string CompanyName
{
get { return _CompanyName; }
set { if (value != _CompanyName) { _CompanyName = value; NotifyPropertyChanged("CompanyName"); } }
}
private int _Share;
public int Share
{
get { return _Share; }
set { if (value != _Share) { _Share = value; NotifyPropertyChanged("Share"); } }
} //negative means short
public string LongShort
{
get { if (Share > 0) return "LONG"; else return "SHORT"; }
}
private float _Value;
public float Value
{
get { return _Value; }
set { if (value != _Value) { _Value = value; NotifyPropertyChanged("Value"); } }
}
public float Cost
{
get { return Share * CostBasis; }
set { ;}
}
private float _CostBasis;
public float CostBasis
{
get { return _CostBasis; }
set { if (value != _CostBasis) { _CostBasis = value; NotifyPropertyChanged("CostBasis"); } }
}
private float _RealizedGain;
public float RealizedGain
{
get { return _RealizedGain; }
set { _RealizedGain = value; NotifyPropertyChanged("RealizedGain"); }
}
private float _CurrentPrice;
public float CurrentPrice
{
get { return _CurrentPrice; }
set
{
_CurrentPrice = value;
NotifyPropertyChanged("CurrentPrice");
}
}
private float _CurrentChange;
public float CurrentChange
{
get { return _CurrentChange; }
set { _CurrentChange = value; NotifyPropertyChanged("CurrentChange"); }
}
ObservableCollection<TradeModel> Trades;
public HoldingModel()
{
Trades = new ObservableCollection<TradeModel>();
}
public void AddTrade(TradeModel trade)
{
//Order can't change, since RealizedGain relies on CostBasis and Share, CostBasis relies on Share
UpdateRealizedGain(trade);
UpdateCostBasis(trade);
Share += trade.Share;
trade.PropertyChanged += PropertyChanged;
Trades.Add(trade);
}
private void UpdateCostBasis(TradeModel trade)
{
if (trade.Share + Share == 0)
{
CostBasis = 0;
return;
}
float cost = CostBasis * Share;
cost += trade.Price * trade.Share;
CostBasis = cost / (Share + trade.Share);
}
private void UpdateRealizedGain(TradeModel trade)
{
if (trade.Share * Share > 0) return; //No realized gain on add-on
if (Math.Abs(trade.Share) > Math.Abs(Share))
{
RealizedGain += Share * (trade.Price - CostBasis);
}
else
{
RealizedGain += trade.Share * (trade.Price - CostBasis);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
//Debug.WriteLine("symbol_user got property {0} changed, bubbling up", propertyName);
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What I wanted to do is that, every time I update the CurrentPrice property in HoldingModel, I want to see the NAV property in PortfolioModel change, and reflect that in the view. I tried all I can but still unable to achieve that. Is there anything that I'm missing? Any help is appreciated
I've also noticed some problems with LongListSelector and ObservableCollection. I've posted it here:
Long List Selector Observable Collection and Visual Tree - problems?
Please check in your project something like this: leave the Page with back button and the reenter the page with LLS - if it's correctly displayed that mean we have the same problems, and I think it's the problem with LLS and we have to wait for WP 8.1. I assume that LLS is not correctly Updated (VisualTree doesn't change), because when I use normal ListBox everything works perfect.
Try to use ListBox (as you don't have grouping):
<ListBox x:Name="Port_SummaryList" ItemsSource="{Binding PortList}" ItemTemplate="{StaticResource PortfolioDataTemplate}"/>
If you don't see changes you can try call (in my project that function didn't worj with LLS but with LisBox works fine):
Port_SummaryList.UpdateLayout();
Try explicitly specifying Mode=OneWay in the NAV binding.
Text="{Binding NAV, Mode=OneWay, StringFormat='{}{0:C}'}"
I just had a case where the Mode behaved like it was defaulting to the Mode=OneTime. After explicitly setting Mode=OneWay, my data changes started to display. The BindingMode Enumeration documentation here suggests Mode=OneWay is implied. Recent experience suggests that may not always be the case.
Related
My problem, developing a shopping system on a Raspberry Pi 3 Bodel B, is as following:
First of all some context:
Raspberry Pi with Barcode Scanner is used as self-service shop
Employees scans a drink and the price on the screen is added up the price of the drink
Now a extension should be provided: A list of all currently scanned products should be shown.
Until now I only achieved, that every scanned product appears as one ListViewItem in the ListView
This is my Model
public partial class Product : object, System.ComponentModel.INotifyPropertyChanged {
private string DescriptionField;
private System.DateTime ExpirationDateField;
private int IDField;
private string NameField;
private decimal PriceField;
[System.Runtime.Serialization.DataMemberAttribute()]
public string Description {
get {
return this.DescriptionField;
}
set {
if ((object.ReferenceEquals(this.DescriptionField, value) != true)) {
this.DescriptionField = value;
this.RaisePropertyChanged("Description");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public System.DateTime ExpirationDate {
get {
return this.ExpirationDateField;
}
set {
if ((this.ExpirationDateField.Equals(value) != true)) {
this.ExpirationDateField = value;
this.RaisePropertyChanged("ExpirationDate");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public int ID {
get {
return this.IDField;
}
set {
if ((this.IDField.Equals(value) != true)) {
this.IDField = value;
this.RaisePropertyChanged("ID");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Name {
get {
return this.NameField;
}
set {
if ((object.ReferenceEquals(this.NameField, value) != true)) {
this.NameField = value;
this.RaisePropertyChanged("Name");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public decimal Price {
get {
return this.PriceField;
}
set {
if ((this.PriceField.Equals(value) != true)) {
this.PriceField = value;
this.RaisePropertyChanged("Price");
}
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
This model describes a product which can be bought in our shop.
My XAML Snippet
<ListView Name="ProductSumUp"
AllowDrop="False"
SelectionMode="None"
CanDrag="False"
CanReorderItems="False"
CanDragItems="False"
Grid.Column="2"
HorizontalAlignment="Left"
Height="290"
Margin="10,10,0,0"
Grid.Row="1"
VerticalAlignment="Top"
Width="180"
RenderTransformOrigin="1.682,0.59"
Foreground="White"
FontFamily="Assets/Fonts/Baskerville.ttf#Baskerville">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock FontFamily="Assets/Fonts/Baskerville.ttf#Baskerville"
Foreground="White">
<Run Text="{Binding Name}"/>
<Run x:Name="{Binding Price}"/>
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now my question is:
Is it possible, if a product was scanned twice, not to add a new ListViewItem, instead, the existing ListViewItem should be manipulated by adding the same Price on the existing Item.
I tried many possibilities and also asked some developers at my work but nobody could figure out.
If you need more information just ask.
Thanks in advance and excuse me for my horrible English grammar :)
So, I worked out a solution by myself with nice support from #JustinXL and #user230910.
I'll just post my code here, so you can see that I came to a solution.
XAML
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
FontFamily="Assets/Fonts/Baskerville.ttf#Baskerville"
Foreground="White">
<Run Text="{Binding Path=groupedProduct.Name}"/>
<Run Text="{Binding PriceSum,
Converter={StaticResource PriceConverter},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<LineBreak/>
<Run Text="{Binding Path=Count,
Converter={StaticResource StringFormatter},
ConverterParameter='Anzahl: {0}',
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
C# - Modelcode
public class ProductGroup : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
private decimal _priceSum;
private int _count;
public Product groupedProduct { get; set; }
public int Count
{
get
{
return _count;
}
set
{
_count = value;
onPropertyChanged(this, "Count");
}
}
public decimal Price { get; set; }
public decimal PriceSum
{
get { return _priceSum; }
set
{
_priceSum = value;
onPropertyChanged(this, "PriceSum");
}
}
}
C# - CollectionFill
ProductSumUp.ItemsSource = _showProducts;
bool prodExists = false;
foreach (ProductGroup prodz in _showProducts)
{
if (prodz.groupedProduct.ID == prod.ID)
{
prodExists = true;
}
}
if (!prodExists)
{
ProductGroup prodGroup = new ProductGroup();
prodGroup.groupedProduct = prod;
prodGroup.Price = prod.Price;
prodGroup.Count = 1;
prodGroup.PriceSum += prod.Price;
_showProducts.Add(prodGroup);
}
else
{
ProductGroup pgroup = _showProducts.First(x => x.groupedProduct.ID == prod.ID);
if (pgroup != null)
{
pgroup.Count++;
pgroup.PriceSum += pgroup.Price;
}
}
Please don't judge my programming style, I solved the problem quick and dirty.
I hope that somebody could use my solution for his/her problems and thanks for your help, it saved me a lot of time.
I've tried solving this myself, looking at several possible solutions here on Stack Overflow, but alas I've been unable to solve this issue.
TL;DR version:
The problem:
A listBox using databinding to show a list of RPG characters, which have a nested property for their attributes. I can't get the attributes to show due to the limitations with nested properties and databindings.
The code below is related to the issue.
I have a listBox that has a databinding that controls what is shown in the list. The databinding uses ObservableCollection for the list of objects that the list contains. All this works fine, but is related to the issue at hand.
The listBox databinding has several nested properties in each element, that I want to display and change in the form, yet I cannot get nested databinding to work correctly.
This is the listBox XAML:
<ListBox x:Name="listCharacters" Margin="2,0" ItemsSource="{Binding}" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Hidden" SelectionChanged="listCharacters_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Character}" x:Name="Symchar">
<Grid Width="125" HorizontalAlignment="Left" Background="{x:Null}">
<Grid.RowDefinitions>
<RowDefinition Height="18"/>
<RowDefinition Height="12"/>
<RowDefinition Height="16"/>
</Grid.RowDefinitions>
<Image Panel.ZIndex="5" HorizontalAlignment="Right" VerticalAlignment="Top" Height="16" Width="16" Margin="0,2,0,0" Source="Resources/1454889983_cross.png" MouseUp="DeleteCharacter" />
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" FontWeight="Bold" FontSize="13.333" Grid.Row="0" TextTrimming="CharacterEllipsis" Padding="0,0,16,0" />
<TextBlock Text="{Binding RealRace.Label, UpdateSourceTrigger=PropertyChanged}" FontSize="9.333" Grid.Row="1" FontStyle="Italic" />
<TextBlock FontSize="9.333" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Top">
<Run Text="{Binding RealClass.Archetype.Label, UpdateSourceTrigger=PropertyChanged}"/>
<Run Text=" - "/>
<Run Text="{Binding RealClass.Label, UpdateSourceTrigger=PropertyChanged}"/>
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And setting the listBox ItemSource:
this.listCharacters.ItemsSource = CharacterList;
This is the character class, I removed unrelated code (XML serialization attributes etc.)
public class Character : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
this.NotifyPropertyChanged("Name");
}
}
private string _player;
public string Player
{
get { return _player; }
set
{
_player = value;
this.NotifyPropertyChanged("Player");
}
}
private string _race;
public string Race
{
get { return _race; }
set
{
_race = value;
this.NotifyPropertyChanged("Race");
}
}
private Race _realRace;
public Race RealRace
{
get { return _realRace; }
set
{
_realRace = value;
Race = value.Id;
this.NotifyPropertyChanged("RealRace");
}
}
private string _gender;
public string Gender
{
get { return _gender; }
set
{
_gender = value;
this.NotifyPropertyChanged("Gender");
}
}
private Attributes _attributes;
public Attributes Attributes
{
get { return _attributes; }
set
{
_attributes = value;
this.NotifyPropertyChanged("Attributes");
}
}
private string _class;
public string Class
{
get { return _class; }
set
{
_class = value;
this.NotifyPropertyChanged("Class");
}
}
private Class _realClass;
public Class RealClass
{
get { return _realClass; }
set
{
_realClass = value;
Class = value.Id;
this.NotifyPropertyChanged("RealClass");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
To keep it simple, the property that I've been testing with, is the 'Attributes' property, this is the code for it:
public class Attributes : INotifyPropertyChanged
{
private int _accurate;
public int Accurate
{
get { return _accurate; }
set
{
_accurate = value;
this.NotifyPropertyChanged("Accurate");
}
}
private int _cunning;
public int Cunning
{
get { return _cunning; }
set
{
_cunning = value;
this.NotifyPropertyChanged("Cunning");
}
}
private int _discreet;
public int Discreet
{
get { return _discreet; }
set
{
_discreet = value;
this.NotifyPropertyChanged("Discreet");
}
}
private int _persuasive;
public int Persuasive
{
get { return _persuasive; }
set
{
_persuasive = value;
this.NotifyPropertyChanged("Persuasive");
}
}
private int _quick;
public int Quick
{
get { return _quick; }
set
{
_quick = value;
this.NotifyPropertyChanged("Quick");
}
}
private int _resolute;
public int Resolute
{
get { return _resolute; }
set
{
_resolute = value;
this.NotifyPropertyChanged("Resolute");
}
}
private int _strong;
public int Strong
{
get { return _strong; }
set
{
_strong = value;
this.NotifyPropertyChanged("Strong");
}
}
private int _vigilant;
public int Vigilant
{
get { return _vigilant; }
set
{
_vigilant = value;
this.NotifyPropertyChanged("Vigilant");
}
}
private int _toughness;
public int Toughness
{
get { return _toughness; }
set
{
_toughness = value;
this.NotifyPropertyChanged("Toughness");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
I want to display each individual attribute in a field when a character in the listBox is selected, this works fine with properties directly in the character class, but due to the limitations on nested properties and databindings, I haven't been able to get it to work with the 'Attributes' properties values.
XAML for one of the attribute input fields:
<TextBox x:Name="attr_Accurate" DataContext="{Binding Path=(local:Character.Attributes), XPath=SelectedItem, ElementName=listCharacters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Path=(local:Accurate)}" PreviewTextInput="NumericInput"/>
The UpdateSourceTrigger is simply a method to only allow integers to be input in the field:
private void NumericInput(object sender, TextCompositionEventArgs e)
{
if (!char.IsDigit(e.Text, e.Text.Length - 1))
{
e.Handled = true;
}
}
If anyone could help me get the values within the selected character's attributes to show up via databindings, I would greatly appreciate it.
Use binding as following:
To Show Accurate Property value:
<TextBox x:Name="attr_Accurate" Text="{Binding Path=SelectedItem.Attributes.Accurate), ElementName=listCharacters, Mode=OneWay}" PreviewTextInput="NumericInput"/>
To Show Cunning property value:
<TextBox x:Name="attr_Accurate" Text="{Binding Path=SelectedItem.Attributes.Cunning), ElementName=listCharacters, Mode=OneWay}" PreviewTextInput="NumericInput"/>
and so one.
(I'm not sure if you want binding to be two way or one so please
change as your need)
I have two properties in my ViewModel:
public double Start { get; set; }
public double Duration { get; set; }
I have three textboxes in my View:
<TextBox Name="StartTextBox" Text="{Binding Start}" />
<TextBox Name="EndTextBox" />
<TextBox Name="DurationTextBox" Text="{Binding Duration} />
I want to achieve the following behavior:
When the user changes the content of the Start- or the EndTextBox, the DurationTextBox should be updated accordingly (Duration = End - Start).
When the user changes the DurationTextBox, the EndTextBox should be updated accordingly (End = Start + Duration).
I can achieve this by listening to the TextChanged events in code behind. I would prefer to achieve this via MultiBinding, though. Is it possible?
The problems I encounter, when I try to use MultiBinding:
If I put the MultiBinding on the DurationTextBox I cannot bind it to the Duration property.
If I put the MultiBinding on the EndTextBox and the user changes the StartTextBox, the EndTextBox gets updated instead of the DurationTextBox.
In any case I'm not able to implement the ConvertBack method.
I think blow codes are much easier to achieve the behavior which you want:
XAML:
<TextBox Name="StartTextBox" Text="{Binding Start}" />
<TextBox Name="EndTextBox" Text="{Binding End}" />
<TextBox Name="DurationTextBox" Text="{Binding Duration}" />
ViewModel:
private double _start;
private double _duration;
private double _end;
public double Start
{
get
{
return _start;
}
set
{
if (_start != value)
{
_start = value;
Duration = _end - _start;
OnPropertyChanged("Start");
}
}
}
public double Duration
{
get
{
return _duration;
}
set
{
if (_duration != value)
{
_duration = value;
End = _duration + _start;
OnPropertyChanged("Duration");
}
}
}
public double End
{
get
{
return _end;
}
set
{
if (_end != value)
{
_end = value;
Duration = _end - _start;
OnPropertyChanged("End");
}
}
}
The OnPropertyChanged in ViewModel implements the INotifyPropertyChanged interface like this:
public class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What is the proper way, for the UI to get notified, that property "Difference" has changed in the following code sample?
The property is read-only. The property's value must always be calculated based on the other properties.
MainWindow.xaml:
<Window x:Name="winCalcs" x:Class="BindingList.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:BindingList"
Title="Calculations" Height="350" Width="525">
<Window.Resources>
<m:OperationList x:Key="OperationData"/>
<CollectionViewSource x:Key="Operations"
Source="{StaticResource ResourceKey=OperationData}"/>
</Window.Resources>
<Grid>
<TabControl x:Name="tabsMain">
<TabItem x:Name="tab01" Header="Tab 1">
<DataGrid x:Name="dg01"
ItemsSource="{Binding
Source={StaticResource ResourceKey=Operations},
UpdateSourceTrigger=PropertyChanged}" />
</TabItem>
<TabItem x:Name="tab02" Header="Tab 2">
<DataGrid x:Name="dg02"
ItemsSource="{Binding
Source={StaticResource ResourceKey=Operations},
UpdateSourceTrigger=PropertyChanged}" />
</TabItem>
</TabControl>
</Grid>
</Window>
Operation.cs:
namespace BindingList
{
class Operation : INotifyPropertyChanged
{
private float _minuend;
private float _subtrahend;
public float Minuend
{
get
{
return this._minuend;
}
set
{
if (this._minuend == value) return;
this._minuend = value;
this.NotifyPropertyChanged("Minuend");
}
}
public float Subtrahend
{
get
{
return this._subtrahend;
}
set
{
if (this._subtrahend == value) return;
this._subtrahend = value;
this.NotifyPropertyChanged("Subtrahend");
}
}
public float Difference
{
get
{
return Minuend - Subtrahend;
}
private set {}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
OperationList.cs:
namespace BindingList
{
class OperationList : BindingList<Operation>
{
public OperationList()
{
Add(new Operation());
}
}
}
Difference changes when Minuend or Subtrahend changes. That means you need to notify changed for Difference within the set of Minuend or Subtrahend.
There is no need for property setter for Difference.
On a side note, there is no need for using this everywhere
public float Minuend
{
get
{
return _minuend;
}
set
{
if (_minuend == value) return;
_minuend = value;
NotifyPropertyChanged("Minuend");
NotifyPropertyChanged("Difference");
}
}
public float Subtrahend
{
get
{
return _subtrahend;
}
set
{
if (_subtrahend == value) return;
_subtrahend = value;
NotifyPropertyChanged("Subtrahend");
NotifyPropertyChanged("Difference");
}
}
public float Difference
{
get
{
return Minuend - Subtrahend;
}
}
In these situations I typically set the property explicitly and and raise the PropertyChanged event.
namespace BindingList
{
class Operation : INotifyPropertyChanged
{
private float _minuend;
private float _subtrahend;
private float _difference;
public float Minuend
{
get
{
return this._minuend;
}
set
{
if (this._minuend == value)
return;
this._minuend = value;
this.NotifyPropertyChanged("Minuend");
this.UpdateDifference();
}
}
public float Subtrahend
{
get
{
return this._subtrahend;
}
set
{
if (this._subtrahend == value)
return;
this._subtrahend = value;
this.NotifyPropertyChanged("Subtrahend");
this.UpdateDifference();
}
}
private void UpdateDifference()
{
this.Difference = this.Minuend - this.Subtrahend;
}
public float Difference
{
get
{
return this._difference
}
private set
{
if (this._difference == value)
return;
this._difference = value;
this.NotifyPropertyChanged("Difference");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
I've spent lots of hours with this problem.
I have a class with data:
public class User : INotifyPropertyChanged
{
private int _key;
private string _fullName;
private string _nick;
public int Key
{
get{return _key;}
set { _key = value; NotifyPropertyChanged("Key"); }
}
public string Nick
{
get { return _nick; }
set { _nick = value; NotifyPropertyChanged("Nick"); }
}
public string FullName
{
get { return _fullName; }
set { _fullName = value; NotifyPropertyChanged("FullName"); }
}
public User()
{
Nick = "nickname";
FullName = "fullname";
}
public User(String nick, String name, int key)
{
Nick = nick;
FullName = name;
}
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return string.Format("{0} {1}, {2}", Key, Nick, FullName);
}
}
Next I have a class with observablecollection of userClass class:
public class UserList : ObservableCollection<UserList>
{
public UserList (){}
~UserList ()
{
//Serialize();
}
public void Serialize(ObservableCollection<UserList> usersColl)
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, usersColl);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
public void Deserialize()
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();
//users = (Hashtable) formatter.Deserialize(fs);
//usersColl = (ObservableCollection<userClass>)formatter.Deserialize(fs);
}
catch (SerializationException e)
{
MessageBox.Show(" Error: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
}
In fact, after lots of testing an editing, big part of code doesn't work, like serialization. But it is not necessary for data binding and binding is what i am solving now.
So i have this collection and want to bind it to listBox.
I tried several ways, but haven't got it to work.
The last one I tried gave me the write error:
The resource 'users' cannot be resolved.
<ListBox Grid.Column="0" Name="userViewLeft" ItemsSource="{Binding Source={StaticResource users} }" />
Some points to be noted
Make Properties public and not private.
Make Variables private.
Follow Naming Conventions and don't append class behind the class.
ItemsSource you supply should be as per scope of the data, In my example the userlist in class scope and I have provided the ItemSource on Window Loaded event.
Here is the an complete example code, In this I have nested the Grid Control inside ListBox because later on you can change the ListBox property for VirtualizingStackPanel.
So that it would give huge performance gain when you have heavy updates on the list.
Also you can use BindingList which is in my opinion better than ObservableCollection performance wise.
User class:
public class User : INotifyPropertyChanged
{
private int _key;
private string _fullName;
private string _nick;
public int Key
{
get { return _key; }
set { _key = value; NotifyPropertyChanged("Key"); }
}
public string NickName
{
get { return _nick; }
set { _nick = value; NotifyPropertyChanged("NickName"); }
}
public string Name
{
get { return _fullName; }
set { _fullName = value; NotifyPropertyChanged("Name"); }
}
public User(String nick, String name, int key)
{
this.NickName = nick;
this.Name = name;
this.Key = key;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return string.Format("{0} {1}, {2}", Key, NickName, Name);
}
}
User List class:
public class Users : ObservableCollection<User>
{
public Users()
{
Add(new User("Jamy", "James Smith", Count));
Add(new User("Mairy", "Mary Hayes", Count));
Add(new User("Dairy", "Dary Wills", Count));
}
}
XAML:
<Grid>
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="416,12,0,0" x:Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<ListBox x:Name="UserList" HorizontalContentAlignment="Stretch" Margin="12,41,12,12">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Key}" Margin="3" Grid.Column="0" />
<TextBlock Text="{Binding NickName}" Margin="3" Grid.Column="1" />
<TextBlock Text="{Binding Name}" Margin="3" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
XAML Code behind:
public partial class MainWindow : Window
{
public static Users userslist = new Users();
DispatcherTimer timer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
timer.Interval = DateTime.Now.AddSeconds(10) - DateTime.Now;
timer.Tick += new EventHandler(timer_Tick);
UserList.ItemsSource = userslist;
}
void timer_Tick(object sender, EventArgs e)
{
userslist.Add(new User("Jamy - " + userslist.Count, "James Smith", userslist.Count));
userslist.Add(new User("Mairy - " + userslist.Count, "Mary Hayes", userslist.Count));
userslist.Add(new User("Dairy - " + userslist.Count, "Dary Wills", userslist.Count));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (button1.Content.ToString() == "Start")
{
button1.Content = "Stop";
timer.Start();
}
else
{
button1.Content = "Start";
timer.Stop();
}
}
}
You need to do 2 things:
Firstly, set the DataContext of whatever element (Window/UserControl/whatever) contains your ListBox to an object that looks like:
public class ViewModel
{
public ViewModel() { this.users = new userListClass(); }
public userListClass users { get; private set; }
}
This is your view model, and it is what you want to bind to.
Secondly, change your binding to ItemsSource="{Binding Path=users}". This translates into "set the value of my ItemsSource property to the value of the property users on this.DataContext. Because the DataContext is inherited from the parent, and you set this to the ViewModel class above, your ListBox will now display your user list.