Optimizing large switch statement - c#

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.

Related

How can I declare GlyphRun object?

I need to draw just a couple of digits. Is it enough to define only GlyphRun and FontRenderingEmSize? If not, please, suggest me, how to draw, for example, string "123" (how to define GlyphRun object for that). I wrote this code:
var gr = new GlyphRun();
gr.Characters = new List<char>() { '1', '2' }; //Mistake
gr.FontRenderingEmSize = 20;
var Glyph = new GlyphRunDrawing(Brushes.Black, gr);
I found this old helper class of mine. Maybe you can make use of it.
public static class GlyphRunText
{
public static GlyphRun Create(
string text, Typeface typeface, double emSize, Point baselineOrigin)
{
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
throw new ArgumentException(string.Format(
"{0}: no GlyphTypeface found", typeface.FontFamily));
}
var glyphIndices = new ushort[text.Length];
var advanceWidths = new double[text.Length];
for (int i = 0; i < text.Length; i++)
{
var glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
glyphIndices[i] = glyphIndex;
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize;
}
return new GlyphRun(
glyphTypeface, 0, false, emSize,
glyphIndices, baselineOrigin, advanceWidths,
null, null, null, null, null, null);
}
}

I want to eliminate all of these case statements (too messy)

I am writing a test program which returns a string "P3-PASS" or "P3-FAIL. In all there are 12 possible tests, P3 to P14 ("P3-FAIL" to "P14-PASS").
I have a button "All_Tests" which calls each test 1 by 1, and the associated button changes colour based on the result.
Ideally I want to do something like PageNum.Background = Brushes.Red, but I can't do this because I can't use a string to access the button. Hence the case statements below. Is there a way to simplify this, because it looks awful. OR is there a way to access the relevant button using a string, or similar. Many Thanks
int PageNum = Int32.Parse(PageTstName);
switch (PageNum)
{
case 3:
if (TstResult == "PASS")
{
Pg3.Background = Brushes.SeaGreen;
Pg3.Foreground = Brushes.White;
}
else // TstResult is "FAIL"
{
Pg3.Background = Brushes.Red;
Pg3.Foreground = Brushes.White;
}
break;
case 4:
if (TstResult == "PASS")
{
Pg4.Background = Brushes.SeaGreen;
Pg4.Foreground = Brushes.White;
}
else // TstResult is "FAIL"
{
Pg4.Background = Brushes.Red;
Pg4.Foreground = Brushes.White;
}
break;
case 5: .....etc
You can create a dictionary that maps the numbers to the controls, e.g.
var controlsByPageNum = new Dictionary<int, Button>()
{
{ 3, Pg3 },
{ 4, Pg4 },
{ 5, Pg5 },
// ...
}
When receiving the test results, you can get the control like this:
int PageNum = Int32.Parse(PageTstName);
var btn = controlsByPageNum[PageNum];
if (TstResult == "PASS")
{
btn.Background = Brushes.SeaGreen;
btn.Foreground = Brushes.White;
}
else // TstResult is "FAIL"
{
btn.Background = Brushes.Red;
btn.Foreground = Brushes.White;
}
Create a method to apply your colors:
private static void ApplyColors(Control control, Brush background, Brush foreground)
{
control.Background = background;
control.Foreground = foreground;
}
And a dictionary to map each case:
private readonly Dictionary<string, Action> dict =
new Dictionary<string, Action>();
Now, in your constructor, fill the dictionary:
dict[$"3~PASS"] = () => ApplyColors(Pg3, Brushes.SeaGreen, Brushes.White);
dict[$"3~FAIL"] = () => ApplyColors(Pg3, Brushes.Red, Brushes.White);
dict[$"4~PASS"] = () => ApplyColors(Pg4, Brushes.SeaGreen, Brushes.White);
dict[$"4~FAIL"] = () => ApplyColors(Pg4, Brushes.Red, Brushes.White);
Here, for each page and result, you define the action to execute. With this, now you can manage your current switch in this way:
var key = $"{PageTstName}~{TstResult}";
if (dict.TryGetValue(key, out Action action))
{
action();
}
else
{
// A non mapped case...
}

Unity c sharp - changing the resolution aspect ratio according to screen size through code

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

How to add multilevel list in word document using openxml sdk and c#?

I am trying to implement multilevel list in word document programmatically using openxml sdk v2.5 and C#. I have used the following code in order to display headings in multilevel list. I am calling the AddHeading method and passing the respective parameters as shown below. My expected result is to be as below.
1. Parent
1.1. childItem
1.1.1. subchildItem
1.2. childItem
2. Parent
2.1. childItem
3. Parent
But the output i am getting is
1. Parent
1. childItem
1. subchildItem
2. childItem
2. Parent
1. childItem
3. Parent
public static void AddHeading(WordprocessingDocument document, string colorVal, int fontSizeVal, string styleId, string styleName, string headingText, int numLvlRef, int numIdVal)
{
StyleRunProperties styleRunProperties = new StyleRunProperties();
Color color = new Color() { Val = colorVal };
DocumentFormat.OpenXml.Wordprocessing.FontSize fontSize1 = new DocumentFormat.OpenXml.Wordprocessing.FontSize();
fontSize1.Val = new StringValue(fontSizeVal.ToString());
styleRunProperties.Append(color);
styleRunProperties.Append(fontSize1);
AddStyleToDoc(document.MainDocumentPart.Document.MainDocumentPart, styleId, styleName, styleRunProperties, document);
Paragraph p = new Paragraph();
ParagraphProperties pp = new ParagraphProperties();
pp.ParagraphStyleId = new ParagraphStyleId() { Val = styleId };
pp.SpacingBetweenLines = new SpacingBetweenLines() { After = "0" };
ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "ListParagraph" };
NumberingProperties numberingProperties1 = new NumberingProperties();
NumberingLevelReference numberingLevelReference1 = new NumberingLevelReference() { Val = numLvlRef };
NumberingId numberingId1 = new NumberingId() { Val = numIdVal }; //Val is 1, 2, 3 etc based on your numberingid in your numbering element
numberingProperties1.Append(numberingLevelReference1); Indentation indentation1 = new Indentation() { FirstLineChars = 0 };
numberingProperties1.Append(numberingId1);
pp.Append(paragraphStyleId1);
pp.Append(numberingProperties1);
pp.Append(indentation1);
p.Append(pp);
Run r = new Run();
Text t = new Text(headingText) { Space = SpaceProcessingModeValues.Preserve };
r.Append(t);
p.Append(r);
document.MainDocumentPart.Document.Body.Append(p);
}
public static void AddStyleToDoc(MainDocumentPart mainPart, string styleid, string stylename, StyleRunProperties styleRunProperties, WordprocessingDocument document)
{
StyleDefinitionsPart part = mainPart.StyleDefinitionsPart;
if (part == null)
{
part = AddStylesPartToPackage(mainPart);
AddNewStyle(part, styleid, stylename, styleRunProperties);
}
else
{
if (IsStyleIdInDocument(mainPart, styleid) != true)
{
string styleidFromName = GetStyleIdFromStyleName(document, stylename);
if (styleidFromName == null)
{
AddNewStyle(part, styleid, stylename, styleRunProperties);
}
else
styleid = styleidFromName;
}
}
}
public static string GetStyleIdFromStyleName(WordprocessingDocument doc, string styleName)
{
StyleDefinitionsPart stylePart = doc.MainDocumentPart.StyleDefinitionsPart;
string styleId = stylePart.Styles.Descendants<StyleName>()
.Where(s => s.Val.Value.Equals(styleName) &&
(((Style)s.Parent).Type == StyleValues.Paragraph))
.Select(n => ((Style)n.Parent).StyleId).FirstOrDefault();
return styleId;
}
public static StyleDefinitionsPart AddStylesPartToPackage(MainDocumentPart mainPart)
{
StyleDefinitionsPart part;
part = mainPart.AddNewPart<StyleDefinitionsPart>();
DocumentFormat.OpenXml.Wordprocessing.Styles root = new DocumentFormat.OpenXml.Wordprocessing.Styles();
root.Save(part);
return part;
}
public static bool IsStyleIdInDocument(MainDocumentPart mainPart, string styleid)
{
DocumentFormat.OpenXml.Wordprocessing.Styles s = mainPart.StyleDefinitionsPart.Styles;
int n = s.Elements<DocumentFormat.OpenXml.Wordprocessing.Style>().Count();
if (n == 0)
return false;
DocumentFormat.OpenXml.Wordprocessing.Style style = s.Elements<DocumentFormat.OpenXml.Wordprocessing.Style>()
.Where(st => (st.StyleId == styleid) && (st.Type == StyleValues.Paragraph))
.FirstOrDefault();
if (style == null)
return false;
return true;
}
private static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart, string styleid, string stylename, StyleRunProperties styleRunProperties)
{
DocumentFormat.OpenXml.Wordprocessing.Styles styles = styleDefinitionsPart.Styles;
DocumentFormat.OpenXml.Wordprocessing.Style style = new DocumentFormat.OpenXml.Wordprocessing.Style()
{
Type = StyleValues.Paragraph,
StyleId = styleid,
CustomStyle = false
};
style.Append(new StyleName() { Val = stylename });
style.Append(new BasedOn() { Val = "Normal" });
style.Append(new NextParagraphStyle() { Val = "Normal" });
style.Append(new UIPriority() { Val = 900 });
styles.Append(style);
}
To use multi-level lists in Microsoft Word, you need to ensure that:
you have the desired multi-level list set up correctly in your numbering definitions part (meaning your w:numbering element contains a w:num and corresponding w:abstractNum that specifies the different list levels);
you have one style for each list level that references both the numbering ID specified by the w:num element and the desired list level; and
your paragraphs reference the correct style for the list level.
Please have a look at my answers on how to create multi-level ordered lists with Open XML and display multi-level lists in Word documents using C# and the Open XML SDK for further details and explanations.

Updating TreeView after changing CellRendererCombo (Gtk#)

Could somebody point me in the right direction on how to update a Gtk.TreeView after changing a CellRendererCombo in Gtk#?
Since the only example I found was in Python, I tried to port the example to C#, but without success so far.
The Python example is here: http://learngtk.org/pygtk-tutorial/cellrenderercombo.html
In the code below I am having difficulties with the method ComboChanged.
After changing the value in the combobox (by selecting a different value) and placing the focus outside of the combobox, the value does not change.
using System;
using Gtk;
using System.Collections.Generic;
public partial class MainWindow: Gtk.Window
{
public MainWindow (): base (Gtk.WindowType.Toplevel)
{
Build ();
var tvComboBox = InitTreeViewWithComboBox ();
var vbox = new Gtk.VBox ();
vbox.PackStart (tvComboBox, true, true, 0);
this.Add (vbox);
this.ShowAll ();
}
// adopted from http://learngtk.org/pygtk-tutorial/cellrenderercombo.html
ListStore liststore_hardware;
ListStore liststore_manufacturers;
private TreeView InitTreeViewWithComboBox ()
{
liststore_manufacturers = new Gtk.ListStore(typeof (string));
var manufacturers = new List<string> {"Sony", "LG", "Panasonic", "Toshiba", "Nokia", "Samsung"};
foreach (var item in manufacturers) {
liststore_manufacturers.AppendValues (item);
}
liststore_hardware = new Gtk.ListStore(typeof (string), typeof (string));
liststore_hardware.AppendValues ("Television", "Samsung");
liststore_hardware.AppendValues ("Mobile Phone", "LG");
liststore_hardware.AppendValues ("DVD Player", "Sony");
var treeview = new Gtk.TreeView ();
treeview.Model = liststore_hardware;
var column_text = new TreeViewColumn { Title = "Text" };
var column_combo = new TreeViewColumn { Title = "Combo" };
treeview.AppendColumn (column_text);
treeview.AppendColumn (column_combo);
var cellrenderer_text = new CellRendererText ();
column_text.PackStart (cellrenderer_text, false);
column_text.AddAttribute (cellrenderer_text, "text", 0);
var cellrenderer_combo = new CellRendererCombo ();
cellrenderer_combo.Editable = true;
cellrenderer_combo.Model = liststore_manufacturers;
cellrenderer_combo.TextColumn = 0;
column_combo.PackStart (cellrenderer_combo, false);
column_combo.AddAttribute (cellrenderer_combo, "text", 1);
cellrenderer_combo.Edited += ComboChanged;
return treeview;
}
void ComboChanged (object o, EditedArgs args)
{
// Not really sure what to do here....
/*
var crc = o as CellRendererCombo;
TreeIter iter; // index within the combobox
if (!crc.Model.GetIterFirst (out iter)) {
return;
}
crc.Model.SetValue (iter, 0, args.NewText);
liststore_hardware.SetValue (iterHardware, 1, args.NewText);
*/
}
Going through the API a bit more I found the solution...:
void ComboChanged (object o, EditedArgs args)
{
TreeSelection selection = treeview.Selection;
TreeIter iter;
if (!selection.GetSelected (out iter)) {
return;
}
liststore_hardware.SetValue (iter, 1, args.NewText);
}

Categories