Related
Hello I want to resize the screen aspect ratio according to the screen size through code but I couldn't find a way to do that:
public class ResolutionFixer : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
var res = Screen.currentResolution;
float ratio = (float)res.width/res.height;
print(ratio);
//change the game's ratio aspect to the give ratio here
}
Note: tried using:
Screen.SetResolution(res.width, res.height, true);
but it didn't work
According to this, I think you cannot emulate Screen.SetResolution() in Editor, Try building it and see if it works
Resizing the Game View through code is possible with some reflection.
This works for me:
using UnityEditor;
using System.Reflection;
public static class GameViewUtils {
public enum GameViewSizeType {
AspectRatio,
FixedResolution
}
private static readonly object gameViewSizesInstance;
private static readonly MethodInfo getGroup;
static GameViewUtils() {
var sizesType = typeof(Editor).Assembly.GetType("UnityEditor.GameViewSizes");
var singleType = typeof(ScriptableSingleton<>).MakeGenericType(sizesType);
var instanceProp = singleType.GetProperty("instance");
getGroup = sizesType.GetMethod("GetGroup");
gameViewSizesInstance = instanceProp.GetValue(null, null);
}
public static void AddCustomSize(GameViewSizeType viewSizeType, GameViewSizeGroupType sizeGroupType, int width, int height, string resolutionName) {
var group = GetGroup(sizeGroupType);
var addCustomSize = getGroup.ReturnType.GetMethod("AddCustomSize"); // or group.GetType().
var assembly = Assembly.Load("UnityEditor.dll");
var gameViewSize = assembly.GetType("UnityEditor.GameViewSize");
var gameViewSizeType = assembly.GetType("UnityEditor.GameViewSizeType");
var ctor = gameViewSize.GetConstructor(new [] {
gameViewSizeType,
typeof(int),
typeof(int),
typeof(string)
});
var newSize = ctor.Invoke(new object[] { (int)viewSizeType, width, height, resolutionName });
addCustomSize.Invoke(group, new [] { newSize });
}
public static void SetSize(int index) {
var gvWndType = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
var selectedSizeIndexProp = gvWndType.GetProperty("selectedSizeIndex",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var gvWnd = EditorWindow.GetWindow(gvWndType);
selectedSizeIndexProp.SetValue(gvWnd, index, null);
}
public static bool SizeExists(GameViewSizeGroupType sizeGroupType, string text) {
return FindSize(sizeGroupType, text) != -1;
}
public static int FindSize(GameViewSizeGroupType sizeGroupType, string text) {
// GameViewSizes group = gameViewSizesInstance.GetGroup(sizeGroupType);
// string[] texts = group.GetDisplayTexts();
// for loop...
var group = GetGroup(sizeGroupType);
var getDisplayTexts = group.GetType().GetMethod("GetDisplayTexts");
var displayTexts = getDisplayTexts.Invoke(group, null) as string[];
for(int i = 0; i < displayTexts.Length; i++) {
string display = displayTexts[i];
// the text we get is "Name (W:H)" if the size has a name, or just "W:H" e.g. 16:9
// so if we're querying a custom size text we substring to only get the name
// You could see the outputs by just logging
// Debug.Log(display);
int pren = display.IndexOf('(');
if (pren != -1)
display = display.Substring(0, pren - 1); // -1 to remove the space that's before the prens. This is very implementation-dependant
if (display == text)
return i;
}
return -1;
}
public static bool SizeExists(GameViewSizeGroupType sizeGroupType, int width, int height) {
return FindSize(sizeGroupType, width, height) != -1;
}
public static int FindSize(GameViewSizeGroupType sizeGroupType, int width, int height) {
// goal:
// GameViewSizes group = gameViewSizesInstance.GetGroup(sizeGroupType);
// int sizesCount = group.GetBuiltinCount() + group.GetCustomCount();
// iterate through the sizes via group.GetGameViewSize(int index)
var group = GetGroup(sizeGroupType);
var groupType = group.GetType();
var getBuiltinCount = groupType.GetMethod("GetBuiltinCount");
var getCustomCount = groupType.GetMethod("GetCustomCount");
int sizesCount = (int)getBuiltinCount.Invoke(group, null) + (int)getCustomCount.Invoke(group, null);
var getGameViewSize = groupType.GetMethod("GetGameViewSize");
var gvsType = getGameViewSize.ReturnType;
var widthProp = gvsType.GetProperty("width");
var heightProp = gvsType.GetProperty("height");
var indexValue = new object[1];
for(int i = 0; i < sizesCount; i++) {
indexValue[0] = i;
var size = getGameViewSize.Invoke(group, indexValue);
int sizeWidth = (int)widthProp.GetValue(size, null);
int sizeHeight = (int)heightProp.GetValue(size, null);
if (sizeWidth == width && sizeHeight == height)
return i;
}
return -1;
}
private static object GetGroup(GameViewSizeGroupType type) {
return getGroup.Invoke(gameViewSizesInstance, new object[] { (int)type });
}
}
Based on the answers in this thread: http://answers.unity.com/answers/1791883/view.html
Hi All i am trying to generate the word document with two different tables included in it, for this purpose i have two similar methods where i am passing word document reference and data object and table to the similar methods..
Now i am looking to make single method in generic way so that in different places i can use single method by passing parameters to it
Method 1 :
private static List<OpenXmlElement> RenderExhaustEquipmentTableDataAndNotes(MainDocumentPart mainDocumentPart, List<ProjectObject<ExhaustEquipment>> exhaustEquipment,Table table)
{
HtmlConverter noteConverter = new HtmlConverter(mainDocumentPart);
var equipmentExhaustTypes = new Dictionary<string, List<ProjectObject<ExhaustEquipment>>>();
foreach (var item in exhaustEquipment)
{
string exhaustEquipmentName = item.TargetObject.Name;
if (!equipmentExhaustTypes.ContainsKey(exhaustEquipmentName))
{
equipmentExhaustTypes.Add(exhaustEquipmentName, new List<ProjectObject<ExhaustEquipment>>());
}
equipmentExhaustTypes[exhaustEquipmentName].Add(item);
}
List<OpenXmlElement> notes = new List<OpenXmlElement>();
int noteIndex = 1;
foreach (var exhaustEquipmentItem in equipmentExhaustTypes)
{
List<string> noteIndices = new List<string>();
for (int exhaustEquipmentConditionIndex = 0; exhaustEquipmentConditionIndex < exhaustEquipmentItem.Value.Count; exhaustEquipmentConditionIndex++)
{
var condition = exhaustEquipmentItem.Value[exhaustEquipmentConditionIndex];
var row = new TableRow();
Run superscriptRun = new Run(new RunProperties(new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }));
if (exhaustEquipmentConditionIndex == 0)
{
row.Append(RenderOpenXmlElementContentCell(new Paragraph(
new List<Run> {
new Run(new RunProperties(), new Text(exhaustEquipmentItem.Key) { Space = SpaceProcessingModeValues.Preserve }),
superscriptRun
}), 1,
new OpenXmlElement[] {new VerticalMerge { Val = MergedCellValues.Restart },new TableCellMargin {
LeftMargin = new LeftMargin { Width = "120" },
TopMargin = new TopMargin { Width = "80" } }
}));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new OpenXmlElement[] { new VerticalMerge { Val = MergedCellValues.Continue } }));
}
row.Append(RenderTextContentCell(condition.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{condition.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{condition.TargetObject.MaxAirflow:R2}"));
if (condition.TargetObject.NotesHTML?.Count > 0)
{
foreach (var note in condition.TargetObject.NotesHTML)
{
var compositeElements = noteConverter.Parse(note);
var htmlRuns = compositeElements.First().ChildElements.Where(c => c is Run).Cast<Run>().Select(n => n.CloneNode(true));
notes.Add(new Run(htmlRuns));
noteIndices.Add(noteIndex++.ToString(CultureInfo.InvariantCulture));
}
}
if (exhaustEquipmentConditionIndex == exhaustEquipmentItem.Value.Count - 1 && condition.TargetObject.NotesHTML?.Count > 0)
{
superscriptRun.Append(new Text($"({String.Join(',', noteIndices)})") { Space = SpaceProcessingModeValues.Preserve });
}
table.Append(row);
}
}
List<OpenXmlElement> notesSection = new List<OpenXmlElement>();
List<OpenXmlElement> result = RenderNotesArray(table, notes, notesSection);
return result;
}
and I am calling this method like this in below
var table = new Table(RenderTableProperties());
table.Append(new TableRow(
RenderTableHeaderCell("Name"),
RenderTableHeaderCell("Constant Volume"),
RenderTableHeaderCell("Minimum Airflow", units: "(cfm)"),
RenderTableHeaderCell("Wet Bulb Temperature", units: "(cfm)")
));
body.Append(RenderExhaustEquipmentTableDataAndNotes(mainDocumentPart, designHubProject.ExhaustEquipment, table));
Method 2:
private static List<OpenXmlElement> RenderInfiltrationTableData(MainDocumentPart mainDocumentPart, List<ProjectObject<Infiltration>> infiltration,Table table)
{
HtmlConverter noteConverter = new HtmlConverter(mainDocumentPart);
var nameByInflitrationObject = new Dictionary<string, List<ProjectObject<Infiltration>>>();
foreach (var infiltrationData in infiltration)
{
string infiltrationName = infiltrationData.TargetObject.Name;
if (!nameByInflitrationObject.ContainsKey(infiltrationName))
{
nameByInflitrationObject.Add(infiltrationName, new List<ProjectObject<Infiltration>>());
}
nameByInflitrationObject[infiltrationName].Add(infiltrationData);
}
List<OpenXmlElement> notes = new List<OpenXmlElement>();
int noteIndex = 1;
foreach (var inflitrationDataItem in nameByInflitrationObject)
{
List<string> noteIndices = new List<string>();
for (int inflitrationNameIndex = 0; inflitrationNameIndex < inflitrationDataItem.Value.Count; inflitrationNameIndex++)
{
var dataItem = inflitrationDataItem.Value[inflitrationNameIndex];
var row = new TableRow();
Run superscriptRun = new Run(new RunProperties(new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }));
if (inflitrationNameIndex == 0)
{
row.Append(RenderOpenXmlElementContentCell(new Paragraph(
new List<Run> {
new Run(new RunProperties(), new Text(inflitrationDataItem.Key) { Space = SpaceProcessingModeValues.Preserve }),superscriptRun
}), 1,
new OpenXmlElement[] {new VerticalMerge { Val = MergedCellValues.Restart },new TableCellMargin {
LeftMargin = new LeftMargin { Width = "120" },
TopMargin = new TopMargin { Width = "80" }}
}));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new OpenXmlElement[] { new VerticalMerge { Val = MergedCellValues.Continue } }));
}
row.Append(RenderTextContentCell($"{dataItem.TargetObject.AirflowScalar.ToString("R2", CultureInfo.CurrentCulture)} cfm {EnumUtils.StringValueOfEnum(dataItem.TargetObject.InfiltrationCalculationType).ToLower(CultureInfo.CurrentCulture)}"));
if (dataItem.TargetObject.NotesHTML?.Count > 0)
{
foreach (var note in dataItem.TargetObject.NotesHTML)
{
var compositeElements = noteConverter.Parse(note);
var htmlRuns = compositeElements.First().ChildElements.Where(c => c is Run).Cast<Run>().Select(n => n.CloneNode(true));
notes.Add(new Run(htmlRuns));
noteIndices.Add(noteIndex++.ToString(CultureInfo.InvariantCulture));
}
}
if (inflitrationNameIndex == inflitrationDataItem.Value.Count - 1 && dataItem.TargetObject.NotesHTML?.Count > 0)
{
superscriptRun.Append(new Text($"({String.Join(',', noteIndices)})") { Space = SpaceProcessingModeValues.Preserve });
}
table.Append(row);
}
}
List<OpenXmlElement> notesSection = new List<OpenXmlElement>();
List<OpenXmlElement> result = RenderNotesArray(table, notes, notesSection);
return result;
}
and then i am calling this method here like as below
var table = new Table(RenderTableProperties());
table.Append(new TableRow(
RenderTableHeaderCell("Type"),
RenderTableHeaderCell("Air Flow")
));
body.Append(RenderInfiltrationTableData(mainDocumentPart, designHubProject.Infiltration, table));
i know these are lots of lines but is there any generic way to use single method out of these two similar methods and i am using .net core
Could any one please suggest any idea or suggestion how can i refactor these two methods into single method that would be very grateful.
many thanks in advance
Before we can create a single function that handles both types, achieving the highly laudable goal of removing gratuitous duplication, we should clean the code up to make it easier to see which parts, if any, are different between the two nearly identical methods. And there is a lot to clean up, even if we only had one function.
In short, your functions are too long, having too much much code in one place, and in fact too much code altogether.
In the following, the original code has been broken down into multiple functions with specific purposes and refactored to remove DIY nonsense in favor of the standard library functions and the removal of pointless code.
static IEnumerable<OpenXmlElement> RenderExhaustEquipmentTableDataAndNotes(MainDocumentPart mainDocumentPart, List<ProjectObject<ExhaustEquipment>> exhaustEquipment, Table table)
{
var equipmentByType = exhaustEquipment.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var items in equipmentByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in items)
{
var row = new TableRow();
if (item == items.First())
{
row.Append(CreateFirstRowStartingCell(items.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
row.Append(RenderTextContentCell(item.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{item.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{item.TargetObject.MaxAirflow:R2}"));
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == items.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
List<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
private static Run CreateSuperScriptRun()
{
return new Run(new RunProperties(new VerticalTextAlignment
{
Val = VerticalPositionValues.Superscript
}));
}
private static void UpdateSuperScript(Run superscriptRun, IEnumerable<OpenXmlElement> notes)
{
superscriptRun.Append(new Text($"({string.Join(",", Enumerable.Range(0, notes.Count()))})")
{
Space = SpaceProcessingModeValues.Preserve
});
}
private static IEnumerable<OpenXmlElement> ParseNotes(MainDocumentPart mainDocumentPart, IEnumerable<OpenXmlElement> notes)
{
return notes == null
? Enumerable.Empty<OpenXmlElement>()
: notes.Select(note => new HtmlConverter(mainDocumentPart).Parse(note))
.Select(note => note.First().ChildElements
.OfType<Run>()
.Select(n => n.CloneNode(true))).Select(htmlRuns => new Run(htmlRuns))
.ToList();
}
private OpenXmlElement CreateFirstRowStartingCell(string key, Run superscriptRun)
{
return RenderOpenXmlElementContentCell(
new Paragraph(new List<Run> {
new Run(new RunProperties(), new Text(key) { Space = SpaceProcessingModeValues.Preserve }),
superscriptRun
}),
1,
new OpenXmlElement[] {
new VerticalMerge { Val = MergedCellValues.Restart },
new TableCellMargin { LeftMargin = new LeftMargin { Width = "120" }, TopMargin = new TopMargin { Width = "80" } }
});
}
Now, let's tackle the second function:
static IEnunumerable<OpenXmlElement> RenderInfiltrationTableData(MainDocumentPart mainDocumentPart, IEnunumerable<ProjectObject<Infiltration>> infiltration, Table table)
{
var infiltrationsByType = infiltration.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var inflitrations in infiltrationsByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in inflitrations)
{
var row = new TableRow();
if (item == inflitrations.First())
{
row.Append(CreateFirstRowStartingCell(inflitrations.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
row.Append(RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}").ToLower());
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == inflitrations.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
IEnumerable<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
As we have seen, duplication can be massively reduced simply by extracting code into simple helper functions.
This also makes it far easier to see just where the differences are between the two functions.
It is simply a matter of
row.Append(RenderTextContentCell(item.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{item.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{item.TargetObject.MaxAirflow:R2}"));
vs.
row.Append(RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}").ToLower());
To achieve your desired goal of a single function, we can make a generic function, and require that the caller pass in a function that will take care of these differences.
static IEnumerable<OpenXmlElement> RenderTableDataAndNotes<T>(
MainDocumentPart mainDocumentPart,
IEnumerable<ProjectObject<T>> projects,
Table table,
Func<ProjectObject<T>, IEnumerable<OpenXmlElement>> createCells
) where T : ITargetObject
{
var projectsByType = projects.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var items in projectsByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in items)
{
var row = new TableRow();
if (item == items.First())
{
row.Append(CreateFirstRowStartingCell(items.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
var itemCells = createCells(item);
foreach (var cell in itemCells)
{
row.Append(cell);
}
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == items.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
IEnumerable<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
Now, when we call it for say some Exhaust Equipment, we do so as follows:
var rendered = RenderTableDataAndNotes(mainDocumentPart, exhaustProjects, table,
exhaust => new[] {
RenderTextContentCell(exhaust.TargetObject.IsConstantVolume ? "Yes" : "No"),
RenderTextContentCell($"{exhaust.TargetObject.MinAirflow:R2}"),
RenderTextContentCell($"{exhaust.TargetObject.MaxAirflow:R2}"),
});
And for infiltration projects, we would do as follows:
var rendered = RenderTableDataAndNotes(
mainDocumentPart,
infiltrationProjects,
table,
infiltration => new[] {
RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}")
.ToLower()
});
The code could still be substantially improved even now. Currently it requires that the various project types implement a common ITargetObject interface declaring the Name property used to group projects by type. If you refactored your code to reduce nesting by hoisting Name to the ProjectObject<T> type, then we could remove the constraint and the otherwise useless requirement that Infiltration and ExhaustEquipment implement the ITargetObject interface.
Note, if you can't change the types, you can adjust the code in a few ways.
For example, you can remove the type constraint on T and build the lookup outside and pass it to the function:
static IEnumerable<OpenXmlElement> RenderTableDataAndNotes<T>(
MainDocumentPart mainDocumentPart,
ILookup<string, ProjectObject<T>> projectsByType,
Table table,
Func<ProjectObject<T>, IEnumerable<OpenXmlElement>> createCells
)
Then you would call it as
var infiltrationProjectsByType = infiltrationProjects.ToLookup(project => project.Name);
var rendered = RenderTableDataAndNotes(
mainDocumentPart,
infiltrationProjectsByType,
table,
infiltration => new[] {
RenderTextContentCell($"{infiltration.TargetObject.AirflowScalar:R2} cfm {infiltration.TargetObject.InfiltrationCalculationType}").ToLower()
}
);
private int SumOfNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Nodes != 0);
int SumOfNodes = 0;
foreach (Hardware i in SumOfHardware )
{
SumOfNodes += i.Nodes;
}
return SumOfNodes;
}
private int SumOfRepeaters()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Repeaters != 0);
int SumOfRepeaters = 0;
foreach (Hardware i in SumOfHardware)
{
SumOfRepeaters += i.Repeaters;
}
return SumOfRepeaters;
}
private int SumOfHubs()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Hubs != 0);
int SumOfHubs= 0;
foreach (Hardware i in SumOfHardware)
{
SumOfHubs += i.Hubs;
}
return SumOfHubs;
}
private string Month()
{
DateTime now = DateTime.Now;
string month = DateTime.Now.ToString("MMMM");
return month;
}
private void DisplayData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
Values = new ChartValues<int> { SumOfNodes() }
},
};
SeriesCollection.Add
(
new ColumnSeries
{
Title = "Repeaters",
Values = new ChartValues<int> { SumOfRepeaters() }
}
);
SeriesCollection.Add
(
new ColumnSeries
{
Title = "Hubs",
Values = new ChartValues<int> { SumOfHubs() }
}
);
Labels = new[] { Month() };
Formatter = value => value.ToString("N");
DataContext = this;
}
enter image description here
At this points I've managed to create an app that adds/removes and updates my items on my database. I'm also planning to add some stats (Started off with graph visualisation) but I'm facing an issue.
I want to seperate columns based on months. So for example as seen by the image attached no matter how many items i add , remove or update the total amount for each item is added to Decemeber. But when January comes any newly added modification to the quantity of my items I would like to see adjacent to the Decemeber one.
P.S.: There is alot of code repitition which will accounted for later on.
I've managed to put something together regarding my answer above which may be usefull for someone in the future
What I've done was to
1) Get my data from my db.
2) Find the Month of interest using LINQ queries **
instead of selecting the items (i.e repeaters, nodes)
**
3) By doing so it also accounts for the items I'm interested in during the month of interest.
4) Do an incremental foreach loop as shown in my code above.
5) Change the methodd i want to display (i.e DisplayData to DisplayDecemberData) and use in the same way as above.
6) Create further methods for the subsequent months and just add the additional information in the ChartValues object.
See Below
private int DecemberNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> decembernodes = t.GetHardware().FindAll(x => x.Date.Month==12);
int DecemberSumOfNodes = 0;
foreach (Hardware i in decembernodes)
{
DecemberSumOfNodes += i.Nodes;
}
return DecemberSumOfNodes;
}
private int JanuaryNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> januarynodes = t.GetHardware().FindAll(x => x.Date.Month==01);
int JanuarySumOfNodes = 0;
foreach (Hardware i in januarynodes)
{
JanuarySumOfNodes += i.Nodes;
}
private void DisplayDecemberData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
Values = new ChartValues<int> { DecemberNodes() }
},
};
private void DisplayJanuaryData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
**Values = new ChartValues<int> { DecemberNodes(), JanuaryNodes() }**
},
};
There is some code repetition and I'm pretty sure there is a more code concise way of actually doing it but for now this seems to do the trick. When I simplify the code i will post it.
I'm writing a project about game's character data.
And each character in the data document have four types, Lv1 and LvMAX, and HP, STR, VIT, INT, MEN.
I use top one code at the middle part and got NullReferenceException when I use it to get some data like:
int x = CD.Parameters.Basic.Awaked.Force.Lv1.STR;
Force will be null. But when I use buttom one at the middle part, Force won't be null.
What's the difference between that two?
Code below
public class ParamType
{
public ParamLv Mebius, Force, Aegis, Magius;
string cost;
DataRow[] Datas;
List<int> ToMebius = new List<int>(), ToForce = new List<int>(), ToAegis = new List<int>(), ToMagius = new List<int>(); //HP, HP, STR, STR, VIT, VIT, INT, INT, MEN, MEN
public ParamType(SData Data, bool awaked)
{
if (awaked)
{
Data.CharaID = CharaCOM.AwakedID(Data.CharaID);
}
Datas = DataCOM.Search(Data.CharaID, Data.DTs.Source, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID, Const.COL_CHARACTER_ID);
cost = DataCOM.Search(Data.DTs.Source, Data.CharaID, Const.COL_COST, 0, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID_WITH_TYPE);
List<int>[] SArray = { ToMebius, ToForce, ToAegis, ToMagius };
for (int i = 0; i < Datas.Length; i++)
{
SArray[i] = new List<int>();
for (int j = Const.COL_PARAM_MIN; j < Const.COL_PARAM_MIN + Const.COL_PARAM_LENGTH; j++)
{
SArray[i].Add(Convert.ToInt32(Datas[i][j]));
}
}
/*
this will send NullReference Exception
ParamLv[] PLArray = new ParamLv[4];
for (int i = 0; i < SArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
*/
/*
This won't get exception and I can get correct data I want.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
*/
}
public class ParamLv
{
public Params Lv1, LvMax;
List<int> ToLv1 = new List<int>(), ToLvMAX = new List<int>(); //HP, STR, VIT, INT, MEN
public ParamLv(SData Data, List<int> ParamsL)
{
for (int i = 0; i < ParamsL.Count; i += Const.COL_PARAM_MIN_MAX_GAP)
{
ToLv1.Add(ParamsL[i]);
ToLvMAX.Add(ParamsL[i + 1]);
}
Lv1 = new Params(Data, ToLv1);
LvMax = new Params(Data, ToLvMAX);
}
public class Params
{
//some method and properties to get or set Parameters.
}
}
Please tell me if something still bad, and this is my first time to ask question here, so If I did something wrong, please tell me. Thanks for #MicroVirus , #Moriarty and #mvikhona told my mistake.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
This works, because you are assigning reference to new ParamLv to your properties.
But in this case:
ParamLv[] PLArray = { Mebius, Force, Aegis, Magius };
for (int i = 0; i < PLArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
you aren't filling your array with variables/properties themselves, but you are filling it with references your properties hold, in the end your array will hold reference to 4 new ParamLw, but your property Force will stay null.
Edit:
I'll try to explain it a bit different. Let's say you have this code:
ParamLv[] PLArray = { Force };
At this moment value of PLArray[0] is same as value of Force, but PLArray[0] isn't Force.
The moment you do this:
PLArray[0] = new ParamLv(Data, null);
new ParamLv(Data, null) returns reference to new ParamLv and you assign this to your PLArray[0], but like I said before PLArray[0] isn't Force, so Force will stay unchanged.
If that didn't explain it well, try to look at this piece of code, it does what you are trying to do.
int a = 1;
int[] myArr = { a }; // a = 1, myArr[0] = 1
myArr[0] = 2; // a = 1, myArr[0] = 2
object obj = null;
object[] objArr = { obj }; // obj = null, objArr[0] = null
objArr[0] = new object(); // obj = null, objArr[0] = 'reference to new object'
I have large switch statement in which I create UIElements based on input value from XElement:
public static UIElement CreateElement(XElement element) {
var name = element.Attribute("Name").Value;
var text = element.Attribute("Value").Value;
var width = Convert.ToDouble(element.Attribute("Width").Value);
var height = Convert.ToDouble(element.Attribute("Height").Value);
//...
switch (element.Attribute("Type").Value) {
case "System.Windows.Forms.Label":
return new System.Windows.Controls.Label() {
Name = name,
Content = text,
Width = width,
Height = height
};
case "System.Windows.Forms.Button":
return new System.Windows.Controls.Button() {
Name = name,
Content = text,
Width = width,
Height = height
};
//...
default:
return null;
}
}
I am creating a lot controls like this and as you can see, too much repetition is going on.
Is there some way to avoid this repetition? Thanks in advance for ideas.
You could create a generic function that does the create:
private static Create<T>(string name, string text, double width, double height) where T: Control, new()
{
return new T { Name = name, Content = text, Width = width, Height = height }
}
Your switch then becomes:
switch (element.Attribute("Type").Value) {
case "System.Windows.Forms.Label" : return Create<System.Windows.Forms.Label>(name, text, width, height);
etc.
}
You could also adapt this to pass in the XElement, whichever you prefer.
If the Type attribute is always the name of the System.Type you want, then you could just do
Control ctrl = (Control) Activator.CreateInstance(Type.GetType(element.Attribute("Type").Value));
ctrl.Name = name;
etc.
If there's a one to one mapping between the value of the attribute and the type you want, then you can declare a readonly static field with the mapping:
private static readonly uiTypeMapping = new Dictionary<string,Type> {
{ "System.Windows.Forms.Label", typeof(System.Windows.Controls.Label) },
{ "System.Windows.Forms.Button", typeof(System.Windows.Controls.Button) },
{ etc. }
};
And use
UIElement elem = (UIElement) Activator.CreateInstance(uiTypeMapping[element.Attribute("Type").Value]);
etc.
Something like this could work... :)
var controlCreators = new Dictionary<string, Func<ContentControl>>
{
{"System.Windows.Forms.Label", () => new Label()},
{"System.Windows.Forms.Button", () => new Button()}
};
Func<ContentControl> createControl;
if (!controlCreators.TryGetValue(element.Attribute("Type").Value, out createControl))
{
return null;
}
var control = createControl();
control.Name = name;
control.Content = text;
control.Width = width;
control.Height = height;
return control;
Those different controls have inheritance trees. So for example Width, Height, Name are defined on FrameworkElement. So you could do something like the following:
object createdObject = null;
switch (element.Attribute("Type").Value)
{
case "System.Windows.Forms.Label":
createdObject = new System.Windows.Controls.Label();
break;
case "System.Windows.Forms.Button":
createdObject = new System.Windows.Controls.Button();
break;
}
var fe = createdObject as FrameworkElement;
if (fe != null)
{
fe.Name = element.Attribute("Name").Value;
fe.Width = Convert.ToDouble(element.Attribute("Width").Value);
fe.Height = Convert.ToDouble(element.Attribute("Height").Value);
}
var ce = createdObject as ContentElement;
if (ce != null)
{
ce.Content = element.Attribute("Value").Value;
}
return createdObject;
Note that by using this approach, in comparison to Flynn's answer, you can also easily add code such as "when the control is an ItemsControl, do this", i.e. code which won't apply to every type, but only to some of them.
You can do it with reflection + expressions.
[TestClass]
public class UnitTest1
{
public class Creator
{
private static Dictionary<string,Func<XElement, Control>> _map = new Dictionary<string, Func<XElement,Control>>();
public static Control Create(XElement element)
{
var create = GetCreator(element.Attribute("Type").Value);
return create(element);
}
private static Expression<Func<XElement, string>> CreateXmlAttributeAccessor(string elementName)
{
return (xl => xl.Attributes(elementName).Select(el => el.Value).FirstOrDefault() ?? "_" + elementName);
}
private static Func<XElement, Control> GetCreator(string typeName)
{
Func<XElement, Control> existing;
if (_map.TryGetValue(typeName, out existing))
return existing;
// mapping for whatever property names you wish
var propMapping = new[]
{
new{ Name = "Name", Getter = CreateXmlAttributeAccessor("Name") },
new{ Name = "Content", Getter = CreateXmlAttributeAccessor("Value") },
};
var t = Assembly.GetAssembly(typeof (Control)).GetType("System.Windows.Controls." + typeName);
var elementParameter = Expression.Parameter(typeof (XElement), "element");
var p = from propItem in propMapping
let member = t.GetMember(propItem.Name)
where member.Length != 0
select (MemberBinding)Expression.Bind(member[0], Expression.Invoke(propItem.Getter, elementParameter));
var expression = Expression.Lambda<Func<XElement, Control>>(
Expression.MemberInit(Expression.New(t),p), elementParameter);
existing = expression.Compile();
_map[typeName] = existing;
return existing;
}
}
[TestMethod]
public void TestMethod1()
{
var xel = new XElement("control",
new XAttribute("Type", "Button"),
new XAttribute("Name", "Foo"),
new XAttribute("Value", "Bar"),
new XElement("NonExistent", "foobar")); // To check stability
var button = (Button) Creator.Create(xel);
Assert.AreEqual("Foo", button.Name);
Assert.AreEqual("Bar", button.Content);
}
}
To make it work with other types then string, you can use Expression.Convert. Left as an exercise.
You could to this with reflection instead, or you could create a Dictionary of strings (what you're switching on now) and Funcs (or actions rather) where you create the controls.
for the specific code you posted, you can assign height and width after the switch statement, since they exsist on Control directly.