I have a bunch of Shape-classes (classic) like Rectangle and Circle in my first module.
In my second module, I have a GUI, made with WPF. I want to show a ListBox of all Shape-classes. The ListBox shall contain the localizable name of the shape, which is saved as a resource string, and an icon, saved as a resource image.
I want my whole code to be as modular as possible, e.g. if I add a new Shape-class, I want to change as few classes as possible.
My first approach would be to make a helper class in my GUI-module, which for each shape holds the Shape's Type, its name as string, and its icon as a Bitmap (or similar). I would then initialize the list at one place, e.g.
var shapeList = new List<ShapeHelperClass>
{
new ShapeHelperClass(typeof(Rectangle), Resources.StringRectangle, Resources.IconRectangle),
new ShapeHelperClass(typeof(Circle), Resources.StringCircle, Resources.IconCircle),
};
and bind this list to the ListBox. Now, if I rename my classes or my resources, nothing will break, and localization should work properly. But, of course, if adding a new Shape-class in the first module, I also need to update this list.
Another approach would be to use reflection to find all my Shape classes, and build the list out of that. However, I would still need some Dictionaries or something similar to map the classes to the Resources. I could find the resources if they follow a pattern, like "Icon" + "Classname". However, if no icon is found, this is only noticed at runtime.
So, my questions are:
Is my first approach a good one, our could it be improved?
How can I make sure that a programmer who adds a new Shape also adds the new Resources and extends the mapping-list? Maybe by Unit-testing?
1.Is my first approach a good one, our could it be improved?
You could create a method in your first module that returns all shapes and call this one in your client application, e.g.:
var shapes = GetShapes();
List<ShapeHelperClass> helpers = new List<ShapeHelperClass>();
foreach(var shape in shapes)
helpers.Add(...);
Then you should never have to modify the client application as a shape is added or removed in the first module.
2.How can I make sure that a programmer who adds a new Shape also adds the new Resources and extends the mapping-list? Maybe by Unit-testing?
Maybe you could write a unit test that uses reflection to find all shape types and asserts that they are included in the list of shapes that is returned from the first module. And likewise for the resources. I can't think of any better automatic way of ensuring this really.
Related
I am attempting to add multiple flags of similar types (arrows) to a live chart using a C# windows forms project. This is to provide a label when a value falls out of a pre-defined specification.
I am currently stuck in how to create new instances of the ArrowAnnotation class so if multiple events happen there will be multiple flags for the people checking the chart. I am able to create one instance and manipulate the position to the latest data point in the series (it shouldn't be a stretch to lock it to a historical point, I just haven't done that yet.)
I have an understanding of creating multiple instances of other classes and keeping track of them with lists/ dictionaries but this one has me stumped (or maybe I don't have as good an understanding as I think?)
I can't share the code I have directly but I think I can write some example code if needed.
edit-
I am looking into using a memberwise clone to copy common attributes of each arrow and add those objects to a dictionary.
Thanks
Okay, I have managed to figure out how to do this for my use case.
When updating the live chart I can call a method if a parameter falls out of specification. In that method I create a new instance of the annotation, and the properties that I want to use as a template. (these values can also be changed with conditional logic if you want slight variation, and can be passed in with the argument) than add that newly made annotation to the annotation group. I am still looking for improvements to the code. I am still wanting a way to assign a name to the arrow, than recall that one and modify it (if I wanted to). And change the pass/fail criteria than apply annotations for the new failure points (but that is going into new territory)
// This is not the full method just the part that counts for this question.
private ArrowAnnotation floatArrow;
private void UpdateChart()
{
GenerateArrows(dateTime, sensorValue);
this.chart1.Annotations.Add(floatArrow);enter code here
}
private void GenerateArrows(DateTime x, double y)
{
floatArrow = new ArrowAnnotation();
floatArrow.Name = Convert.ToString(x);
floatArrow.ToolTip = Convert.ToString(x);
floatArrow.AxisXName = "ChartArea1\\rX";
floatArrow.AxisYName = "ChartArea1\\rY";
floatArrow.X = x.ToOADate();
floatArrow.Y = y;
floatArrow.Height = 5;
floatArrow.Width = 0;
floatArrow.BackColor = Color.Red;
}
Is it possible to just "link" two properties of different ViewModels in a way such that when one property changes, the other one changes too. So in essence, I want two properties in different ViewModels behave as if they were one.
It would be nice if I could just do something like the following in my ViewModels:
WhenPropertyChanges(() => SettingX).CopyValueTo(() => ModelView2.SettingX);
Example: On my settings page, when I change a setting, I want the new value to be available in the ViewModel of another page.
I know I can achieve that with the third Layer (Model), but it feels a bit clunky and the way I solved it for now doesn't feel right (Firing events).
I've implemented following MVVM-Pattern from this page: http://www.wintellect.com/devcenter/krome/linking-property-change-notifications-in-xamarin-forms-or-wpfsilverlight. As far as I understood, the author says his code makes it possible, but unfortunately he doesn't show how.
I also found this article (http://blog.alectucker.com/post/2014/07/26/using-messageingcenter-in-xamarin-forms-for-viewmodel-to-viewmodel-navigation.aspx), but I don't necessarily want to navigate to the other View. Still, maybe I can use this MessagingCenter somehow to achieve my goal?
This is addressed in the final paragraph of my article that you reference. I use a simple extension method to do it. An example of this can be seen in the source code example from the article, in this file: https://github.com/Wintellect/XamarinSamples/blob/master/PropertyDependencyDemo%2FPropertyDependencyDemo%2FMvvm%2FObservableExtensions.cs
For your specific example, it would look something like this:
// using PropertyDependencyDemo.Mvvm;
// ... use the namespace above that contains the ObservableExtensions class
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => ModelView2.SettingX = ModelView1.SettingX);
All this does is hook into the PropertyChanged event of the source viewmodel for you in a name-safe way.
One word of caution though: you need to be careful to not create a situation where you inadvertently prevent an object from being garbage collected. The reason this can happen is that in this example, ViewModel1 will now have a PropertyChanged handler that references a PropertyDependency object that in turn references both ModelView1 and ModelView2 due to the captured references in the Action lambda expression.
IF you know for sure that this won't be a problem (perhaps both go out of scope together), then there is nothing to worry about. But if you find yourself facing a situation where you need to prevent ModelView1 from keeping ModelView2 pinned, then you can do so using a WeakReference. Again, this is unlikely to be a concern, but if you find it leaking memory then you can change the above to this:
// assuming "TModelView" is the class name of the viewmodels
var wr = new WeakReference<TModelView>(ModelView2);
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => {
TModelView mv;
if (wr.TryGetTarget(out mv))
mv.SettingX = ModelView1.SettingX;
});
I think if the navigation is lineare ( you only need the information of page 1 in page 2 ) you can use the MessagingCenter to do what you want.
If the user is able to change the data in the page 2 ( and be updated in the page 1 ) the messagingCenter is a little to tricky to use. I think you can use the first link you provide. Just use an abstract class with all shared data in your ViewModels.
If you want more help put an more completed code example.
I have a GUI control that I need to generalize so it can be used in different situations and I need suggestions. Let me give you some background first. I have a model that stores all my data for a specific application. I access and set the data points through properties in this model.
Right now I am passing the instance of the model to the GUI control and there the client can set/reset/read in the control two specific pieces of data (in the code I am using the properties). That is all good, but now the model has 3 more sets of these two "columns" (six new fields) that also need to be manipulated by the same control in other 3 new different situations. Obviously I don't want to create 3 more copies of this same control (yes, extensibility was not considered when the control was first designed, I know, I know). So, I tried passing a reference to the properties in the control constructor which of course does not work (compiler error: A property, indexer or dynamic member access may not be passed as an out or ref parameter). So, my question is, what would be a good design strategy in this case? How can I generalize this control so it can be reused and it can set/edit these other properties in the model?
Thanks!
I am working to generate terrain for our project, something that will be contained in the Model class that I can draw, but I new class would be alright since I may need to look inside for specific data often, and then I would just need the basic function to work with the Game class.
Anyway, I have a fair amount of knowledge of the XNA framework, but because of how convoluted it handles anything. So my problem is I can't just make a Model, I can't instantiate that class or anything. I have what I believe the proper data to form a model's geometry, which is all I need right now, and later possibly have it textured.
I don't know where to go from here.
XNA you usually use Content.Load, to have their content pipeline read in a file and parse it specifically, but I want to avoid that because I want my terrain generated. I can compute an array of Vertex data and indices for the triangles I want to make-up a mesh, but so far my efforts have tried to instantiate any object like Model or those it contains, have failed.
If there is some factory class I can use to build it, I have no idea what that is, so if someone else can point me in the right direction there and give me a rough outline on how to build a model, that would help.
If that's not the answer, maybe I need to do something completely different, either centered on using Content.Load or not, but basically I don't want my terrain sitting in a file, consistent between executions, I want to control the mesh data on load and randomize it, etc.
So how can I get a model generated completely programmatically, to show up on the screen, and still have its data exposed?
Model and its associated classes (eg: ModelMesh), are convenience classes. They are not the only way to draw models. It is expected that sometimes, particularly when doing something "special", you will have to re-implement them entirely, using the same low-level methods that Model uses.
Here's the quick version of what you should do:
First of all, at load time, create a VertexBuffer and an IndexBuffer and use SetData on each to fill each with the appropriate data.
Then, at draw time, do this:
GraphicsDevice.SetVertexBuffer(myVertexBuffer);
GraphicsDevice.Indices = myIndexBuffer;
// Set up your effect. Use a BasicEffect here, if you don't have something else.
myEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.Textures[0] = myTexture; // From Content.Load<Texture2D>("...")
GraphicsDevice.DrawIndexedPrimitives(...);
i'm kind of stuck here. i'm using a dragndrop-library which serializes the dragged UIElements via XamlWriter.Save().
now i'm trying to dragndrop some instances of a class extending Grid. in the constructor i push some elements into the visual tree.
the constructor gets called when the object is deserialized and the elements get added again (and again and again depending on how often i dragndrop). sadly Children.Count tells me there are no child elements if i retrieve the value from within the constructor. if i retrieve it from outside (by myGrid.Children.Count) it gives me the higher (unwanted) amount.
is there any other function i should use to initialize the visuals? or any other way to prevent those duplicates?
thanks and cheers
took a while, but i seem to have found a solution.
now i'm able to create a base class already containing the visual elements all the subclasses need. and they are parsable via XamlWriter/Reader without duplicates. whew.
1) extend UserControl (dont Add->UserControl but Add->Class and inherit manually)
2) put the standard InitializeControl(); into the constructor
3) implement InitializeControl() and put the layouting, child adding and whatever in there
4) xamlwrite/xamlread/inherit like crazy
hope it will be helpful to someone and that i havent overseen the unforeseen..
--
edit: of course.
there will be duplicates but we cant see them.
after the XamlReader is through there are the UIElements created by my InitializeComponent() AND the ones getting xaml-parsed after that. any references in the code regard the code-created controls which are NOT show :/
sigh.
one far from perfect way around this is to put a switch into an Initialized event handler, like so:
if(HasContent)
Initialize();
else
Reinitialize();
Multiinitialize();
where the Initialize() would add the UIElements to the visual tree, the Reinitialize() just finds the right references (by control = FindName("controlName")) and the Multiinitialze() recreates the event handlers and what else gets lost on the way throug the XamlReader.
well, now you can instantiate the custom inherited UserControl in xaml but there is no easy way to set attributes. that is because setting the attributes happens before the Initialized-event and there are NullPointerExceptions awaiting. you could work around that with bindings i guess.
but there HAS to be an easier way. please let me know!