Nested Collections not updating the UI when properties change - wpf

I had a listbox of user controls and each usercontrol displays properties of the bounded data and bind a collection to custom control when the custom control changes the data the UI not updated my code is as follows :
ObservableCollection<Subscription> subscriptions = new ObservableCollection<Subscription> SubscriptionRepository.GetSubscriptions());
SubListBox.ItemsSource = subscriptions;
xaml :
<DataTemplate x:Key="MyDataTemplate">
<UserControls:SubscriptionUC />
</DataTemplate>
<ListBox x:Name="SubListBox" ItemTemplate="{StaticResource MyDataTemplate}">
</ListBox>
user control :
<TextBlock Text="{Binding Path=DaysAttended}" />
<cc:CustomControl SubscriptionSource="{Binding Path=SubscriptionDays,Mode=TwoWay}" />
Subscription class :
public class Subscription : INotifyPropertyChanged
{
public int SubscriptionTypeId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public ObservableCollection<SubscriptionDay> SubscriptionDays { get; set; }
public int DaysAttended { get { return SubscriptionDays.Count(d => d.Attended == true); } }
public void DayChanged()
{
RaisePropertyChanged("SubscriptionDays");
RaisePropertyChanged("DaysAttended");
}
}
DayChanged() is called from SubscriptionDay class when SubscriptionDay property changed and it is called but DaysAttended not updated.

You also need to have setter for DaysAttended property,For Two Way binding it is must that your property should have getter and setter .I hope this will help.

Related

WPF Combo Box not displaying dynamically changed selected item

I am trying to change the selected item of a combo box based on a change in another combo box. The situation is complicated by the fact that both combo boxes appear in a list of item templates.
The XAML is as follows:
<ListBox ItemsSource="{Binding AncillaryExtendedPropertyViewModels}" ItemTemplateSelector="{StaticResource templateSelector}"/>
<DataTemplate x:Key="EnumDataTemplate"> <Grid Margin="4"
MinHeight="25"> <ComboBox SelectedItem="{Binding ExtendedPropertyEnum,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding ExtendedPropertyEnumList}"
DisplayMemberPath="Value"/> </Grid> </DataTemplate>
The data context of the view containing the XAML is set to AncillaryBaseViewModel. The following is a cut down version of AncillaryBaseViewModel.
public class AncillaryBaseViewModel : ComplexOrderItemViewModel, IDataErrorInfo
{
private ObservableCollection<ExtendedPropertyViewModel> _ancillaryExtendedPropertyViewModels;
public ObservableCollection<ExtendedPropertyViewModel> AncillaryExtendedPropertyViewModels
{
get { return _ancillaryExtendedPropertyViewModels; }
set
{
_ancillaryExtendedPropertyViewModels = value;
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
}
and the ExtendedPropertyViewModel class....
public class ExtendedPropertyViewModel : DataTemplateSelector
{
private ExtendedProperty _extendedProperty;
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{ return ExtendedProperty.ExtendedPropertyEnum; }
set
{
if (ExtendedProperty.ExtendedPropertyEnum != value)
{
_extendedProperty.ExtendedPropertyEnum = value;
AncillaryBaseViewModel parent = RequestParent();
if (parent != null)
{
parent.AncillaryExtendedPropertyViewModels[7].ExtendedPropertyEnum =
ExtendedProperty.ExtendedPropertyEnum.DependentExtendedPropertyEnums[0];
}
parent.OrderItem.SetStockCode();
PropertyChanged += parent.OnExtendedPropertyChanged;
OnPropertyChanged();
}
}
}
and the ExtendedProperty class....
public class ExtendedProperty : ViewModelBase
{
private ExtendedPropertyEnum _extendedPropertyEnum;
public int ExtendedPropertyID { get; set; }
public int OrderItemTypeID { get; set; }
public string DisplayName {get; set;}
public ObservableCollection<ExtendedPropertyEnum> ExtendedPropertyEnumList { get; set; }
public string Value { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{
return _extendedPropertyEnum;
}
set
{
_extendedPropertyEnum = value;
OnPropertyChanged("ExtendedPropertyEnum");
}
}
}
To summarise, when I change the value of combo box A through the UI, this calls the ExtendedPropertyEnum setter within ExtendedPropertyViewModel, which changes the ExtendedPropertyEnum bound to another combo box B, which is in the same list. I would expect this to update the value displayed in combo box B accordingly, which it does not.
As an aside, changing the value of combo box A does update a label that is not within a data template. The XAML for this label is....
<Label Content="{Binding StockCode}" MinWidth="100"/>
This is updated by the following code within AncillaryBaseViewModel....
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
}
I thought I could change this to the following to force the combo boxes in the list to update.
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
However, this did not work either.
Any help greatly appreciated.
Roger.
if I understand the question correctly then you are expecting a changed value within an observablecollection to be reflected within the UI. This will not happen. observablecollections raise notifyproperty events when the collection itself changes, not when values within them change. You'll either need to raise a notifyproperty event on the value changing or remove the item from the list and add it back with a new value.

WPF Combobox initial dictionary binding value not showing

I have a wpf combobox bound to a property LogicalP of a class SInstance. The ItemSource for the combobox is a dictionary that contains items of type LogicalP.
If I set LogicalP in SInstance to an initial state, the combobox text field shows empty. If I select the pulldown all my dictionary values are there. When I change the selection LogicalP in SInstance gets updated correctly. If I change Sinstance in C# the appropriate combobox value doesn't reflect the updated LogicalP from the pulldown.
I've set the binding mode to twoway with no luck. Any thoughts?
My Xaml:
<UserControl.Resources>
<ObjectDataProvider x:Key="PList"
ObjectType="{x:Type src:MainWindow}"
MethodName="GetLogPList"/>
</UserControl.Resources>
<DataTemplate DataType="{x:Type src:SInstance}">
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource PList}}"
DisplayMemberPath ="Value.Name"
SelectedValuePath="Value"
SelectedValue="{Binding Path=LogicalP,Mode=TwoWay}">
</ComboBox>
</Grid>
</DataTemplate>
My C#:
public Dictionary<string, LogicalPType> LogPList { get; private set; }
public Dictionary<string, LogicalPType> GetLogPList()
{
return LogPList;
}
public class LogicalPType
{
public string Name { get; set; }
public string C { get; set; }
public string M { get; set; }
}
public class SInstance : INotifyPropertyChanged
{
private LogicalPType _LogicalP;
public string Name { get; set; }
public LogicalPType LogicalP
{
get { return _LogicalP; }
set
{
if (_LogicalP != value)
{
_LogicalP = value;
NotifyPropertyChanged("LogicalP");
}
}
}
#region INotifyPropertyChanged Members
#endregion
}
They are not looking at the same source.
You need to have SInstance supply both the LogPList and LogicalP.
_LogicalP is not connected to LogPList
If you want to different objects to compare to equal then you need to override Equals.
Here's my working solution. By moving the dictionary retrieval GetLogPList to the same class as that supplies the data (as suggested by Blam) I was able to get the binding to work both ways. I changed binding to a list rather than a dictionary to simplify the combobox
Here's the updated Xaml showing the new ItemsSource binding and removal of the SelectedValuePath:
<DataTemplate DataType="{x:Type src:SInstance}">
<Grid>
<ComboBox ItemsSource="{Binding GetLogPList}"
DisplayMemberPath ="Name"
SelectedValue="{Binding Path=LogicalP,Mode=TwoWay}">
</ComboBox>
</Grid>
</DataTemplate>
I then changed the dictionary LogPList to static so that it would be accessible to the class SInstance:
public static Dictionary<string, LogicalPType> LogPList { get; private set; }
Finally, I moved GetLogPList to the class SInstance as a property. Note again it's returning a list as opposed to a dictionary to make the Xaml a little simpler:
public class SInstance : INotifyPropertyChanged
{
public List<LogicalPType> GetLogPList
{
get { return MainWindow.LogPList.Values.ToList(); }
set { }
}
private LogicalPType _LogicalP;
public string Name { get; set; }
public LogicalPType LogicalP
{
get { return _LogicalP; }
set
{
if (_LogicalP != value)
{
_LogicalP = value;
NotifyPropertyChanged("LogicalP");
}
}
}
#region INotifyPropertyChanged Members
#endregion
}

How to bind XamDataGrid to Model Properties through ViewModel

In my C#/WPF app, I and am trying to bind an Infragistics WPF XamDataGrid to an ObservableCollection. MyViewModel exposes the Model property. When I add the property whose value I want displayed to MyViewModel, everything works fine. Below shows an example where I'm binding to the UnitID property exposed by an instance of a MyViewModel contained in the UnitsOfMeasure ObservableCollection.
If I change "Name" to "Model.Name", at runtime the correct number of rows appears in the data grid, but all fields are blank. No runtime error is raised.
My workaround is to add each property to MyViewModel and reference the property in the Model in each getter and setter. Makes for redundant code, and doesn't agree with Laurent's demo's.
This xaml works:
<igEditors:XamDataGrid Theme="Royale" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Stretch" Margin="2" Name="xamDataGridUnitsOfMeasure" DataSource="{Binding Path=UnitsOfMeasure}" VerticalAlignment="Stretch">
<igEditors:XamDataGrid.FieldLayoutSettings>
<igEditors:FieldLayoutSettings AutoGenerateFields="False"/>
</igEditors:XamDataGrid.FieldLayoutSettings>
<igEditors:XamDataGrid.FieldLayouts>
<igEditors:FieldLayout IsDefault="True">
<igEditors:FieldLayout.Fields>
<igEditors:Field Name="UnitID" Label="UOM ID">
<igEditors:Field.Settings>
<igEditors:FieldSettings AllowEdit="False"/>
</igEditors:Field.Settings>
</igEditors:Field>
.
.
.
Changing UnitID to Model.UnitID does not work:
<igEditors:XamDataGrid Theme="Royale" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Stretch" Margin="2" Name="xamDataGridUnitsOfMeasure" DataSource="{Binding Path=UnitsOfMeasure}" VerticalAlignment="Stretch">
<igEditors:XamDataGrid.FieldLayoutSettings>
<igEditors:FieldLayoutSettings AutoGenerateFields="False"/>
</igEditors:XamDataGrid.FieldLayoutSettings>
<igEditors:XamDataGrid.FieldLayouts>
<igEditors:FieldLayout IsDefault="True">
<igEditors:FieldLayout.Fields>
<igEditors:Field Name="Model.UnitID" Label="UOM ID">
<igEditors:Field.Settings>
<igEditors:FieldSettings AllowEdit="False"/>
</igEditors:Field.Settings>
</igEditors:Field>
.
.
.
Here's part of the ViewModel that is functional:
public class UnitOfMeasureViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the UnitOfMeasureViewModel class.
/// </summary>
public UnitOfMeasureViewModel(UnitOfMeasure model)
{
Model = model;
//Model.PropertyChanged += (s, e) =>
// {
// RaisePropertyChanged(e.PropertyName);
// };
}
public UnitOfMeasure Model
{
get;
private set;
}
public string UnitID
{
get
{
return Model.UnitID;
}
set
{
Model.UnitID = value;
}
}
The nested binding is supported by XamGrid control - http://help.infragistics.com/Help/NetAdvantage/WPF/2012.1/CLR4.0/html/xamGrid.html
Or
Refer to this link of Infragistics help -http://www.infragistics.com/community/forums/t/67603.aspx
This will help you do the nested binding
Else =>
Do you want to extract interface out of Model object class and try to implement it at the class holding Model as property?
public interface IModel
{
public string UnitId { get; set; }
}
public class Model : IModel
{
private string unitId;
public string UnitId
{
get { return unitId; }
set { unitId = value; }
}
}
public class ModelParent : IModel
{
private Model model;
public string UnitId
{
get { return model.UnitId; }
set { model.UnitId = value; }
}
}

Can a View have two View Models as its Data Context?

I have two datagrids in a single view but the collections which are ItemsSource of these datagrids are in different View Models. So is it possible to bind these two datagrids with the collections in two different View Models?
Go for a view model combining both:
public class ViewModelA {
public ObservableCollection<CustomClass> Items { get; set; }
/* properties, etc. */
}
public class ViewModelB {
/* properties, etc. */
}
public class CombiningViewModel {
public ViewModelA A { get; set; }
public ViewModelB B { get; set; }
}
Binding can be done like
<DataGrid ItemsSource="{Binding A.Items}">
<!-- Sample, not complete -->
</DataGrid>
No, not directly. You do have options though:
You could set the DataCOntext of the view to itself, then expose each viewmodel through a separate property and bind to those properties:
public class MyView : Window
{
public MyView()
{
this.DataContext = this;
}
public ViewModel1 FirstViewModel { get; set; }
public ViewModel2 SecondViewModel { get; set; }
}
Or you could make a wrapper viewmodel which either extends (inherits from) one of the viewmodels, or wraps them both and surfaces the appropriate properties:
public class MyCompositeViewModel
{
public ViewModel1 FirstViewModel { get; set; }
public ViewModel2 SecondViewModel { get; set; }
}
You can set the DataContext for each DataGrid rather than for the container view.
<Grid>
<DataGrid ... DataContext="..." />
<DataGrid ... DataContext="..." />
</Grid>
Or don't use a DataContext and Bind to the models directly
<DataGrid ItemsSource="{Binding Source={StaticResource ...}}" />

Child item in TreeView not updating

I copied the basic method of having checkbox in a treeview from the official Silverlight toolkit Checkboxes in a TreeView example.
When a user clicks on a parent TreeViewItem I want all of the child items to be checked, as in the above example. This works fine when the parent is collapsed, clicking the checkbox puts a tick in the parent and when you expand the node all children have a tick in the checkbox.
However it doesn't work if the parent is expanded. None of the children are updated to have a tick in the checkbox, although the underlying data list is updated.
My XAML is as follows:
<sdk:HierarchicalDataTemplate x:Key="NodeTemplate" ItemsSource="{Binding Path=Contracts}">
<StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Path=Name}">
<CheckBox IsTabStop="False" IsThreeState="{Binding Path=HasContracts}" IsChecked="{Binding Path=Selected, Mode=TwoWay}" Click="CheckBox_Click" />
<TextBlock Text="{Binding Path=Name}" Tag="{Binding Path=ID}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
<sdk:TreeView x:Name="tvClientContract" ItemsSource="{Binding Path=ClientContracts, Mode=TwoWay}" ItemTemplate="{StaticResource NodeTemplate}"/>
This is bound to a List<ClientContract> and uses the same code behind as in the linked example.
The ClientContract object is:
public int ID { get; set; }
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
How can I force the child to repaint itself as the underlying List<ClientContract> object is updated?
If you want to use INotifyPropertyChange(what I did instead of using ObservableCollection) here is how you do it per example on the ID element:
public class myclass : INotifyPropertyChanged
{
private int id_Value;
public int ID
{
get { return id_Value; }
set
{
id_Value = value;
NotifyPropertyChanged("ID");
}
}
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I hope this helps if it was what you were trying to do.
Try using ObservableCollection<ClientContract> instead of a List<>. Usually you want to databind to this collection type instead when the data is dynamic so it can notify the UI of collection changes.

Resources