Designer serialization and collection - c#

I have an custom control that represents a grid; and implements another custom control.
When opening this control in de designer, I am able to use the collection editor to set my collection. When saving; the designer successfully saves my collection.
However, when dropping this control on a form; it still (and should) expose(s) the collection property allowing me to modify the default values as i have defined in the other control.
However; when saving this designer; it also tries to store the predefined items in the collection; adding the default ones with every save.
What is the best way to solve this problem? I have attached a code sample.
Code sample where i have defined my collection:
GridPicture.cs
[Category("Layout")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
public GridPictureColumnDefinitionCollection ColumnDefinitions
{
// The DesignerSerializationVisibility attribute instructs the design editor to serialize the contents of the collection to source code.
// This will place all the code required to add the items to a collection variable of GridPictureColumnDefinitionCollection.
get
{
return m_ColumnDefinitions;
}
}
Generated designer code of my first 'implementation' of this grid; Picture1.cs
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition1 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition2 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureRowDefinition gridPictureRowDefinition1 = new VGTest.GridPictureRowDefinition();
gridPictureColumnDefinition1.Auto = true;
gridPictureColumnDefinition1.Value = 0F;
gridPictureColumnDefinition2.Auto = true;
gridPictureColumnDefinition2.Value = 0F;
this.ColumnDefinitions.Add(gridPictureColumnDefinition1);
this.ColumnDefinitions.Add(gridPictureColumnDefinition2);
gridPictureRowDefinition1.Auto = true;
gridPictureRowDefinition1.Value = 0F;
this.RowDefinitions.Add(gridPictureRowDefinition1);
Code sample when i place this picture1 on another picture; picture2.cs: (Note that picture11 is the picture1, as it is the 1st of picture1 ;)
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition1 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition2 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition3 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureRowDefinition gridPictureRowDefinition1 = new VGTest.GridPictureRowDefinition();
// Some code removed that does the Auto and Value settings as above
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition1);
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition2);
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition3);
The picture2 control; when it regenerates the InitializeComponent() method; now adds the columndefinitions which i have added in picture1.
I have written this temporary fix for the problem:
{
// This makes sure column definitions are only serialized when configured at a implementation of this GridPicture.
// This is a Quick/Dirty fix for the following problem:
// When MyPanel (:GridPicture) is put on PanelContainer(:Picture); the picture designer (re)serializes this each save.
return this.GetType().BaseType.Name == typeof(Picture).Name;
}

I was unable to find a better method. I have decided to stick with the solution below.
{
// This makes sure column definitions are only serialized when configured at a implementation of this GridPicture.
// This is a Quick/Dirty fix for the following problem:
// When MyPanel (:GridPicture) is put on PanelContainer(:Picture); the picture designer (re)serializes this each save.
return this.GetType().BaseType.Name == typeof(Picture).Name;
}

Related

FlyoutNavigation Controller

Using MonoDevelop, I have been looking at an IOS implementation of a side slide out menu using FlyoutNavigationController, but have hit a couple of stumbling blocks.
Firstly, how can you access the font elements of the generated list?
I can easily modify row heights etc, but am unsure of how to proceed with modifying the list items, can this be down with a tablesource and item styling?
Secondly, how to open a view from this list?
Currently an empty view is used by default but new views are to be opened from the side menu list, I have tried using the push navigation controller but it fails to open.
Any ideas are more than welcome.
navigation = new FlyoutNavigationController();
navigation.View.Frame = UIScreen.MainScreen.Bounds;
View.AddSubview(navigation.View);
navigation.NavigationRoot = new RootElement ("Menu List")
{
new Section ("Menu List")
{
from page in SlideList
select new StringElement (page.title) as Element
}
};
navigation.NavigationTableView.BackgroundColor = UIColor.DarkGray;
navigation.NavigationTableView.RowHeight = 30;
navigation.NavigationTableView.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine;
navigation.NavigationTableView.SeparatorColor = UIColor.LightGray;
navigation.NavigationTableView.SectionHeaderHeight = 60;
//navigation.NavigationTableView.DataSource = SlideList;
//navigation.ViewControllers = Array.ConvertAll (MenuItems, title => new UINavigationController (new TaskPageController (navigation, title)));
navigation.ViewControllers = Array.ConvertAll (MenuItems, title => new TaskPageController (navigation, title));
this.NavigationItem.LeftBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Action, delegate {
navigation.ToggleMenu();
});
I haven't used the FlyOutNavigationController before, but I took a look at this example:
https://github.com/xamarin/FlyOutNavigation
It looks like you're supposed to have the same number of StringElements as Controllers. For the ViewControllers array, it looks like you can supply your own custom controllers instead of just plain ViewControllers. After that, clicking a list item should automatically navigate to the appropriate controller.
In regards to styling, looking at the source for this NavigationController, I don't see much in terms of being able to stylize the cells. I did a quick search for how to go about styling MonoTouch Dialog lists and it looks like there isn't an easy way without subclassing elements:
Monotouch Dialog: Styling Elements
However, I can share with you how I've accomplished the two questions you asked without the Dialog framework.
You can create a custom class that extends UITableViewSource:
http://docs.xamarin.com/guides/ios/user_interface/tables/part_2_-_populating_a_table_with_data
In the GetCell method override, you can grab an instance of the cell's label and set the font like so:
cell.TextLabel.Font = UIFont.FromName("TitlingGothicFB Cond", 20);
Another thing you can do with your custom UITableViewSource class is create a custom event:
public event EventHandler ListItemSelected;
Inside the RowSelected method you can dispatch this event:
public override void RowSelected (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
ListItemSelected(this, new MyCustomEventArgs(indexPath.Row));
}
In the controller class that was responsible for instantiating this TableSource, you can listen and handle this event like so:
var customTableSource = new CustomTableSource(myList);
MyTable.Source = customTableSource;
customTableSource.ListItemSelected += (object sender, EventArgs e) => {
if((e as MyCustomEventArgs).rowSelected == 1){
this.NavigationController.PushViewController(new MyNextViewController(), true));
}
}

Set Combobox Items and DataSource

I'm having a problem where every time I build my solution, the compile succeeds but when I run my program it will error as the forms designer.cs file has had the data source for my custom comboboxes added to it automatically; resulting in an exception stating
Items collection cannot be modified when the DataSource property is set.
Any ideas on what might be the problem? I've tried setting the data source after the initialize component method but this results in a different error as the unit type is null..
The type of data source is set in a property for the control and below is the relevant code
form.Designer.cs (this is generated for you not a custom cs file called designer)
//
// cmbWheelUnitCR
//
this.cmbWheelUnitCR.DataSource = ((object)(resources.GetObject("cmbWheelUnitCR.DataSource")));
this.cmbWheelUnitCR.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbWheelUnitCR.FormattingEnabled = true;
this.cmbWheelUnitCR.Items.AddRange(new object[] {
"mm",
"yd"});
My custom combobox
public string UnitType
{
get { return m_unitType; }
set { m_unitType = value;
this.DataSource = Units.Instance.UnitTypes(m_unitType);}
}
public UnitComboBox()
{
InitializeComponent();
}
I was able to solve this accidentally by setting the data source within an override onLoad event..

Windows 8 - Animating custom property in code-behind

Basically, I want to make bunch of Shapes and make them animated. So I came up with following custom class:
public class FunkyShape : DependencyObject
{
public double Animator
{
get { return (double)GetValue(AnimatorProperty); }
set { SetValue(AnimatorProperty, value); }
}
public static readonly DependencyProperty AnimatorProperty =
DependencyProperty.Register("Animator", typeof(double), typeof(FunkyShape),
new PropertyMetadata(0, new PropertyChangedCallback(Animator_Changed)));
private static void Animator_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double delta = (double)e.NewValue - (double)e.OldValue;
((FunkyShape)d).ProcessDelta((double)e.NewValue, delta);
}
private void ProcessDelta(double val, double delta)
{
Holder.Width = val;
Holder.Height = val;
// Keep shape centered
HolderPosition.X = delta / 2;
HolderPosition.Y = delta / 2;
}
private Shape Holder;
public TranslateTransform HolderPosition
{
get { return (TranslateTransform)Holder.RenderTransform; }
}
public FunkyShape(Canvas playground, Shape shapeToInit)
{
Holder = shapeToInit;
Holder.Width = 10;
Holder.Height = 10;
Holder.Fill = new SolidColorBrush(Colors.Blue);
Holder.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center;
Holder.RenderTransform = new TranslateTransform()
{
X = 500,
Y = 500
};
Holder.RenderTransformOrigin = new Point(0.5, 0.5);
// init done
playground.Children.Add(Holder);
Animate();
}
public void Animate()
{
DoubleAnimation g1 = GrowAnimation();
Storyboard sb = new Storyboard();
Storyboard.SetTarget(g1, this);
// CAN'T FIND ANIMATOR PROPERTY
Storyboard.SetTargetProperty(g1, "Animator");
sb.Children.Add(g1);
sb.Begin(); // THROWS EXCEPTION
}
private static DoubleAnimation GrowAnimation()
{
DoubleAnimation growAnimation = new DoubleAnimation();
growAnimation.Duration = TimeSpan.FromMilliseconds(3000);
growAnimation.From = 0;
growAnimation.To = 100;
growAnimation.AutoReverse = true;
growAnimation.EnableDependentAnimation = true;
growAnimation.RepeatBehavior = new RepeatBehavior(5);
return growAnimation;
}
}
However, when I try making an instance of the class and adding it to the canvas, I get Exception - Storyboard.Being() throws it and tells me that it can't find Animator property.
So - what am I doing wrong?
EDIT: After 3 code changes - it is still not working; I get "Cannot resolve TargetProperty Animator on specified object" error. So if somebody knows the answer - please help out by modifying the code. Thanks!
EDIT: OK, after 24 hours of banging head against the wall there is some progress - if I add shape through XAML it animates, but if I add it through code behind (Canvas.Children.Add), it doesn't work. Let me see if I can figure out why.
OK,
I've found the workaround for what is obviously a bug within the framework (although I'm sure some MS employee will post response and say it's a feature/it-is-by-design). Several things need to be done:
Add default/parameter-less constructor
Change base class of FunkyShape to UserControl.
Open up XAML view of the Page class where you want to add shapes
Add one instance of FunkyShape as a child within the Canvas XAML (<tm:FunkyShape /> for example). IT WON'T WORK WITHOUT THIS.
Make an instance of FunkyShape in code-behind, add it to canvas, start animation and enjoy seeing it works
Switch to less buggy technology.
In Windows 8 you cannot animate custom properties without also setting the enabledependentanimation property to true. This is because non-deterministic animations are disabled by default.
Reference: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.animation.pointanimation.enabledependentanimation.aspx
Yes, you must define this property as a dependency property, not just a regular CLR property. This involves quite a bit of simple boiler plate code. See thus blog post for a complete example:
http://timheuer.com/blog/archive/2012/03/07/creating-custom-controls-for-metro-style-apps.aspx
OK, I had this problem too, but I didn't want to include a public parameterless constructor in my class, so I found another way.
Basically, the issue is that WinRT is a native platform, and it can't do reflection on .NET code. That's why the build process for WinRT apps generates metadata about the types used in XAML (you can find the relevant code in obj/(Debug|Release)/XamlTypeInfo.g.cs).
If a type is never used in XAML, no metadata about this type is generated, which means (among other things) that you can't animate the properties of the type.
If you're writing a class library, you can just include a XAML resource dictionary and declare a dummy instance of the type; it will cause metadata to be generated. However, it requires that the type has a public parameterless constructor, which might not be desirable.
So there is another solution: provide the metadata yourself. There are a several interfaces to implement, and they have many members, so it can be quite tedious to do manually. Fortunately, you don't have to! Here's what you can do:
add a public parameterless constructor to the class (temporarily)
create a XAML ResourceDictionary and declare an instance of the class in it (as described above)
copy the XamlTypeInfo.g.cs file into your project (I renamed it to XamlTypeInfo.cs)
replace the call to the constructor with throw new NotImplementedException()
delete the ResourceDictionary file
remove the public parameterless constructor
And you're done, the animation now works properly.
The process is still quite tedious, so it would be nice to have a tool to do the work for us...
EDIT: much easier solution: apply the [Bindable] attribute to the class. It makes the metadata generator take the type into account even if it's not used in XAML. (ignore the fact that the doc says it's for C++ types; it works just fine on C# classes as well)

Why my control's properties won't change outside its class?

I'm new in C# but not new to coding --being doing it for almost two decades--, and have a problem with properties in a custom control I'm building, which inherits from a Panel. When I put my properties, I can see them in the Designer properties list and can even set them, but when running my little application, it seems these properties values are not used. The same if I change a property programatically: no error but my control does nothing, it is like they are not properly set. However, if I do it programatically whithin the class, they do work. My guess is that something in my properties set/get stuff is not right. Please see the following code chunk of how I'm doing it:
public class ColorStrip : Panel
{
// properties
// ------------------------------------------
// size of color clusters (boxes)
private int _clusterSize = 20;
// controls if show the buttons panel
private Boolean _showButtons;
// property setters/getters
// ------------------------------------------
// clusterSize...
public int clusterSize
{
get { return _clusterSize; }
set { _clusterSize = value; }
}
// showButtons...
public Boolean showButtons
{
get { return _showButtons; }
set { Console.Write(_showButtons); _showButtons = value; }
}
....
So in my form, for instance in the load or even in a click event somewhere, if I put colorStrip1.showButtons = false; or colorStrip1.showButtons = true; whatever (colorStrip1 would be the instance name after placing the control in the form in design mode)... console.write says always 'false'; Even if I set it in the design properties list as 'true' it will not reflect the settled value, even if I default it to true, it will never change externally. Any ideas? Non of the methods get the new and externally settled property value neither, obviously the getter/setter thing is not working. Seems to me I'm not doing right the way I set or get my properties outside the class. It works only inside it, as a charm...Any help...very appreciate!
Cheers
lithium
p.s. TO CLARIFY SOLUTION:
Setting the property in this case didn't work because I was trying to use a new set value within the constructor, which seems can't get the new values since it is, well, building the thing. If I change the property value in Design mode > Property editor or in code externally to the object, say in it's parent form's load event, it will change it but readable for all methods except the constructor, of course :)
It's likely an issue of the order of execution. Your property setter just sets a variable, but doesn't actually trigger anything on the control to update the state related to this variable (e.g. adding or showing the buttons I assume).
When you set the property befre the rest of the initialization is done, the value is being used, otherwise it isn't because during the initial go the default value is still the property value.
You need to act on the setter, here's some pseudocode to illustrate:
set {
_showButtons = value;
if (alreadyInitialized) {
UpdateButtons();
}
}
Note: make sure to first set the value, then act - otherwise you end up using the old value (just like your Console.Write() is doing).
The quoted code doesn't look problematic. Are you sure you're referencing the same instance of ColorStrip? Also, check your .Designer.cs file to ensure that the code setting the property is there.
In fact, try simplifying your code by using auto-implementing properties:
public int clusterSize { get;set;}
public Boolean showButtons {get;set;}
public ColorStrip() { ... clusterSize = 20; ... }

Binding collections to DataGridView in Windows Forms

I'm trying to bind a collection to a DataGridView. As it turns out it's impossible for the user to edit anything in this DataGridView although EditMode is set to EditOnKeystrokeOrF2.
Here is the simplified code:
public Supplies()
{
InitializeComponent();
List<string> l = new <string>();
l.Add("hello");
this.SuppliesDataGridView.DataSource = l;
}
It also doesn't work when I change the collection type to SortableBindingList, Dictionary or even use a BindingSource.
What can be wrong here?
For me the following method works as expected:
Open your form (usercontrol, etc.) with the designer
Add a BindingSource to your form
Select the BindingSource in your form and open the properties page
Select the DataSource property and click on the down arrow
Click on Add project data source
Select Object
Select the object type you wish to handle
This should be the type that will be handled by your collection, not the CustomCollection itself!
Show the available data sources by selecting from the MenuBar Data - Show Data Sources
Drag and Drop your ItemType from the DatasSources on your form
Go into the code of your form and bind your CustomCollection to the BindingSource
var cc = new CustomCollection();
bindingSource1.DataSource = cc;
Remarks:
The DataGridView is just the last part in your chain to (dis)allow changing, adding and removing objects from your list (or CustomCollection). There is also a property AllowNew within the BindingSource and the ICollection interface has a property IsReadOnly which must be set to false to allow editing. Last but not least, the properties of your class within the collection must have a public setter method to allow changing of a value.
Try this:
public class CustomCollection { public string Value { get; set; } }
public Supplies()
{
InitializeComponent();
List<CustomCollection> l = new List<CustomCollection> { new CustomCollection { Value = "hello" } };
this.SuppliesDataGridView.DataSource = l;
}
Once you've set the DataSource property you'll then want to fire off the DataBind() method.
this.SuppliesDataGridView.DataSource = l;
this.SuppliesDataGridView.DataBind();
UPDATE:
As you rightly pointed out in the comments, the DataBind() method doesn't exist for this control.
This link might provide some helpful information: http://msdn.microsoft.com/en-us/library/fbk67b6z%28v=VS.90%29.aspx

Categories

Resources