I have a Bar chart on Kentico reporting section. And I know Kentico uses Microsoft Chart Controls. Microsoft Chart controls have the capability of creating a trending line on Bar graph - But I do see any option how I can utilize those on Kentico Reporting.
Is there any option there on reporting tool to get this trending line ?
If there is no option can anybody suggest anything else ?
Using custom module is the last option for me to try. If anybody has any specific suggestion regarding this custom module, please share that, too.
I am using Kentico 7.
Got it working the way Brend suggested however the mean is not coming up
ChartArea area = graph.ChartControl.ChartAreas[chartAreas - 1];
StripLine line = new StripLine();
// Set threshold line so that it is only shown once
line.Interval = 0;
// Set the threshold line to be drawn at the calculated mean of the first series
line.IntervalOffset = graph.ChartControl.DataManipulator.Statistics.Mean(graph.ChartControl.Series[0].Name);
line.BackColor = System.Drawing.Color.DarkGreen;
line.StripWidth = 0.25;
// Set text properties for the threshold line
//line.Text = "Mean";
line.ForeColor = System.Drawing.Color.Black;
// Add strip line to the chart
area.AxisY.StripLines.Add(line);
Also, for other trend line I am using bellow code, again having no luck as it looks like the datapoints are not set at the point where my code runs :
int chartAreas = graph.ChartControl.ChartAreas.Count;
if (chartAreas > 0)
{
graph.ChartControl.Series.Add("TrendLine");
graph.ChartControl.Series["TrendLine"].ChartType = SeriesChartType.Line;
graph.ChartControl.Series["TrendLine"].BorderWidth = 3;
graph.ChartControl.Series["TrendLine"].Color = System.Drawing.Color.Red;
// Line of best fit is linear
string typeRegression = "Linear";//"Exponential";//
// The number of days for Forecasting
string forecasting = "1";
// Show Error as a range chart.
string error = "false";
// Show Forecasting Error as a range chart.
string forecastingError = "false";
// Formula parameters
string parameters = typeRegression + ',' + forecasting + ',' + error + ',' + forecastingError;
graph.ChartControl.Series[0].Sort(PointSortOrder.Ascending, "X");
// Create Forecasting Series.
graph.ChartControl.DataManipulator.FinancialFormula(FinancialFormula.Forecasting, parameters, graph.ChartControl.Series[0], graph.ChartControl.Series["TrendLine"]);
}
The actual issue, I guess, is not having the graph.ChartControl.Series[0] at the place I am running my TrendLine generation code. Any idea how can I get it ?
Report charts are rendered through \CMSModules\Reporting\Controls\ReportGraph.ascx
You can modify the method GetReportGraph and add additional setup to the chart control ucChart based on some condition, e.g. the report name and chart name (you will have to hardcode that)
Note that you will need to modify Kentico code directly, so keep the changes at the lowest possible level, I recommend:
Put the extra setup code to an external class
Call it with just one extra line of code
Add comment to mark that extra line of code as customization
e.g.:
/* YourCompany */
MyChartExtender.ExtendChart(ucChart, ...);
/* YourCompany end */
Make sure you note that change for future upgrades
I've modified the controls before and you can use this code in the GetReportGraph() method just before enabling the subscription.
// apply the trendline
if (TrendValue > 0)
{
int chartAreas = graph.ChartControl.ChartAreas.Count;
if (chartAreas > 0)
{
ChartArea area = graph.ChartControl.ChartAreas[chartAreas - 1];
StripLine line = new StripLine();
line.IntervalOffset = TrendValue;
line.BorderColor = System.Drawing.ColorTranslator.FromHtml(TrendColor);
line.BackColor = System.Drawing.ColorTranslator.FromHtml(TrendColor);
line.StripWidth = TrendLineWidth;
line.ToolTip = TrendToolTip;
line.Text = TrendText;
line.TextLineAlignment = trendLineAlignment;
line.TextOrientation = TextOrientation.Horizontal;
line.TextAlignment = trendTextAlignment;
area.AxisY.StripLines.Add(line);
}
}
Of course you'll have to add the appropriate properties and pass the values through from the rest of the pages/controls using this control.
#region Trending
/// <summary>
/// Value for the single trendline for whole chart
/// </summary>
public int TrendValue
{
get
{
return mTrendValue;
}
set
{
mTrendValue = value;
}
}
/// <summary>
/// Color of the trend line in hex format (i.e. #0000FF)
/// </summary>
public string TrendColor
{
get
{
return mTrendColor;
}
set
{
mTrendColor = value;
}
}
/// <summary>
/// Tool tip of the trend line
/// </summary>
public string TrendToolTip
{
get
{
return mTrendToolTip;
}
set
{
mTrendToolTip = value;
}
}
/// <summary>
/// Text of the trend line
/// </summary>
public string TrendText
{
get
{
return mTrendText;
}
set
{
mTrendText = value;
}
}
/// <summary>
/// Trend line width
/// </summary>
public double TrendLineWidth
{
get
{
return mTrendLineWidth;
}
set
{
mTrendLineWidth = value;
}
}
string mTrendLineAlignment;
public string TrendLineAlignment
{
get
{
return mTrendLineAlignment;
}
set
{
mTrendLineAlignment = value;
}
}
private System.Drawing.StringAlignment trendLineAlignment
{
get
{
switch (TrendLineAlignment)
{
case "center":
return System.Drawing.StringAlignment.Center;
case "near":
return System.Drawing.StringAlignment.Near;
case "far":
return System.Drawing.StringAlignment.Far;
default:
return System.Drawing.StringAlignment.Near;
}
}
}
string mTrendTextAlignment;
public string TrendTextAlignment
{
get
{
return mTrendTextAlignment;
}
set
{
mTrendTextAlignment = value;
}
}
private System.Drawing.StringAlignment trendTextAlignment
{
get
{
switch (TrendTextAlignment)
{
case "center":
return System.Drawing.StringAlignment.Center;
case "near":
return System.Drawing.StringAlignment.Near;
case "far":
return System.Drawing.StringAlignment.Far;
default:
return System.Drawing.StringAlignment.Near;
}
}
}
#endregion
Related
I would like to have an image in column for boolean type. To specify column properties, I use OLVColumn attribute and there I specify ImageAspectName.
[OLVColumn("UseK", DisplayIndex = 4, ImageAspectName = "ImageForUseK", CheckBoxes = false, Width = 40)]
public bool UseK
{
get { return useK; }
set
{
if (useK == value) return;
useK = value;
OnPropertyChanged("UseK");
}
}
protected bool useK = true;
public string ImageForUseK()
{
return "success";
}
This works fine and displaying image in column, but also displaying literal "True" next to image.
How can I avoid displaying string value of boolean property, while still using ImageAspectName?
You could set AspectToStringFormat to a space.
In a project, I need to export a wall's original geometry to an IFC file. The so-called original geometry is the geometry of a wall without being cut by doors or windows hosted on the wall, without connections with roofs, floors, beams, columns, etc. The original geometry I wanted usually should be a shape like a box.
Unfortunately, there is no direct Revit API that gives me a wall's original geometry. The element.get_Geometry method returns the final geometry cut by doors, windows, and connected floors, roofs, etc.
One possible way to get a wall's original geometry is to rebuild geometry based on the wall's parameters myself, but my lazy method is to let Revit do the work. My method has five steps as follows:
Step 1: Start a Revit transaction.
Step 2: Before calling element.get_Geometry, temporarily delete the doors and windows hosted in the wall, as well as the roofs and floors connected with the wall, from the Revit document.
Step 3: Call document.Regenerate method to update the elements in the document. Of course, the wall's geometry should also be updated.
Step 4: Call element.get_Geometry to get the original geometry that I wanted.
Step 5: Rollback the transaction so that the Revit document remains unchanged.
The problem comes out in Step 2. Even if I had deleted the doors and windows, there are still openings in the returned geometry.
My question is, how to delete all elements related to a wall?
My Revit version is 2013. The .rvt file I used is rac_basic_sample_project.rvt shipped with Revit. The wall I want to export is the wall with id of 117698 or 117654.
My project is based on Revit IFC exporter source code.
The following is the code segment I used to get original geometry:
private GeometryElement GetOriginalWallGeometry2(Element element)
{
Document doc = element.Document;
GeometryElement geomElem = null;
//Step 1
using (Transaction t = new Transaction(doc))
{
//Step 2:
//delete wall joins
Autodesk.Revit.DB.Wall wall = element as Autodesk.Revit.DB.Wall;
//assert element is a wall
//the joined floors or roofs can be deleted as expected.
if (null != wall)
{
while (Autodesk.Revit.DB.WallUtils.IsWallJoinAllowedAtEnd(wall, 0))
{
Autodesk.Revit.DB.WallUtils.DisallowWallJoinAtEnd(wall, 0);
}
while (Autodesk.Revit.DB.WallUtils.IsWallJoinAllowedAtEnd(wall, 1))
{
Autodesk.Revit.DB.WallUtils.DisallowWallJoinAtEnd(wall, 1);
}
}
//The following code of deleting doors doesn't work as expected.
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> elementsList = collector.OfCategory(BuiltInCategory.OST_Doors).ToElements(); //here should be OST_Doors or others?
foreach (Element elem in elementsList)
{
try
{
doc.Delete(elem);
}
catch (System.Exception ex)
{
}
}
}
//The following code of deleting windows doesn't work as expected.
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> elementsList = collector.OfCategory(BuiltInCategory.OST_Windows).ToElements();//here should be OST_Windows or others?
foreach (Element elem in elementsList)
{
try
{
doc.Delete(elem);
}
catch (System.Exception ex)
{
}
}
}
//The following code also doesn't work as expected.
Autodesk.Revit.DB.HostObject hostObj = element as Autodesk.Revit.DB.HostObject;
if (hostObj != null)
{
IList<ElementId> idlist = hostObj.FindInserts(true, true, true, true);
foreach (ElementId id in idlist)
{
try
{
doc.Delete(id);
}
catch (System.Exception ex)
{
}
}
}
//Floors can be deteled as expected.
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> linkList = collector.OfCategory(BuiltInCategory.OST_Floors).ToElements();
foreach (Element elelink in linkList)
{
try
{
doc.Delete(elelink);
}
catch (System.Exception ex)
{
}
}
}
//Roofs can be deteled as expected.
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> linkList = collector.OfCategory(BuiltInCategory.OST_Roofs).ToElements();
foreach (Element elelink in linkList)
{
try
{
doc.Delete(elelink);
}
catch (System.Exception ex)
{
}
}
}
//Step 3
doc.Regenerate();
//Step 4
Options options;
View ownerView = element.Document.GetElement(element.OwnerViewId) as View;
if (ownerView == null)
{
options = GeometryUtil.GetIFCExportGeometryOptions();
}
else
{
options = new Options();
options.View = ownerView;
}
geomElem = element.get_Geometry(options);
//Step 5
FailureHandlingOptions failureOptions = t.GetFailureHandlingOptions();
failureOptions.SetClearAfterRollback(true);
failureOptions.SetDelayedMiniWarnings(true);
t.SetFailureHandlingOptions(failureOptions);
try
{
t.RollBack();
}
catch (System.Exception ex)
{
}
}
return geomElem;
}
I use the following method to retrieve elements of a particular category.
/// <summary>
/// Get all elements of the specified type that fall into the specified category
/// <para>The specified type must derive from Element, or you can use Element but you get everything :)</para>
/// </summary>
/// <typeparam name="T">The type of element to get</typeparam>
/// <param name="builtInCategory">The BuiltinCategory to discriminate the element set</param>
/// <returns>The collection of elements that match the type and specified categry</returns>
public IEnumerable<T> GetElements<T>(BuiltInCategory builtInCategory) where T : Element
{
FilteredElementCollector collector = new FilteredElementCollector(Document);
// Seems you must be a subclass of element to use the OfClass method
if (typeof(T) != typeof(Element))
collector.OfClass(typeof(T));
collector.OfCategory(builtInCategory);
return collector.Cast<T>();
}
If you are trying to get doors and windows then it would be used as such
var doors = GetElements<FamilyInstance>(BuiltInCategory.OST_DOORS);
var windows = GetElements<FamilyInstance>(BuiltInCategory.OST_WINDOWS);
That presumes that the openings you are looking for are doors or windows.
If you are looking for void extrusions, etc, within the wall or other types of openings then you will need to be more specific in your question.
As a more complex version of the function shown above I use the following method when I wish to also apply a filter against the elements being retrieved.
/// <summary>
/// Get the collection of elements of the specified type that are within the provided category that also pass the filter.
/// <para>The specified type must derive from Element, or you can use Element but you get everything :)</para>
/// </summary>
/// <typeparam name="T">The type of element to get</typeparam>
/// <param name="builtInCategory">The BuiltinCategory to discriminate the element set</param>
/// <param name="filter">The filter to check the element against</param>
/// <returns>The collection of elements of the specified type and specified category that pass the filter</returns>
public IEnumerable<T> GetElements<T>(BuiltInCategory builtInCategory, ElementFilter filter) where T : Element
{
FilteredElementCollector collector = new FilteredElementCollector(Document);
// Seems you must be a subclass of element to use the OfClass method
if (typeof(T) != typeof(Element))
collector.OfClass(typeof(T));
collector.OfCategory(builtInCategory);
collector.WherePasses(filter);
return collector.Cast<T>();
}
The advantage of using the method approach defined above is that it will isolate your code from Revit API changes in the future rather than using the same code blocks repeatedly throughout your code base.
If you want to retrieve Windows, Doors and any other opening on the wall, this is the right code:
var ids = (yourCurrentWallElement as Wall).FindInserts(true, true, true, true);
foreach (ElementId id in ids)
{
var el = doc.GetElement(id);
Debug.WriteLine(" Id: " + el.Id);
Debug.WriteLine(" Type: " + el.GetType().Name);
Debug.WriteLine(" Category: " + el.Category.Name);
Debug.WriteLine(" Type: " + el.GetType().Name);
if (el is FamilyInstance)
{
var fi = el as FamilyInstance;
if (fi != null)
Debug.WriteLine(" Symbol Name: " + fi.Symbol.Name);
}
}
More info on the FindInserts -> http://revitapisearch.com/html/58990230-38cb-3af7-fd25-96ed3215a43d.htm
Other example -> http://spiderinnet.typepad.com/blog/2012/04/get-wall-inserts-using-the-revit-wallfindinserts-net-api.html
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
XNA - File not found problem
Here I am trying to load a Round.png file in windows phone 7 application project. I don't know how to load this image during run time. I am really sorry if this is a silly question as i m a newbie in windows app development. Please help...
Thanks in advance!!!
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D texRound;
Rectangle HitRegion;
bool isSelected = false;
TouchCollection touches = TouchPanel.GetState();
//start position of round, in the center of screen
int positionX = 400;
int positionY = 240;
//random number Axis X and Y
Random randomX;
Random randomY;
//the range for random number of start and end of X, Y
int startX, endX;
int startY, endY;
//total time
float milliseconds = 0f;
//score count
int count = 0;
//game font
SpriteFont font;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
// Extend battery life under lock.
InactiveSleepTime = TimeSpan.FromSeconds(1);
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
texRound = Content.Load<Texture2D>("Round");
randomX = new Random();
randomY = new Random();
// The X axis bound range of touch for ball
startX = texRound.Width;
endX = GraphicsDevice.Viewport.Width - texRound.Width;
// The X axis bound range of touch for ball
startY = texRound.Height;
endY = GraphicsDevice.Viewport.Height - texRound.Height;
// Define the HitRegion of ball in the middle of touchscreen
HitRegion = new Rectangle(positionX - texRound.Width / 2,
positionY - texRound.Height / 2, texRound.Width,
texRound.Height);
// Load the font definition file
font = Content.Load<SpriteFont>("gamefont");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
// Accumulate the elapsed milliseconds every frame
milliseconds +=
(float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (milliseconds > 1000)
{
// When the milliseconds greater than 1000 milliseconds,
// randomly locate a new position for the ball
HitRegion.X = randomX.Next(startX, endX + 1);
HitRegion.Y = randomY.Next(startY, endY + 1);
// Reset the milliseconds to zero for new milliseconds
// count
// make the ball not been selected
milliseconds = 0f;
if (isSelected)
isSelected = false;
}
base.Update(gameTime);
Point touchPoint = new Point((int)touches[0].Position.X, (int)touches[0].Position.Y);
if (HitRegion.Contains(touchPoint))
{
isSelected = true;
count++;
}
else
{
isSelected = false;
}
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Green);
// TODO: Add your drawing code here
spriteBatch.Begin();
if (isSelected)
{
spriteBatch.Draw(texRound, HitRegion, Color.Red);
}
else
{
spriteBatch.Draw(texRound, HitRegion, Color.White);
}
spriteBatch.DrawString(font, "Score:" + count.ToString(),
new Vector2(0f, 0f), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
The path in the inner exception looks correct: \"Content\\Round.xnb\", the extra slashes are just because it's escaped. This is a relative path from your executable, be default it's something like: Visual Studio Projects\MyCoolGame\MyCoolGame\bin\x86\Debug\Content\Round.xnb. You should be comparing this path to the the real path to the Round.xnb file, not the png.
If Round.xnb does not exist anywhere, then your png file isn't part of Content project, or the Content project isn't being built
If the file does exist, but is in a subdirectory, specify the name of the subdirectory when you load the texture like: Content.Load<Texture2D>("myDir\\Round");
Since the inner exception isn't looking for "Round.png.xnb", you have your names up correctly and #Ricket's answer won't help you any further.
I'm assuming you correctly put the Round.png file in your Content directory (or project) and it's being compiled properly, as per default settings.
Right-click the file and open its properties. Check the "Name" field. That's what Content.Load<> is expecting. So for example, if you drag a file called "Circular.png" into the Content folder, then it will be auto-named "Circular", but if you rename the file to "Round.png", typically it will remain named "Circular", leaving you to manually change the name in the properties and update all references in your code.
I've got some C# code like this (class file = Status.cs):
/// <summary>
/// Constructs a status entity with the text specified
/// </summary>
/// <param name="someParameter">Some parameter.</param>
public Status(string someParameter)
{
SomeProperty = someParameter;
}
/// <summary>
/// An example of a virtual property.
/// </summary>
public virtual string SomeProperty { get; private set; }
And I want to do 3 things to it:
perform a Resharper "to property with backing field" on it
get rid of the "private set" and replace it with a regular "set"
change the constructor so it initializes the private field instead of the property
So the end result would look like this:
/// <summary>
/// Constructs a status entity with the text specified
/// </summary>
/// <param name="someParameter">Some parameter.</param>
public Status(string someParameter)
{
_someProperty = someParameter;
}
private string _someProperty;
/// <summary>
/// An example of a virtual property.
/// </summary>
public virtual string SomeProperty
{
get { return _someProperty; }
set { _someProperty = value; }
}
And my question is: Is there a way to automate this type of refactoring using, say, the Resharper API ?
Background:
For those who might wonder why I want to do this, it's because:
I'm upgrading NHibernate (old=2.1.2.4000, new=3.3.1.4000) and Fluent NHibernate (old=1.1.0.685, new=1.3.0.0).
I've got rid of the old NHibernate.ByteCode.Castle.dll and the corresponding line in the config file, so I can now use the default proxy that's built into the latest NHibernate.
Between the new implementation of NHibernate and the new version of Fluent, there seem to be two problems when I try to build and run unit tests (part of this is FxCop complaining, but anyway):
a) exceptions are thrown because of the "private set", and
b) exceptions are thrown because the virtual property is being initialized in the constructor.
So I found that if I make those changes, it compiles and the unit tests pass.
So that's fine for a class or two, but there are over 800 class files and who knows how many properties.
I'm sure there are lots of ways to do this (e.g. using reflection, recursing through the directories and parsing the files etc), but it seems like Resharper is the proper tool for something like this.
Any help appreciated, thank you, -Dave
--In response to the answer saying "just change it to a protected set and you're done":
Unfortunately it’s not that simple.
Here is the error that occurs on running unit tests (before making any changes to any class):
Test method threw exception:
NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
.Status: method set_StatusText should be 'public/protected virtual' or 'protected internal virtual'
..Status: method set_Location should be 'public/protected virtual' or 'protected internal virtual'
So if I change the class as suggested (where the only change is to change the “private set” to a “protected set”), the project will not build, because:
Error 2 CA2214 : Microsoft.Usage : 'Status.Status(string, StatusLocation)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences:
Status..ctor(String, StatusLocation)
Status.set_Location(StatusLocation):Void C:\\Status.cs 28
That is why it is also necessary to change any statement in the constructor which initializes one of these virtual properties.
The previous implementation of the NHibernate proxy (ByteCode.Castle) did not seem to care about the “private set”, whereas this one does.
And, admittedly, the second error is because of FxCop, and I could just put an attribute on the class to tell FxCop not to complain about this, but that seems to just be making the problem go away, and initializing virtual properties in constructors is, as I understand it, bad practice anyway.
So my question still stands. The changes I initially described are the changes I would like to make. How can I automate these changes?
I went ahead and wrote a C# utility to parse such a class file. The "protected set" idea is valid to a point (thanks Hazzik), but it still needs a backing field. The below code produces the output I described above (except uses a "protected set"). Regards, -Dave
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
// TODO: write recursive algorithm to loop through directories
// TODO: handle generic collections as Fluent NHibernate treats those differently
class Program
{
public static string ConvertInitialCapitalToUnderscoreAndLowerCase(string input)
{
var firstChar = input.Substring(0, 1);
var restOfStmt = input.Substring(1);
var newFirst = "_" + firstChar.ToLower();
var output = newFirst + restOfStmt;
return output;
}
// this gets any tabs or spaces at the beginning of the line of code as a string,
// so as to preserve the indentation (and/or add deeper levels of indentation)
public static string GetCodeLineIndentation(string input)
{
var charArray = input.ToCharArray();
var sbPrefix = new StringBuilder();
foreach (var c in charArray)
{
// if it's a tab or a space, add it to the "prefix"
if (c == 9 || c == ' ')
{
sbPrefix.Append(c);
}
else
{
// get out as soon as we hit the first ascii character (those with a value up to 127)
break;
}
}
return sbPrefix.ToString();
}
static void Main(string[] args)
{
const string path = #"C:\pathToFile\Status.cs";
Console.WriteLine("Examining file: " + path);
if (!File.Exists(path))
{
Console.WriteLine("File does not exist: " + path);
throw new FileNotFoundException(path);
}
// Read the file.
var arrayOfLines = File.ReadAllLines(path);
// Convert to List<string>
var inputFileAsListOfStrings = new List<string>(arrayOfLines);
// See if there are any virtual properties.
var virtualProps = inputFileAsListOfStrings.Where(s => s.Contains("public virtual")).ToList();
// See if there are any "private set" strings.
var privateSets = inputFileAsListOfStrings.Where(s => s.Contains("private set")).ToList();
if (virtualProps.Count > 0)
{
Console.WriteLine("Found " + virtualProps.Count + " virtual properties in the class...");
}
if (privateSets.Count > 0)
{
Console.WriteLine("Found " + privateSets.Count + " private set statements in the class...");
}
// Get a list of names of the virtual properties
// (the 4th "word", i.e. index = 3, in the string, will be the property name,
// e.g. "public virtual string SomePropertyName"
var virtualPropNames = virtualProps.Select(vp => vp.Trim().Split(' ')).Select(words => words[3]).ToList();
if (virtualPropNames.Count() != virtualProps.Count())
{
throw new Exception("Error: the list of virtual property names does not equal the number of virtual property statements!");
}
// Find all instances of the virtual properties being initialized.
// By examining the overall file for instances of the virtual property name followed by an equal sign,
// we can identify those lines which are statements initializing the virtual property.
var initializeStatements = (from vpName in virtualPropNames
from stmt in inputFileAsListOfStrings
let stmtNoSpaces = stmt.Trim().Replace(" ", "")
where stmtNoSpaces.StartsWith(vpName + "=")
select stmt).ToList();
if (initializeStatements.Count() > 0)
{
Console.WriteLine("Found " + initializeStatements.Count + " initialize statements in the class...");
}
// now process the input based on the found strings and write the output
var outputFileAsListOfStrings = new List<string>();
foreach (var inputLineBeingProcessed in inputFileAsListOfStrings)
{
// is the input line one of the initialize statements identified previously?
// if so, rewrite the line.
// e.g.
// old line: StatusText = statusText;
// becomes: _statusText = statusText;
var isInitStmt = false;
foreach (var initStmt in initializeStatements)
{
if (inputLineBeingProcessed != initStmt) continue;
// we've found our statement; it is an initialize statement;
// now rewrite the format of the line as desired
var prefix = GetCodeLineIndentation(inputLineBeingProcessed);
var tabAndSpaceArray = new[] {' ', '\t'};
var inputLineWithoutPrefix = inputLineBeingProcessed.TrimStart(tabAndSpaceArray);
var outputLine = prefix + ConvertInitialCapitalToUnderscoreAndLowerCase(inputLineWithoutPrefix);
// write the line (preceded by its prefix) to the output file
outputFileAsListOfStrings.Add(outputLine);
Console.WriteLine("Rewrote INPUT: " + initStmt + " to OUTPUT: " + outputLine);
isInitStmt = true;
// we have now processed the input line; no need to loop through the initialize statements any further
break;
}
// if we've already determined the current input line is an initialize statement, no need to proceed further;
// go on to the next input line
if (isInitStmt)
continue;
// is the input line one of the "public virtual SomeType SomePropertyName" statements identified previously?
// if so, rewrite the single line as multiple lines of output.
// the input will look like this:
/*
public virtual SomeType SomePropertyName { get; set; }
*/
// first, we'll need a private variable which corresponds to the original statement in terms of name and type.
// what we'll do is, write the private field AFTER the public property, so as not to interfere with the XML
// comments above the "public virtual" statement.
// the output will be SIX LINES, as follows:
/*
public virtual SomeType SomePropertyName
{
get { return _somePropertyName; }
protected set { _someProperty = value; }
}
private SomeType _somePropertyName;
*/
var isPublicVirtualStatement = false;
foreach (var vp in virtualProps)
{
if (inputLineBeingProcessed != vp) continue;
// the input line being processed is a "public virtual" statement;
// convert it into the six line output format
var thisOutputList = new List<string>();
// first separate any indentation "prefix" that may exist (i.e. tabs and/or spaces),
// from the actual string of text representing the line of code
var prefix = GetCodeLineIndentation(inputLineBeingProcessed);
var tabAndSpaceArray = new[] { ' ', '\t' };
var inputLineWithoutPrefix = inputLineBeingProcessed.TrimStart(tabAndSpaceArray);
var originalVpStmt = inputLineWithoutPrefix.Split(' ');
// first output line (preceded by its original prefix)
var firstOutputLine = prefix +
originalVpStmt[0] + ' ' +
originalVpStmt[1] + ' ' +
originalVpStmt[2] + ' ' +
originalVpStmt[3];
thisOutputList.Add(firstOutputLine);
// second output line (indented to the same level as the original prefix)
thisOutputList.Add(prefix + "{");
// get field name from property name
var fieldName = ConvertInitialCapitalToUnderscoreAndLowerCase(originalVpStmt[3]);
// third output line (indented with the prefix, plus one more tab)
var thirdOutputLine = prefix + "\t" + "get { return " + fieldName + "; }";
thisOutputList.Add(thirdOutputLine);
// fourth output line (indented with the prefix, plus one more tab)
var fourthOutputLine = prefix + "\t" + "protected set { " + fieldName + " = value; }";
thisOutputList.Add(fourthOutputLine);
// fifth output line (indented to the same level as the first curly bracket)
thisOutputList.Add(prefix + "}");
// sixth output line (the "index 2" value of the original statement will be the string representing the .Net type)
// (indentation is the same as the "public virtual" statement above)
var sixthOutputLine = prefix +
"private" + ' ' +
originalVpStmt[2] + ' ' +
fieldName + ";";
thisOutputList.Add(sixthOutputLine);
// now write the six new lines to the master output list
outputFileAsListOfStrings.AddRange(thisOutputList);
isPublicVirtualStatement = true;
Console.WriteLine("Rewrote INPUT: " + inputLineBeingProcessed + " to OUTPUT: <multi-line block>");
break;
}
// if we've already determined the current input line is a "public virtual" statement, no need to proceed further;
// go on to the next input line
if (isPublicVirtualStatement)
continue;
// if we've gotten this far, the input statement is neither an "initialize" statement, nor a "public virtual" statement;
// So just write the output. Don't bother logging this as most lines will not be ones we'll process.
outputFileAsListOfStrings.Add(inputLineBeingProcessed);
}
// write the output file
var newPath = path.Replace(".cs", "-NEW.cs");
File.WriteAllLines(newPath, outputFileAsListOfStrings);
}
}
i'm having some issues getting the .Filter() method to work in subsonic, and i'm constantly getting errors like the one below:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Line 36: bool remove = false;
Line 37: System.Reflection.PropertyInfo pi = o.GetType().GetProperty(w.ColumnName);
Line 38: if (pi.CanRead)
Line 39: {
Line 40: object val = pi.GetValue(o, null);
i'm making calls like the one below- is this the corrent way to use it? There seems to be no documentation on the use of this method
NavCollection objTopLevelCol = objNavigation.Where(Nav.Columns.NavHigherID,Comparison.Equals, 0).Filter();
thanks in advance
The value you filter against needs to be the Property Name, not the database column name.
You might try this:
lCol = objNavigation.Where(Nav.HigherIDColumn.PropertyName,Comparison.Equals, 0).Filter();
Or here's a slightly more verbose method that works for me based on custom overrides of the .Filter() method. It seemed to work better (for me at least) by explicitly creating the Where beforehand:
SubSonic.Where w = new SubSonic.Where();
w.ColumnName = Nav.HigherIDColumn.PropertyName;
w.Comparison = SubSonic.Comparison.NotIn;
w.ParameterValue = new string[] { "validvalue1", "validvalue2" };
lCol = objNavigation.Filter(w, false);
Here's the overrides:
/// <summary>
/// Filters an existing collection based on the set criteria. This is an in-memory filter.
/// All existing wheres are retained.
/// </summary>
/// <returns>NavCollection</returns>
public NavCollection Filter(SubSonic.Where w)
{
return Filter(w, false);
}
/// <summary>
/// Filters an existing collection based on the set criteria. This is an in-memory filter.
/// Existing wheres can be cleared if not needed.
/// </summary>
/// <returns>NavCollection</returns>
public NavCollection Filter(SubSonic.Where w, bool clearWheres)
{
if (clearWheres)
{
this.wheres.Clear();
}
this.wheres.Add(w);
return Filter();
}
/// <summary>
/// Filters an existing collection based on the set criteria. This is an in-memory filter.
/// Thanks to developingchris for this!
/// </summary>
/// <returns>NavCollection</returns>
public NavCollection Filter()
{
for (int i = this.Count - 1; i > -1; i--)
{
Nav o = this[i];
foreach (SubSonic.Where w in this.wheres)
{
bool remove = false;
System.Reflection.PropertyInfo pi = o.GetType().GetProperty(w.ColumnName);
if (pi != null && pi.CanRead)
{
object val = pi.GetValue(o, null);
if (w.ParameterValue is Array)
{
Array paramValues = (Array)w.ParameterValue;
foreach (object arrayVal in paramValues)
{
remove = !Utility.IsMatch(w.Comparison, val, arrayVal);
if (remove)
break;
}
}
else
{
remove = !Utility.IsMatch(w.Comparison, val, w.ParameterValue);
}
}
if (remove)
{
this.Remove(o);
break;
}
}
}
return this;
}
And SubSonic 2.0 doesn't actually support In/NotIn for the IsMatch function, so here's the customized version that does (in SubSonic\Utility.cs):
public static bool IsMatch(SubSonic.Comparison compare, object objA, object objB)
{
if (objA.GetType() != objB.GetType())
return false;
bool isIntegerVal = (typeof(int) == objA.GetType());
bool isDateTimeVal = (typeof(DateTime) == objA.GetType());
switch (compare)
{
case SubSonic.Comparison.In:
case SubSonic.Comparison.Equals:
if (objA.GetType() == typeof(string))
return IsMatch((string)objA, (string)objB);
else
return objA.Equals(objB);
case SubSonic.Comparison.NotIn:
case SubSonic.Comparison.NotEquals:
return !objA.Equals(objB);
case SubSonic.Comparison.Like:
return objA.ToString().Contains(objB.ToString());
case SubSonic.Comparison.NotLike:
return !objA.ToString().Contains(objB.ToString());
case SubSonic.Comparison.GreaterThan:
if (isIntegerVal)
{
return ((int)objA > (int)objB);
}
else if (isDateTimeVal)
{
return ((DateTime)objA > (DateTime)objB);
}
break;
case SubSonic.Comparison.GreaterOrEquals:
if (isIntegerVal)
{
return ((int)objA >= (int)objB);
}
else if (isDateTimeVal)
{
return ((DateTime)objA >= (DateTime)objB);
}
break;
case SubSonic.Comparison.LessThan:
if (isIntegerVal)
{
return ((int)objA < (int)objB);
}
else if (isDateTimeVal)
{
return ((DateTime)objA < (DateTime)objB);
}
break;
case SubSonic.Comparison.LessOrEquals:
if (isIntegerVal)
{
return ((int)objA <= (int)objB);
}
else if (isDateTimeVal)
{
return ((DateTime)objA <= (DateTime)objB);
}
break;
}
return false;
}
IF you're using .net 3.5 you could just do this with a lambda function:
NavCollection objTopLevelCol =
objNavigation.Where(nav => nav.NavHigherID == 0);
Filter is designed to work on a collection - is "objNavigation" a collection? The problem you're running into is that the criteria for Filter() can't be met with the column name "NavHigherID".
I was having same prob, try doing your filter like this :
lCol = objNavigation.Where("NavHigherID",Comparison.Equals, 0).Filter();