In the code below my itemssource does not change but one of the properties of the control changes, which changes which field is bound. I need to clear the datagrid items and force it to rebind. How do I do that? By the way, onpropertychanged for the Items property is raised but the grid does not rebuild.
Thank you,
Sam
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Windows;
System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;
System.Windows.Navigation;
System.Windows.Shapes;
System.Collections.ObjectModel;
Poolman.Entities;
Poolman.Common;
Poolman.Models;
System.Threading;
System.Threading.Tasks;
Infragistics.Windows.DataPresenter;
Infragistics.Windows.DataPresenter.Events;
Infragistics.Controls.Editors;
Infragistics.Windows.DockManager;
namespace
Poolman.Controls
{
public partial class ExposureMatrixAccountGrid : Common.
BaseUserControl
public IList<ExposureItem
> Items
get { return (IList<ExposureItem
>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value
); }
}
public static readonly DependencyProperty
ItemsProperty =
DependencyProperty.Register("Items", typeof(IList<ExposureItem>), typeof(ExposureMatrixAccountGrid), new UIPropertyMetadata(null
,DataChanged));
public bool
IsQuantityView
get { return (bool
)GetValue(IsQuantityViewProperty); }
set { SetValue(IsQuantityViewProperty, value
IsQuantityViewProperty =
DependencyProperty.Register("IsQuantityView", typeof(bool), typeof(ExposureMatrixAccountGrid), new UIPropertyMetadata(new Boolean
(),ViewChanged));
private ObservableCollection<ExposureItem
> _GroupedItems;
public ObservableCollection<ExposureItem
> GroupedItems
get { return
_GroupedItems; }
set
if (_GroupedItems != value
)
_GroupedItems =
value
;
OnPropertyChanged(
"GroupedItems"
);
private bool
_LoadingData;
LoadingData
_LoadingData; }
if (_LoadingData != value
_LoadingData =
"LoadingData"
public decimal MinValue { get; set
; }
public decimal MaxValue { get; set
public XamDataGrid AccountGrid { get { return
Grid1; } }
IsDirty;
public
ExposureMatrixAccountGrid()
InitializeComponent();
SummaryCalculator.Register(MarketValueSummaryCalculator
.Calculator);
IsDirty =
true
IsVisibleChanged +=
new DependencyPropertyChangedEventHandler
(ExposureMatrixAccountGrid_IsVisibleChanged);
void ExposureMatrixAccountGrid_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs
e)
RebuildGrid();
public static void ViewChanged(DependencyObject sender, DependencyPropertyChangedEventArgs
ExposureMatrixAccountGrid control = sender as ExposureMatrixAccountGrid
control.IsDirty =
control.RebuildGrid();
public static void DataChanged(DependencyObject sender, DependencyPropertyChangedEventArgs
private void
RebuildGrid()
if (!IsDirty || this.Visibility != Visibility
.Visible)
return
LoadingData =
Grid1.DataSource =
null
new ObservableCollection<ExposureItem
>();
Grid1.DataSource = GroupedItems;
Task
.Factory.StartNew(() =>
// Group items by (not) Coupon
try
Dispatcher.BeginInvoke((
Action)delegate
()
MinValue = Items.Any() ? Items.Min(x => x.Qty) : 0;
MaxValue = Items.Any() ? Items.Max(x => x.Qty) : 0;
GroupedItems =
>(
Items
.GroupBy(x =>
new
{ x.AcctNo, x.Collateral, x.DeliveryDate, x.AcctMarketValue, x.PM_Name, x.Type })
.Select(x =>
ExposureItem
AcctNo = x.Key.AcctNo,
Collateral = x.Key.Collateral,
DeliveryDate = x.Key.DeliveryDate,
AcctMarketValue = x.Key.AcctMarketValue,
PM_Name = x.Key.PM_Name,
Type = x.Key.Type
})
.OrderBy(x => x.AcctNo));
false
});
finally
}).ContinueWith(x =>
if (x.Exception != null
throw new Exception("See Inner Exception"
, x.Exception);
},
TaskScheduler
.FromCurrentSynchronizationContext());
///
<summary>
Build out coupon coulums in datagrid. Add a column for total and for every distinct coupon.
</summary>
<param name="sender"></param>
<param name="e"></param>
private void Grid1_FieldLayoutInitialized(object sender, Infragistics.Windows.DataPresenter.Events.FieldLayoutInitializedEventArgs
Style AmountStyle = (Style)TryFindResource(IsQuantityView ? "AmountStyle" : "Percent3Style"
string stringFormat = "{0:" + ((Setter)AmountStyle.Setters[0]).Value.ToString() + "}"
SummaryCalculator
calculator;
List<decimal
> coupons = Items.Select(x => x.Coupon).OrderBy(x => x).Distinct().ToList();
coupons.Insert(0, -1m);
// Create a total column. Use minus one as a coupon to identify the total column
foreach (decimal coupon in
coupons)
string columnName = Guid.NewGuid().ToString();
// Generate a bogus field name so the summary calculator knows what to total
if
(IsQuantityView)
calculator =
new SumSummaryCalculator
();
else
new PctMarketValueSummaryCalculator
UnboundField unboundField = new
UnboundField
Name = columnName,
Label = coupon == -1 ?
"Total" : coupon.ToString("#.###") + "%"
,
DataType =
typeof(decimal
),
Settings =
new FieldSettings
{ EditorStyle = AmountStyle },
Width =
new FieldLength
(80),
};
MultiBinding mb = new MultiBinding { Converter = new CouponColumnConverter
() };
mb.Bindings.Add(
new Binding());
// The row being bound
new Binding { Source = coupon });
// The coupon that identifies the column.
new Binding { Source = Items });
// Lookup list to see if an entry for coupon for passed row exists
new Binding { Source = IsQuantityView });
// true to display qty, false to display pct mkt value.
new Binding
{ Source = unboundField });
{ Source = MinValue });
{ Source = MaxValue });
unboundField.Binding = mb;
e.FieldLayout.Fields.Add(unboundField);
Grid1.FieldLayouts[0].SummaryDefinitions.Add(
new SummaryDefinition
{ Calculator = calculator, StringFormat = stringFormat, SourceFieldName = columnName });
private void Grid1_InitializeRecord(object sender, InitializeRecordEventArgs
//e.Record.DataPresenter.Background = Brushes.Yellow;
//var zz = e.Record.FieldLayout.Fields[0].GetValue(CellValuePresenter.
private void Grid1_Loaded(object sender, RoutedEventArgs
//int rowCount = Grid1.Records.Count;
//if (rowCount == 0)
// return;
//int cellCount = ((DataRecord)Grid1.Records[0]).Cells.Count;
//for (int row = 0; row < rowCount; row++)
// for (int cell = 6; cell < cellCount; cell++)
// CellValuePresenter.FromCell((Grid1.Records[row] as DataRecord).Cells[cell]).Background = Brushes.Yellow;
public class CouponColumnConverter :
IMultiValueConverter
public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo
culture)
ExposureItem boundItem = (ExposureItem
)value[0];
decimal coupon = System.Convert.ToDecimal(value[1]);
// coupon will be minus one if this is the totals column
IList<ExposureItem> list = (IList<ExposureItem
>)value[2];
bool isQuantityView = (bool
)value[3];
UnboundField field = (UnboundField
)value[4];
decimal minValue = (decimal
)value[5];
decimal maxValue = (decimal
)value[6];
decimal
result = 0;
IEnumerable<ExposureItem
> items = list.Where(x =>
x.AcctNo == boundItem.AcctNo
&& x.Collateral == boundItem.Collateral
&& x.DeliveryDate == boundItem.DeliveryDate
&& x.PM_Name == boundItem.PM_Name
&& x.Type == boundItem.Type
&& (x.Coupon == coupon || coupon == -1));
// Sum all coupons if this is the totals column
if (items == null
return Binding
.DoNothing;
(isQuantityView)
result = items.Sum(x => x.Qty);
(items.Any() && items.First().AcctMarketValue != 0)
result = items.Sum(x => x.MktValue) / items.First().AcctMarketValue;
//MultiBinding heatMap = new MultiBinding { Converter = new Converters.HeatMapConverter() };
//heatMap.Bindings.Add(new Binding { Source = result });
//heatMap.Bindings.Add(new Binding { Source = minValue });
//heatMap.Bindings.Add(new Binding { Source = maxValue });
//Style backgroundStyle = new Style();
//backgroundStyle.Setters.Add(new Setter { Property = System.Windows.Controls.Control.BackgroundProperty, Value = heatMap });
//field.Settings.CellValuePresenterStyle = backgroundStyle;
result;
public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo
throw new NotImplementedException
Accounts may be repeated on multiple rows becuse each security type is on its own row.
This class ensures the account market value for each account is counted once only.
public class MarketValueSummaryCalculator :
SumSummaryCalculator
static MarketValueSummaryCalculator
_Calculator;
public static MarketValueSummaryCalculator
Calculator
get
if (_Calculator == null
_Calculator =
new MarketValueSummaryCalculator
private List<decimal
> SummedAccounts;
MarketValueSummaryCalculator()
SummedAccounts =
new List<decimal
public override void BeginCalculation(SummaryResult
summaryResult)
SummedAccounts.Clear();
base
.BeginCalculation(summaryResult);
public override void Aggregate(object dataValue, SummaryResult summaryResult, Record
record)
ExposureItem item = (ExposureItem)((DataRecord
)record).DataItem;
(SummedAccounts.Where(x => x == item.AcctNo).Any())
SummedAccounts.Add(item.AcctNo);
.Aggregate(dataValue, summaryResult, record);
public override string
Name
get { return "MarketValueSummaryCalculator"
Summs MarketValue / AccountMarketValue
public class PctMarketValueSummaryCalculator :
static PctMarketValueSummaryCalculator
public static PctMarketValueSummaryCalculator
private decimal
MktValue;
AcctMktValue;
PctMarketValueSummaryCalculator()
MktValue += (
Convert
.ToDecimal(dataValue) * item.AcctMarketValue);
(!SummedAccounts.Where(x => x == item.AcctNo).Any())
AcctMktValue += item.AcctMarketValue;
public override object EndCalculation(SummaryResult
AcctMktValue == 0 ? 0 : MktValue / AcctMktValue;
get { return "PctMarketValueSummaryCalculator"
<
common:BaseUserControl x:Class
="Poolman.Controls.ExposureMatrixAccountGrid"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:igDP
="http://infragistics.com/DataPresenter"
xmlns:xamSpin
="clr-namespace:xamlSpinnersWPF;assembly=BlackSpike.XamSpinner"
xmlns:common="clr-namespace:Poolman.Common"
xmlns:views
="clr-namespace:Poolman.Views"
xmlns:controls
="clr-namespace:Poolman.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth
="300">
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type common:BaseUserControl
}}}" >
<igDP:XamDataGrid x:Name
="Grid1"
Grid.Row
="0"
FieldLayoutInitialized
="Grid1_FieldLayoutInitialized"
InitializeRecord
="Grid1_InitializeRecord"
Loaded
="Grid1_Loaded"
ScrollViewer.HorizontalScrollBarVisibility
="Auto"
DataSource="{Binding GroupedItems
}"
GroupByAreaLocation
="None">
<igDP:XamDataGrid.FieldSettings
>
<igDP:FieldSettings AllowEdit="False"
SummaryDisplayArea
="BottomFixed"
AllowRecordFiltering
="True"
FilterLabelIconDropDownType
="MultiSelectExcelStyle"
CellClickAction
="SelectRecord"/>
</igDP:XamDataGrid.FieldSettings
<igDP:XamDataGrid.FieldLayoutSettings
<igDP:FieldLayoutSettings
AllowDelete
="False"
AllowAddNew
AllowFieldMoving
="WithinLogicalRow"
AutoGenerateFields
HighlightAlternateRecords
RecordSelectorLocation
="None"
FilterUIType
="LabelIcons"/>
</igDP:XamDataGrid.FieldLayoutSettings
<igDP:XamDataGrid.FieldLayouts
<igDP:FieldLayout
<igDP:FieldLayout.SortedFields
<igDP:FieldSortDescription
Direction="Ascending"
FieldName
="ACCT_NO"/>
</igDP:FieldLayout.SortedFields
<igDP:FieldLayout.Fields
<igDP:Field Name="AcctNo" Label="Acct" FixedLocation="FixedToNearEdge" Width
="60">
<igDP:Field.Settings
<igDP:FieldSettings EditorStyle="{StaticResource PoolmanDecimalStyle
}"/>
</igDP:Field.Settings
</igDP:Field
<igDP:Field Name="PM_Name" Label="PM" FixedLocation
="FixedToNearEdge" />
<igDP:Field Name="Collateral" Label="Coll." Width="60" FixedLocation
="FixedToNearEdge"/>
<igDP:Field Name="Type" Label="Type" Width="60" FixedLocation
<igDP:Field Name="AcctMarketValue" Label
="MV">
<igDP:FieldSettings EditorStyle="{StaticResource AmountStyle
<igDP:Field Name="DeliveryDate" Label
="Dlvry Date">
</igDP:FieldLayout.Fields
<igDP:FieldLayout.SummaryDefinitions
<igDP:SummaryDefinition SourceFieldName="AcctMarketValue"
Calculator="{x:Static controls:MarketValueSummaryCalculator
.Calculator}"
StringFormat
="{}{0:#,#;(#,#)}" />
<igDP:SummaryDefinition SourceFieldName="PctMktValue"
="Avg"
="{}{0:#,#;(#,#)}"/>
</igDP:FieldLayout.SummaryDefinitions
</igDP:FieldLayout
</igDP:XamDataGrid.FieldLayouts
</igDP:XamDataGrid
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="216" Width="216" Grid.Row
Visibility="{Binding Path=LoadingData, Converter={StaticResource BoolToVisConverter
}}">
<xamSpin:ucSpinnerApple
<xamSpin:ucSpinnerApple.RenderTransform
<ScaleTransform ScaleX="9" ScaleY
="9"/>
</xamSpin:ucSpinnerApple.RenderTransform
</xamSpin:ucSpinnerApple
</Canvas
</Grid
</
common:BaseUserControl
Hi Stephan, when the list that is used as the datasource for this grid changes, the grid does not rebuild. So my question is how do I make the grid rebuild.
Thanks,
Hello Sam,
Since the List doesn't implement the INotifyCollectionChanged interface, it is expected that the XamDataGrid doesn't update its Records. My suggestion is to use an ObservableCollection, which implement this interface.
I am binding to an observable collection. I think this is a bug. I worked around it by taking my grid out of xaml and re-creating it each time in code behind.
Thank you for your feedback. I am glad that you resolved your issue and I believe that other community members may benefit from this as well. Sine I wasn't able to reproduce your issue, if you could send me an isolated sample project, where this is reproduced, I could be able to investigate it further for you.
Thanks again.
Hi Stefan, Please see the code you posted here: http://ko.infragistics.com/community/forums/p/72122/365596.aspx#365596
for an example. Click the set source button twice.
By the way I am curious why you are calling Dispatcher when you are already running on the UI thread (see http://social.msdn.microsoft.com/Forums/en/wpf/thread/14f7ca5e-fb1c-433e-94d4-269c3ddcedec).
Hello,
I can say that if you click the "Set Source" button twice, it is expected that the "LOADING" screen stays visible the second time, because the FieldLayoutInitilaized event doesn't fire, where I set the Visibility of the "Loading" screen to Collapsed. It doesn't fire, because the layout is already initialized. As to the dispatcher, I use it in order to delay the setting of the DataSource, because it is a CPU consuming operation and if it comes imediately after setting the Visibility of the Loading screen, the screen cannot be rendered and it doesn't show at all.
Hope this helps you.
>>It doesn't fire, because the layout is already initialized
Thank you Stefan, but that does not sound right - how can the field layout be initialized when the datasource has changed? In our case we are adding columns based on the data itself. I have worked around this by creating the grid in code behind so this is not an urgent problem however I am interested to know why the layout initialized event does not fire again.
I am just checking if you require any further assistance on the matter.
I have been looking into your last post and ‘FieldLayoutInitialized’ event fires only the first time when the FieldLayout of the XamDataGrid is created. This event does not fire when you change the data of the control. For more information :
http://help.infragistics.com/NetAdvantage/WPF/2012.1/CLR4.0/?page=InfragisticsWPF4.DataPresenter.v12.1~Infragistics.Windows.DataPresenter.DataPresenterBase~FieldLayoutInitialized_EV.html
If you would like to hence the change of the FieldLayout, I can suggest you use the ‘LayoutUpdated’ event of the XamDataGrid checking whether the DataSource is null because this event fires even if the XamDataGrid is empty when you start the application :
private void xamDataGrid1_LayoutUpdated(object sender, EventArgs e)
if(this.xamDataGrid1.DataSource != null)
// your code
If you have any other questions on this matter, feel free to ask.