I have a process which uses recursion to generate controls from XML. The system is complicated. I've broken down as small as I can. The last Plugin is visible, the rest are not. I suspect GenerateControls() is broken. Why don't all the plugins display?
Form:
public partial class PdLoadingForm : Form
{
public PdLoadingForm()
{
InitializeComponent();
PhysDocDocument document = MockDocument();//Generate some mock data
GenerateControls(document.Nodes);//Generate controls using recursion.
}
private PhysDocDocument MockDocument()
{
PhysDocDocument document = new PhysDocDocument();
PhysDocNode outerNode = new PhysDocNode();
outerNode.Display = "outer Node";
//Generate 3 plugins. Each plugin generates a textbox. I only see 1 Textbox.
//I expect to see 3 textboxes.
for(int i = 0; i < 3; i++)
{
PhysDocNode innerNode = new PhysDocNode() { Display = "test" + i };
PhysDocNode innerNodeContent = new PhysDocNode() { Display = "IO" };
innerNodeContent.Plugins.Add(new Plugin());
innerNode.Nodes.Add(innerNodeContent);
outerNode.Nodes.Add(innerNode);
}
document.Nodes.Add(outerNode);
return document;
}
private void GenerateControls(List<PhysDocNode> children, CustomControl parent = null)
{
foreach (PhysDocNode node in children)
{
CustomControl parentControl = new CustomControl(node);
if (node.Nodes != null && node.Nodes.Count > 0)
GenerateControls(children: node.Nodes, parent: parentControl);
foreach (Plugin plugin in node.Plugins)
{
Control childControl = plugin.CreateUIControl();//Ask the plugin for a control
AddControl(childControl: childControl, parent: parentControl);
}
AddControl(childControl: parentControl, parent: parent);
}
}
private void AddControl(Control childControl, Control parent)
{
childControl.Dock = DockStyle.Top;
if(parent == null)//add to form
Controls.Add(childControl);
else//add to parent
parent.Controls.Add(childControl);
}
}
Plugin:
public class Plugin
{
[XmlAttribute]
public string type { get; set; }
public Control CreateUIControl()
{
TextBox testBox = new TextBox();
testBox.Text = "plugin";
return testBox;
}
}
CustomControl:
public class CustomControl: UserControl
{
public CustomControl(PhysDocNode nodeInfo)
{
InitializeComponent();
Label label = new Label();
label.Text = "Rtb..." + nodeInfo.Display;
label.Dock = DockStyle.Top;
contentPanel.Controls.Add(label);//just drop a panel on the user control in design view
}
Looking at the documentation, it seems that the Dock property manage the positionning of the label relatively to its parent.
You may try to specify the label position by, for example, setting its Top property's value to a multiple of its index in your control list.
Related
Is it possible to remove the top blue border(or atleast change its color) of xamarins TableSection:
I looked at Xamarin TableView documentation, but I found no help there:
https://developer.xamarin.com/guides/xamarin-forms/user-interface/tableview/
My code at the moment looks like this:
public class UserProfilePushNotification : TableView
{
public UserProfilePushNotification(string text) : base()
{
Intent = TableIntent.Data;
Root = new TableRoot
{
new TableSection
{
new SwitchCell
{
Text = text
},
new TextCell()
{
Text = string.Empty
},
new TextCell
{
Text = "Android Version: 1.2.1"
}
}
};
}
}
I was digging into this problem and I found that TableView is implemented (in the default renderer) as a ListView.
The TableSection is just a normal item in the Listview, the first one.
If you don't use the Title Property from the TableSection (in this case you aren't using it) you can hide it.
To do this I created a custom render for the TableView and hidden the first element of the ListView:
[assembly:ExportRenderer(typeof(Project.MenuTableView), typeof(Project.Droid.MenuTableViewRenderer))]
namespace Project.Droid
{
public class MenuTableViewRenderer : TableViewRenderer
{
private bool _firstElementAdded = false;
protected override void OnElementChanged (ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged (e);
if (Control == null)
return;
var listView = Control as Android.Widget.ListView;
listView.ChildViewAdded += (sender, args) =>
{
if (!_firstElementAdded)
{
args.Child.Visibility = ViewStates.Gone;
_firstElementAdded = true;
}
};
// Uncomment this if you want to remove all the dividers from the table.
//listView.DividerHeight = 0;
}
}
}
We have a windows form PropertyGrid that we use to display all the properties. We have drawn a checkbox on Boolean property that checks it self and unchecks itself based on the value. this all works fine.
the issue is, that user wants to change the check box value in single click, whereas property grid changes it on a double click and I cant figure out a way to handle clicks or change property value on single click when property type is Boolean.
How to change property value in single click?
PropertyGrid internally has methods which allows us to use them with reflection to get the GridItem under mouse when you click on its PropertyGridView internal control.
In below code, I handled mouse click on its PropertyGridView control and checked if the item under mouse position is a boolean property, I reversed it's value. The event will fire for the label of property, also for icon area of the property editor:
PropertyGrid
using System;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
public class ExPropertyGrid : PropertyGrid
{
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
var grid = this.Controls[2];
grid.MouseClick += grid_MouseClick;
}
void grid_MouseClick(object sender, MouseEventArgs e)
{
var grid = this.Controls[2];
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var invalidPoint = new Point(-2147483648, -2147483648);
var FindPosition = grid.GetType().GetMethod("FindPosition", flags);
var p = (Point)FindPosition.Invoke(grid, new object[] { e.X, e.Y });
GridItem entry = null;
if (p != invalidPoint) {
var GetGridEntryFromRow = grid.GetType()
.GetMethod("GetGridEntryFromRow", flags);
entry = (GridItem)GetGridEntryFromRow.Invoke(grid, new object[] { p.Y });
}
if (entry != null && entry.Value != null) {
object parent;
if (entry.Parent != null && entry.Parent.Value != null)
parent = entry.Parent.Value;
else
parent = this.SelectedObject;
if (entry.Value != null && entry.Value is bool) {
entry.PropertyDescriptor.SetValue(parent,!(bool)entry.Value);
this.Refresh();
}
}
}
}
Drawing CheckBox in PropertyGrid
public class MyBoolEditor : UITypeEditor
{
public override bool GetPaintValueSupported
(System.ComponentModel.ITypeDescriptorContext context)
{ return true; }
public override void PaintValue(PaintValueEventArgs e)
{
var rect = e.Bounds;
rect.Inflate(1, 1);
ControlPaint.DrawCheckBox(e.Graphics, rect, ButtonState.Flat |
(((bool)e.Value) ? ButtonState.Checked : ButtonState.Normal));
}
}
Class which used in screenshot
public class Model
{
public int Property1 { get; set; }
[Editor(typeof(MyBoolEditor), typeof(UITypeEditor))]
public bool Property2 { get; set; }
[TypeConverter(typeof(ExpandableObjectConverter))]
public Model Property3 { get; set; }
}
I'd like to comment, but rep isn't high enough yet.
The accepted answer works great. However as mentioned the code doesn't trigger the PropertyValueChanged event.
Adding a call to OnPropertyValueChanged triggers the PropertyValueChanged event.
entry.PropertyDescriptor.SetValue(parent, !(bool)entry.Value);
this.Refresh();
base.OnPropertyValueChanged(null);
Then in the PropertyValueChanged event code you can access the custom object that has been changed.
To communicate the changed property back to the form create some properties in the custom object, with Browsable set to false so they do not appear in the PropertyGrid.
[Browsable(false)]
public string changedParent { get; set; }
[Browsable(false)]
public string changedLabel { get; set; }
[Browsable(false)]
public string changedValue { get; set; }
At the top of the Form class create this static property
public partial class Form1 : Form
{
private static Form1 form = null;
In the constructor of Form1 link form to this.
public Form1()
{
InitializeComponent();
..
..
form = this;
Back in grid_MouseClick before triggering OnPropertyValueChanged save off the changed property information.
entry.PropertyDescriptor.SetValue(parent, !(bool)entry.Value);
this.Refresh();
form.sh.changedParent = entry.Parent.Label;
form.sh.changedLabel = entry.Label;
form.sh.changedValue = entry.Value.ToString();
base.OnPropertyValueChanged(null);
Now in the PropertyValueChanged event code you can determine which property was changed.
form.customobject.changedParent
form.customobject.changedLabel
form.customobject.changedValue
The best answer used a reflection to get GridItem under the mouse. However, I don't see the point in doing this since it's enough to request a dedicated GridItem. Here is my implementation of MouseClick:
void grid_MouseClick(object sender, MouseEventArgs e)
{
GridItem entry = SelectedGridItem;
if (entry != null && entry.Value != null && entry.Value is bool b)
{
var obj = SelectedObjects.Length == 1 ? SelectedObject : SelectedObjects;
entry.PropertyDescriptor.SetValue(obj, !b);
}
}
The above code from Reza Aghaei work long time but now changed the list of controls.
void grid_MouseClick(object sender, MouseEventArgs e)
{
var grid = this.Controls[2]; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var invalidPoint = new Point(-2147483648, -2147483648);
///// following line throws a Nullreference Exception
var FindPosition = grid.GetType().GetMethod("FindPosition", flags);
var p = (Point)FindPosition.Invoke(grid, new object[] { e.X, e.Y });
GridItem entry = null;
...
}
Now you need to select the right control(PropertyGridView).
Below my solution.
int idx = -1;
for (int i = 0; i < this.Controls.Count; i++)
{
Control control = this.Controls[i];
if (control.Text.Contains("PropertyGridView"))
{
idx = i;
break;
}
}
var grid = this.Controls[idx];
I need to create a user control MyTypeListControl to display collection of objects of type MyType using a user controls MyTypeDisplayControl instance for each of those objects.
So that I could
add instance of MyTypeListControl to my WinForm, then
load collection of MyType and
assign it to MyTypeListControl's DataSource.
In the result it should generate and show appropriate count of MyTypeDisplayControl instances in MyTypeListControl's instance.
In case if I needed to show list of properties - equivalent would be DataGrid with specific fields from MyType assigned to specific DataGrid's columns, but I want to view each MyType item as a user control - with more power for visual representation and functionality than DataGrid provides for it's rows.
Is that even possible?
I found this SO resource how to create My collection type, but this is only small part of the problem solution...
It is quite easy (if you know how) and doesn't take so much effort as you might think in the first place (at least for a simple implementation that handles collection of less then 100 items).
So at first lets create a MyType:
public class MyType
{
public static MyType Empty = new MyType(String.Empty, DateTime.MinValue);
public MyType(string myName, DateTime myBirthday)
{
MyName = myName;
MyBirthday = myBirthday;
}
public DateTime MyBirthday { get; private set; }
public string MyName { get; private set; }
}
At next we need a MyTypeControl:
public partial class MyTypeControl : UserControl
{
private MyType _MyType;
private Label labelBirthday;
private Label labelName;
private Label labelSeparator;
public MyTypeControl()
{
InitializeComponent();
}
public event EventHandler MyTypeChanged;
public MyType MyType
{
get { return _MyType; }
set
{
if (_MyType == value)
return;
_MyType = value ?? MyType.Empty;
OnMyTypeChanged(EventArgs.Empty);
}
}
protected virtual void OnMyTypeChanged(EventArgs eventArgs)
{
UpdateVisualization();
RaiseEvent(MyTypeChanged, eventArgs);
}
protected void UpdateVisualization()
{
SuspendLayout();
labelName.Text = _MyType.MyName;
labelBirthday.Text = _MyType.MyBirthday.ToString("F");
labelBirthday.Visible = _MyType.MyBirthday != DateTime.MinValue;
ResumeLayout();
}
private void InitializeComponent()
{
labelName = new Label();
labelBirthday = new Label();
labelSeparator = new Label();
SuspendLayout();
labelName.Dock = DockStyle.Top;
labelName.Location = new Point(0, 0);
labelName.TextAlign = ContentAlignment.MiddleCenter;
labelBirthday.Dock = DockStyle.Top;
labelBirthday.TextAlign = ContentAlignment.MiddleCenter;
labelSeparator.BorderStyle = BorderStyle.Fixed3D;
labelSeparator.Dock = DockStyle.Top;
labelSeparator.Size = new Size(150, 2);
Controls.Add(labelSeparator);
Controls.Add(labelBirthday);
Controls.Add(labelName);
MinimumSize = new Size(0, 48);
Name = "MyTypeControl";
Size = new Size(150, 48);
ResumeLayout(false);
}
private void RaiseEvent(EventHandler eventHandler, EventArgs eventArgs)
{
var temp = eventHandler;
if (temp != null)
temp(this, eventArgs);
}
}
Then comes our magically list control:
public class MyTypeListControl : UserControl
{
private ObservableCollection<MyType> _Items;
public MyTypeListControl()
{
AutoScroll = true;
_Items = new ObservableCollection<MyType>();
_Items.CollectionChanged += OnItemsCollectionChanged;
}
public Collection<MyType> Items
{
get { return _Items; }
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateVisualization();
}
private void UpdateVisualization()
{
SuspendLayout();
Controls.Clear();
foreach (var item in _Items)
{
var control = new MyTypeControl { MyType = item, Dock = DockStyle.Top };
Controls.Add(control);
Controls.SetChildIndex(control, 0);
}
ResumeLayout();
}
}
And now simply create the list control in your form or parent control and fill it with some meaningful values:
myTypeListControl.Items.Add(new MyType("Adam", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 40))));
myTypeListControl.Items.Add(new MyType("Eva", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 38))));
In a windows application project I have a form which used a user control. I want to hide a label and textbox on user control. In which event of form I can do this ?
This method in user control which named DoctorPermissionApprove:
public void LoadDoctorPermission(int fromWhere)
{
if (fromWhere == 0) // Başhekimden geldiyse?
{
labelDoctor.Visible = true;
editDoctorWithoutHead.Visible = true;
}
else if (fromWhere == 1) // Normal Hekimden geldiyse
{
labelDoctor.Visible = false;
editDoctorWithoutHead.Visible = false;
}
}
And in form:
private void ExistRequestAndNewEntryForm_Shown(object sender, EventArgs e)
{
var obj = new DoctorPermissionApprove();
obj.LoadDoctorPermission(0);
}
For example I tried in shown event. But it still visible
I want to hide or show this components when the anybody open the form
Thank you a lot
In the UserControl class add a public property to set the internal label visibility true or false. This can be accessed from your parent form where your usercontrol is added.
Example:
public class YourUserControl
{
//This code will be in designer class
private Label lblYourLabelToHide = new Label();
//Create this public property to hide the label
public bool IsLabelVisible
{
set { lblYourLabelToHide.Visible = value; }
}
}
public class YourParentForm
{
//This will be in designer
private YourUserControl userControl = new YourUserControl();
public void Form_Load()
{
//based on some criteria
userControl.IsLabelVisible = false;
}
}
I have a user control that inherited from Combobox control. I want to bind data in the constructor of the user control. but when I add it to form and run the project it shows duplicated items.
When I add control to my winform its add items in the Designer file of form and when I run project it added again in constructor of user control.
public partial class CheckSeriesBox : ComboBox
{
private static List<string> CheckSeries;
public CheckSeriesBox()
{
InitializeComponent();
CheckSeries = new List<string>();
SetCheckSeries();
this.Items.AddRange(CheckSeries.ToArray());
this.SelectedIndex = 0;
}
public static List<string> SetCheckSeries()
{
CheckSeries.Add("A");
CheckSeries.Add("B");
}
}
http://social.msdn.microsoft.com/forums/vstudio/en-US/3e35b534-7d3f-4832-8859-b5cb838bd62a/extended-combobox-adds-items-twice
public partial class CheckSeriesBox : ComboBox
{
private static List<string> CheckSeries;
public CheckSeriesBox()
{
InitializeComponent();
CheckSeries = new List<string>();
SetCheckSeries();
if (DesignMode)
{
this.Items.AddRange(CheckSeries.ToArray());
}
}
public static List<string> SetCheckSeries()
{
CheckSeries.Add("A");
CheckSeries.Add("B");
}
protected new bool DesignMode
{
get
{
if (base.DesignMode)
{
return true;
}
else
{
Control parent = this.Parent;
while ((parent != null))
{
System.ComponentModel.ISite site = parent.Site;
if ((site != null) && site.DesignMode)
{
return true;
}
parent = parent.Parent;
}
return false;
}
}
}
}