I created a custom composite control that contains, among other things, child controls from this project https://googlevisnet.codeplex.com/ (it is a .NET library for google charts which i checked various times). I am using VisualStudio 2012 and a simple C# Class library.
I override CreateChildControl like so:
protected override void CreateChildControls()
{
Controls.Clear();
//create the pie control
GenderChart = new GVPieChart();
GenderChart.Width = PieChartDimentions[0];
GenderChart.Height = PieChartDimentions[1];
GenderChart.ChartData(GenderDistributionTable);
//create the bar control
AgeChart = new GVColumnChart();
AgeChart.Width = BarChartDimentions[0];
AgeChart.Height = BarChartDimentions[1];
AgeChart.ChartData(AgeDistributionTable);
this.Controls.Add(GenderChart);
this.Controls.Add(AgeChart);
}
and override Render like so:
AgeChart.RenderControl(output);
When I run the code on the page everything seems to work, and i can tell that the control rendered the area correctly (i can see the div with the specific size), however I do not see the chart itself.
The weird thing is that for a moment there it was working and then stopped again without me applying any changes to the code.
Any Ideas?
Apparently what solve the problem was something as stupid as adding the ID for the controls.
protected override void CreateChildControls()
{
Controls.Clear();
//create the pie control
GenderChart = new GVPieChart();
GenderChart.ID = "genderChar";
GenderChart.Width = PieChartDimentions[0];
GenderChart.Height = PieChartDimentions[1];
GenderChart.ChartData(GenderDistributionTable);
////create the bar control
AgeChart = new GVColumnChart();
AgeChart.ID = "ageChart";
AgeChart.Width = BarChartDimentions[0];
AgeChart.Height = BarChartDimentions[1];
AgeChart.ChartData(AgeDistributionTable);
this.Controls.Add(GenderChart);
this.Controls.Add(AgeChart);
}
I don't know how i forgot something as essential as ID. Our mind stops to think simple at some point.
Cheers :)
Related
I'm working through a project where I'm going to have multiple square size instances of the same set of form components.
I can either create 8 instances manually in my form UI or what I'd rather do is create a view (or Item Renderer) and then dynamically add instances of that view to my main view.
How do I add a create and add a custom view dynamically to the main view in my Xamarin form?
Note: Including Swift tag because you might know the answer if you know Swift or Objective C since the API wraps Apple API.
If IIUC:
Create a view in XCode Interface Builder
In ViewDidLoad create an instance of the custom instance views
Add each instance to the main view
I'd read a guide if there was one but I can't find anything specifically on this.
Some what related. I can create a new View in Xcode interface Builder pretty easily. Is there a way to export that as a class to my application?
Update:
I've found a textfield in Interface Builder where I can enter the name of a class. Back in Visual Studio my main View Controller can see the HelloWorld class. I've found a method named AddChildViewController. I try testing it. Nothing happens. Do I need to set the position and size? I can't find any API to do this.
Tomorrow I will scour the ancient texts again for example code. Maybe there is something I missed?
public override void ViewDidLoad()
{
base.ViewDidLoad();
var view = new HelloView(this.Handle);
var handle = view.Handle;
base.AddChildViewController(view);
var view2 = new HelloView(this.Handle);
handle = view.Handle;
base.AddChildViewController(view2);
}
I noticed a note in the console log:
<ViewController: 0x600000a94180> already have child of:
<ViewController: 0x600000a94180>
Update II:
This adds a new NSView and then a NSButton in that view to the main window:
var frame = new CoreGraphics.CGRect(0, 0, 100, 100);
var button = new NSButton(frame) {
Title = "My Button",
};
var view = new NSView(frame) {};
view.AddSubview(button);
View.AddSubview(view);
It doesn't add my custom class yet though.
Update III:
I'm able to add the custom HelloWorldView class but the controls are not visible. If I add a button to the form I see it but it is anchored to the bottom of the screen. I don't see the controls created from Interface Builder.
//var frame = this.View.Frame;
var frame = new CoreGraphics.CGRect(0, 0, 100, 20);
var button = new NSButton(frame) {
Title = "My Button"
};
var frame2 = new CoreGraphics.CGRect(0, 0, 100, 100);
var helloView = new HelloView() {
};
helloView.Frame = frame2;
helloView.AddSubview(button);
mainFrame.AddSubview(helloView);
HelloView.cs:
public partial class HelloView : NSView
{
public HelloView () : base ()
{
}
}
Note about the code above: I removed the handle parameter because it was causing a compiler error.
Setup: Visual Studio for Mac using Xamarin C# and XCode Interface Builder
--
Notes for the bounty.
To receive the bounty you must show how to do either step 1 or step 2 mentioned above in the Bounty Requirements section. I prefer step 1. If you are unsure ask.
I have Xamarin forms time picker following custom renderer for IOS
[assembly: ExportRenderer(typeof(TimePicker), typeof(Time24PickerRenderer))]
namespace LabOraTimeStamp.iOS.Renderers
{
public class Time24PickerRenderer:TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
var doneBtn = toolbar.Items[1];
//Set the Done to OK
doneBtn.Title = "OK";
}
}
}
I wanted to change the default "done" to "Ok".
1) How can I do that? the line mentioned above for setting the title does not affect anything.
2) I already implemented localization for xamarin forms.I just wanted to use existing Resx values from custom renderer to show the string for appropriate culture.How can I achieve that?
So the reason why your code isn't working is because the done button is created with the UIBarButtonSystemItem.Done style. It doesn't care about the Title property. Renderer code here.
To work around that issue you could try replacing the Xamarin created done button with your own custom Ok button.
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
// Replace Xamarin's buttons with custom ones.
var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
var doneButton = new UIBarButtonItem();
doneButton.Title = "OK";
doneButton.Clicked += (o, a) => Control.ResignFirstResponder();
toolbar.SetItems(new [] { spacer, doneButton}, false);
I've been working on developing a custom control which will be used in our CRM frontend. The control itself is nothing special, it simply wraps two labels, text edits, and a button into a single control. (The control is only acting as a wrapper, a bit long winded, but unfortunately our only option due to various restrictions)
I though it would be nice to give the control a Font and ForeColor property, that would change the Font and Color of the labels. Changing the font size means that the relative position of the text boxes be changed to keep everything in line. No problem.
I encapsulated the layout logic in an UpdateLayout method, which is called on the set accessor of the Font property and everything works beautifully at design time, however, at runtime, the Font of the labels is correct, but the layout of the text boxes and button are still in the default positions, hence, the labels overlap.
What am I missing in for updating the position of controls at the init stage in runtime? I've tried calling the UpdateLayout() method from both Initialize and the constructor of the control, alas to no avail.
Am i missing something obvious here?
EDIT:
As requested, I whipped up a quick test. My test control looks like so (Not including Designer code):
public partial class TestControl : UserControl
{
private Font _font;
[Browsable(true)]
public override Font Font
{
get
{
return this._font ?? SystemFonts.DefaultFont;
}
set
{
this._font = value;
this.DoLayout();
}
}
private void DoLayout()
{
this.label1.Font = this._font;
this.Size = new Size(label1.Width + textBox1.Width + 10,
label1.Height >= textBox1.Height ? label1.Height : textBox1.Height);
this.textBox1.Location = new Point(label1.Location.X + 5 + label1.Width, 1);
this.Update();
}
public TestControl()
{
InitializeComponent();
}
protected override void OnLayout(LayoutEventArgs e)
{
base.OnLayout(e);
DoLayout();
}
}
That works great at design time, but runtime, less so...
EDIT2:
So the above code doesn't entirely reflect the problem accurately, however, I have tried Jogy's suggestion of overriding the OnLayout method, and lo and behold, it works!
I'm relatively new to Custom Controls, so a rookie mistake on my part. This will definitely be committed to the long term memory.
Override OnLayout() method and call your UpdateLayout() there.
Thanks for supplying the code, I would provide the properties by reusing already available controls.
public override Font Font
{
get { return this.label1.Font; }
set
{
this.label1.Font = value;
// Additional code to update related controls.
}
}
Also be aware that the declaration of
private Font _font;
Delivers a non-initialized variable, and by using it in the "Do_Layout" might use a null value. Maybe change it to the following when using your code.
this.label1.Font = this.Font;
We have a form which displays media items in tab pages of a tab control, and I'm implementing a feature which allows users to 'pop out' the tab pages into their own forms.
However, when I add the media player to a form rather than a TabPage, the background switches from the gradient fill of a tab page to the plain SystemColors.Control background of the parent form. I need to add the the media player to a control which has the same background as a TabControl, but which doesn't display a tab at the top. I tried adding the media player to the TabControl's control collection, but that just throws an exception.
How do I get a control which looks like a TabControl with no tabs? Should I keep trying to add the media player to a TabControl, or should I try to write a Panel with a custom-drawn background? If the latter, how do I make sure that works with all possible themes?
The questions seems to be about the UseVisbleBackgroundStyle. AFAIK only buttons and TabPages have this property.
The following is a very dirty hack, just to get you started:
1) derive a customControl from Panel and add "using System.Windows.Forms.VisualStyles;"
2) Add the following code
//warning: incomplete, add error checking etc
private readonly VisualStyleElement element = VisualStyleElement.Tab.Body.Normal;
public bool UseVisbleBackgroundStyle { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
if (UseVisbleBackgroundStyle)
{
var x = new VisualStyleRenderer(element);
x.DrawBackground(pe.Graphics, this.ClientRectangle);
}
else
{
base.OnPaint(pe);
}
}
Thanks to Henk - I eventually went with:
protected override void OnPaintBackground(PaintEventArgs e)
{
if (TabRenderer.IsSupported && Application.RenderWithVisualStyles)
{
TabRenderer.DrawTabPage(pe.Graphics, this.ClientRectangle);
}
else
{
base.OnPaintBackground(pe);
ControlPaint.DrawBorder3D(pe.Graphics, this.ClientRectangle, Border3DStyle.Raised);
}
}
Try creating your own customer UserControl
This answer is modified from another answer site. It does the trick rather cleanly.
In the load event for the window containing the tab control, try:
// TabControl is the name of the tab control in this window.
TabControl.Appearance = TabAppearance.FlatButtons;
TabControl.Multiline = false;
TabControl.SizeMode = TabSizeMode.Fixed;
TabControl.ItemSize = new Size(0,1);
// The tabs are now gone. Select the panel you want to display
TabControl.SelectTab("InProgressTab");
I've encountered an odd problem that doesn't make any sense to me. I am trying to dynamically set up MasterPage Content controls on a page. I have it working nicely with the following code:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
MasterPageFile = "~/MasterPages/Default.master";
string existantContentPlaceHolderID = "ContentPlaceHolder1";
string nonExistantContentPlaceHolderID = "foo";
//Control c = Master.FindControl(existantContentPlaceHolderID);
//Control c1 = Master.FindControl(nonExistantContentPlaceHolderID);
TextBox t = new TextBox
{
Text = "Text"
};
ITemplate iTemplate = new GenericITemplate(container => container.Controls.Add(t));
AddContentTemplate(existantContentPlaceHolderID, iTemplate);
}
public delegate void InstantiateTemplateDelegate(Control container);
public class GenericITemplate : ITemplate
{
private readonly InstantiateTemplateDelegate m_instantiateTemplate;
public void InstantiateIn(Control container)
{
m_instantiateTemplate(container);
}
public GenericITemplate(InstantiateTemplateDelegate instantiateTemplate)
{
m_instantiateTemplate = instantiateTemplate;
}
}
This works great, except I want to be able to double-check that the contentPlaceHolderIDs exist on the MasterPage before calling AddContentTemplate as the Page will throw an error if you add a Content control that points to a non-existing ContentPlaceHolder.
The problem I am having is that in the above example when I call one of the commented Master.FindControl lines, the TextBox no longer renders.
Does anyone have any ideas why this might be... I cannot makes heads or tails of what is going on.
Thanks,
Max
The problem is that AddContentTemplate just records its parameters in a hashtable ready to be combined with the master page instance when it is created. Calling it after the master page has been created won't do anything, and reading the Master property causes the master page to be created.
The best way I can see around this is to create a separate instance of the master page with LoadControl, which you can inspect without affecting the page's own Master property...
MasterPage testMaster = (MasterPage) LoadControl( MasterPageFile );
Control c = testMaster.FindControl(existantContentPlaceHolderID);
There's some overhead in creating a second instance, but it's not immediately obvious to me whether it will be worth worrying about.