Is it possible to add a measure dynamically bypassing th data source altogether.
For example, If an Excel file is loaded in the cube and displayed in the grid. Is it possilble to have the user create a new measure and added into the collection of measure. This measure will be essentialy a sum and a product of two other measure. Also, we want to display it right next to other measure in the grid.
Thanks.
Hello,
Yes you can do that using your own aggregator. Here is the sample code:
1. Create and add a cube metadata to the flat data source
CubeMetadata cubeMetadata = new CubeMetadata { DataTypeFullName = "ExcelData", DisplayName = "My excel cube" };
flatDataSource.CubesSettings.Add(cubeMetadata);
2. Create and add a dimension metadata to the cube metadata
DimensionMetadata calcMeasureMetadata = new DimensionMetadata
{
SourcePropertyName = "Cost",
DimensionType = DimensionType.Measure,
DisplayName = "Final Price",
Aggregator = new CustomMeasureAggregator()
};
cubeMetadata.DimensionSettings.Add(calcMeasureMetadata);
3. Define your own aggregator:
public class CustomMeasureAggregator : Aggregator<double>
public override IAggregationResult<double, double> Evaluate(IAggregationResult<double, double> oldResult, IAggregateable aggregateable, IEnumerable items)
double result = 0;
IList<object> dataItems = items.Cast<DataRowMetadata>().Select(drm => drm.DataObject).ToList();
if (dataItems.Count > 0)
Type itemsType = dataItems[0].GetType();
PropertyInfo unitsPropertyInfo = itemsType.GetProperty("Cost");
Func<object, object> func = ReflectionHelper.CreatePropertyGetMethod(unitsPropertyInfo);
foreach (object dataItem in dataItems)
// you can use IAggregateable.GetValue(object) instead
// but it operates only over the property specified into DimensionMetadata.SourcePropertyName
/*result += (double)aggregateable.GetValue(dataItem);*/
result += (double)func(dataItem);
}
return new SingleResult<double>(result * 1.05);
public override IAggregationResult<double, double> Evaluate(IAggregationResult<double, double> oldResult, double value)
// you don't need this one
throw new NotImplementedException();
Because of the usage of excel file the items’ type is type which is dynamically generated. That’s why you need to use reflection in order to extract the values from DataRowMetadata.DataObject property. If your calculations are based over a single property you can use IAggregateable.GetValue(object). It has the same Func<object, object> defined internally and it returns the value of the property you have set as source property of DimensionMetadata.
Best regards.
PPilev.
Following this example, I can no longer see 'Cost' Measure in the measures in the data selector on the right..Is it the intended bahavior..? What If I still wante d to Raw Cost numbers too.
this is the screenshot before. I am using a test measure to replace it with the actual measure later. I will also port the 'after' snapshot
Column 'test' is hidden automatically and replaced with 'A New Measure' column which is sum of col1 and col2. Here is the code ...
private
FlatDataSource CreateDataSource(Stream fileStream)
{_flatDataSource = new FlatDataSource();
ExcelDataConnectionSettings excelDataSettings =new ExcelDataConnectionSettings { FileStream = fileStream, GeneratedTypeName = "ExcelData", WorksheetName = "MMExample" };
_flatDataSource.ConnectionSettings = excelDataSettings;
_flatDataSource.DimensionsGenerationMode =
DimensionsGenerationMode.Mixed;
_flatDataSource.Cube =
);
_flatDataSource.Rows =
DataSourceBase.GenerateInitialItems("[Asset Class].[Asset Class],[Trading Desk].[Trading Desk]"
_flatDataSource.Measures =
DataSourceBase
.GenerateInitialItems(_measures);
CubeMetadata cubeMetadata = new CubeMetadata()
DataTypeFullName =
"ExcelData"
,
DisplayName =
"ESPExample Data"
if
(_dynamicMeasureRequested)
CustomMeasureAggregator agg = new CustomMeasureAggregator
(_flatDataSource);
agg.ColumnNames = _columnNames;
agg.Operations = _operations;
DimensionMetadata calcMeasureMetadata = new
DimensionMetadata
SourcePropertyName =
"Test"
DimensionType =
DimensionType
.Measure,
DisplayFormat = _dynamicMeasureFormat,
DisplayName = _newDynamicMeasureName,
Aggregator = agg
_flatDataSource.CubesSettings.Add(cubeMetadata);
#region
Code will only work in version 11.1 - hierachry between year/months.
#endregion
) };
stringDataDescriptor.AddLevel<
string>(s => "All", "All Values"
string>(s => s, "Members"
_flatDataSource.HierarchyDescriptors.Add(stringDataDescriptor);
return
_flatDataSource;
What I have understood based on your snapshots and your code is as follows:
You have a column named Test and your calculated measure, named “A New Measure”, is based on this column but when the result is visualized you have lost the Test column. I don’t know the content of _measures variable you have used to populate the measures, so I’m not sure if you have tried to load it. I guess that there is no such measure Test created because you have not added metadata for it.
This is because if you want both the “Test” and “A New Measure” to exist you need to explicitly add a metadata for Test measure because the metadata of “A New Measure” has overridden the default one. If you put a XamPivotDataSelector control on your page you’ll be able to see the measures that are created for your current metadata configuration.
Adding this metadata item to the dimensions metadata collection should make the trick:
DimensionMetadata testMeasureMetadata = new DimensionMetadata
SourcePropertyName = "Test",
DisplayFormat = this._dynamicMeasureFormat,
DisplayName = "Test (Default)"
It does not work. It always hides itself. Should I zip up my project and attach.
If you have any concerns to put it in the public space please contact me through our developers support team.Thanks.
Hello Alex,
Please contact me through our developers support team, so we can look into your issue in details.Best regards.Plamen.
Hi Plamen,
I’ve decided to post a request for help on my next problem even though I am not sure it is directly related to the name of this (sub) forum. If it (my question) does not relate to what you are usually work on , could you pls refer me to a proper forum/specialist ?
Here is the thing:
Few weeks ago with your great help I was able to implement dynamic creation of "derived" columns and provide a logic for evaluation of expressions entered by the users for those columns. In fact those are not measures , but dimensions.
Recently I have started to get unhandled exceptions from InfragisticsWPF4.Olap.FlatData.v11.1.
Usually it happens when I try to drag another dimension from data selector into the grid . Here is the exception
///////////////////////////////////////////////////
System.IndexOutOfRangeException was unhandled
Index was outside the bounds of the array.InfragisticsWPF4.Olap.FlatData.v11.1
at Infragistics.Olap.FlatData.FlatDataModelProvider.CreateLevelMembers(IEnumerable`1 items, IMember parentMember, IHierarchy hierarchy, Int32 hierarchyIndex, HierarchyDescriptor hierarchyDescriptor, Int32 levelIndex, DimensionMetadata dimensionMetadata, String uniqueNamePart) at Infragistics.Olap.FlatData.FlatDataModelProvider.BuildMembers(IHierarchy hierarchy) at Infragistics.Olap.FlatData.FlatDataModelProvider.LoadMembersAsync(ICube cube, ILevel level) at Infragistics.Olap.FilterViewModelBase.LoadFilterMembersAsync() at Infragistics.Olap.FlatData.FlatDataSource.CreateFilterViewModel(IHierarchy hierarchy) at Infragistics.Controls.Grids.DropTriggerAction.AssociatedObjectDrop(Object sender, DropEventArgs e) at System.EventHandler`1.Invoke(Object sender, TEventArgs e) at Infragistics.DragDrop.DragSource.OnDrop(DropEventArgs args) at Infragistics.DragDrop.DragDropManager.OnDrop(MouseEventArgs mouseEventArgs) at Infragistics.DragDrop.DragDropManager.EndDrag(Boolean dragCancel) at Infragistics.DragDrop.DragDropManager.QueryContinueDrag(DependencyObject sender, Boolean escapePressed, Boolean mouseReleased) at Infragistics.DragDrop.DragDropManager.OnMouseLeftButtonUp(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args) at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted) at System.Windows.Input.InputManager.ProcessStagingArea() at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input) at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport) at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel) at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at System.Windows.Application.Run(Window window) at System.Windows.Application.Run() at Stealth.App.Main() in D:\dev\svn\repos\ares\trunk\Stealth.Apps.Ares\obj\x86\Debug\App.g.cs:line 0 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
{Void CreateLevelMembers(System.Collections.Generic.IEnumerable`1[Infragistics.Olap.FlatData.DataRowMetadata], Infragistics.Olap.Data.IMember, Infragistics.Olap.Data.IHierarchy, Int32, Infragistics.Olap.FlatData.HierarchyDescriptor, Int32, Infragistics.Olap.FlatData.DimensionMetadata, System.String)}
I understand that it is almost impossible to get to the root of teh problem with such info, but maybe it is known issue and there is a patch/workaround for it.
I have also mentioned that it usually happens with desterilized layout views. I use doe persisting a solution offered by Atanas Dyulgerov in his blog :
http://blogs.infragistics.com/blogs/atanas_dyulgerov/archive/2010/07/15/serializing-xampivotgrid-views-in-silverlight.aspx
Thanks a lot,
Have a nice weekend,
Alex
Thanks a lot Plamen,
Based on your example I was able to create virtual dimension without underlaying data source type property for it.
Thanks again,
Alex Nebogatov
Hi Alex,
Have a look at the attached sample. It covers different cases so refer to the comments in code. If you have any questions let me know.
Plamen.
Sorry - forgot to add it in the first place. As far as I understand - using measures is possible for numeric properties only . Am I right ?
Thanks,