I'd like to make a radio button group that will be like this when nothing is checked already using BEMCheckBoxes:
It appears to me that I can't simply add the number in the button.
So I decided to make a view with a label and a checkbox centered in it.
Here is the code I have so far:
// Add An horizontal stackView with check group
UIStackView uIStackView = new UIStackView();
uIStackView.TranslatesAutoresizingMaskIntoConstraints = false;
uIStackView.Axis = UILayoutConstraintAxis.Horizontal;
uIStackView.Alignment = UIStackViewAlignment.Center;
qolEvaluationCard.AddSubview(uIStackView);
uIStackView.Anchor(top: criteria.BottomAnchor, leading: criteria.LeadingAnchor, trailing: criteria.TrailingAnchor, size: new CGSize(0, 32));
BEMCheckBoxGroup bEMCheckBoxGroup = new BEMCheckBoxGroup();
bEMCheckBoxGroups.Add(bEMCheckBoxGroup);
for (int score= 1; score <= 10; score++)
{
var container = new UIView(new CGRect(0, 0, 32, 32));
var label = new UILabel();
label.Text = $#"{score}";
label.TintColor = UIColor.Black;
container.AddSubview(label);
var checkbox = new BEMCheckBox(new CGRect(0, 0, 32, 32));
container.AddSubview(checkbox);
checkbox.BoxType = BEMBoxType.Circle;
checkbox.OnAnimationType = BEMAnimationType.Stroke;
checkbox.Center = label.Center = new CGPoint(container.Frame.Size.Width / 2, container.Frame.Size.Height / 2);
// Transparent BackgroundColor
checkbox.BackgroundColor = null;
checkbox.TintColor = UIColor.Gray;
checkbox.OnTintColor = optimistic_orange;
bEMCheckBoxGroup.AddCheckBoxToGroup(checkbox);
uIStackView.AddArrangedSubview(container);
}
The result is not what I expect but I can't figure out what is wrong:
Why are my checkboxes not transparent to let appear the label?
There is also a problem of distribution within the UIStackView obviously.
Any help appreciated.
The Anchor extension method is:
internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size = default)
{
uIView.TranslatesAutoresizingMaskIntoConstraints = false;
if (top != null)
{
uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
}
if (leading != null)
{
uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true;
}
if (bottom != null)
{
uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
}
if (trailing != null)
{
uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
}
if (size.Width != 0)
{
uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
}
if (size.Height != 0)
{
uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
}
}
EDIT 1: Just found out with the help of the Reveal tool that My labels size was ambiguous.
var label = new UILabel();
becomes
var label = new UILabel(new CGRect(0, 0, 32, 32));
This solves the 'invisible label' issue.
my answer is Swift codes, but give You concept of works.
I suggest You to break down problem to simple steps.
just try to layout checkboxes (BEMCheckBox) correctly on UIStackView, then create custom View that contain UILabel + BEMCheckBox and now add them to UIStackView and review layout.
Why label not appear on checkbox?
because you add label first, then you put checkbox on it.
container.addSubview(checkbox)
container.addSubview(label)
Setup StackView:
to achive checkboxes horizontal layout
let stackView: UIStackView = {
let stack = UIStackView(arrangedSubviews: customCheckboxes)
stack.spacing = 8
stack.distribution = .fillEqually
stack.axis = .horizontal
stack.alignment = .fill
return stack
}()
then snap stackView to corners using autoLayout or whatever
I think using UIStackView is the best choice, don't use UICollectionView for this simple condition.
How to update label state when checkbox selected?
I didn't use BEMCheckBoxGroup before, but just reviewed source codes and there is a function notifyCheckBoxSelectionChanged trigger when checkbox selection change.
func notifyCheckBoxSelectionChanged(_ checkBox: BEMCheckBox) {
if checkBox.on {
// Change selected checkbox to this one
selectedCheckBox = checkBox
} else if checkBox == selectedCheckBox {
// Selected checkbox was this one, clear it
selectedCheckBox = nil
}
}
but it's not pubic, selectedCheckBox is what you need to know about this group. I think you should add target on valueChanged to this group, I don't see any public API to delegate user selection of checkboxes!
Related
I am having an issue where my UISearchBar does not resize on phone rotation unless I touch on the search bar so that it has focus (see images below).
The search bar is created and added to a UIMapView as a subview. See code.
Searchbar creation:
public UISearchController DefineSearchController()
{
_searchResultsController = new SearchResultsVC(_mapView);
_searchResultsController.searchItemSelected += PlaceSelect;
_searchUpdater = new SearchResultsUpdator();
_searchUpdater.UpdateSearchResults += _searchResultsController.Search;
//add the search controller
var searchController = new UISearchController(_searchResultsController)
{
SearchResultsUpdater = _searchUpdater
};
var scb = searchController.SearchBar;
scb.SizeToFit();
scb.SearchBarStyle = UISearchBarStyle.Minimal;
var img = UIImage.FromBundle("tabSpace");
scb.SetBackgroundImage(img, UIBarPosition.Top, UIBarMetrics.Default);
var textField = scb.ValueForKey(new NSString("searchField")) as UITextField;
if (textField != null)
{
var backgroundView = textField.Subviews[0];
if (backgroundView != null)
{
backgroundView.BackgroundColor = UIColor.White;
backgroundView.Layer.BorderColor = AppColour.PersianBlue.GetUIColour().CGColor;
backgroundView.Layer.BorderWidth = 1;
backgroundView.Layer.CornerRadius = 10;
backgroundView.ClipsToBounds = true;
}
}
var localEnterPoI = NSBundle.MainBundle.LocalizedString("placeHolderSearchForLocation", "Enter a PoI to search for");
scb.Placeholder = localEnterPoI;
searchController.Delegate = new SearchControllerDelegate();
searchController.HidesNavigationBarDuringPresentation = false;
return searchController;
}
Added to the subview:
//Define Search Controller
_mapSearchManager = new MapSearchManager(_mapView);
_searchController = _mapSearchManager.DefineSearchController();
var scb = _searchController.SearchBar;
_mapView.AddSubview(scb);
NSLayoutConstraint.ActivateConstraints(new NSLayoutConstraint[]{
scb.TopAnchor.ConstraintEqualTo(_mapView.TopAnchor, 30),
scb.LeadingAnchor.ConstraintEqualTo(_mapView.LeadingAnchor, 10),
scb.TrailingAnchor.ConstraintEqualTo(_mapView.LeadingAnchor, -10),
});
I heave search extensively and was only able to find one similar issue:
UISearchBar doesn't resize when frame is resized in IOS 11
I implementing both of suggestion but it didn't make any difference.
Has anyone else encounted this or know what a possible solution might be.
Cheers
There are 2 lists called listversion & MIN_list. Using values of these list I have created a line chart. Everything is work fine. But I am wondering whether it is possible to add a data table with legend keys in to the chart like MS Excel.
chart.Series.Clear();
chart.ChartAreas[0].AxisX.Title = "Version";
chart.ChartAreas[0].AxisX.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
chart.ChartAreas[0].AxisY.Title = "Time";
chart.ChartAreas[0].AxisY.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
Series MIN = chart.Series.Add("Minimum");
MIN.Points.DataBindXY(listVersion, MIN_list[j]);
MIN.ChartType = SeriesChartType.Line;
MIN.Color = Color.Red;
MIN.BorderWidth = 3;
I am looking forward to something like this
If it is possible How can I do it ?
Thank you.
Yes you can do that:
Here are the steps I took:
First we disable the original Legend as it can't be manipulated the way we need to..:
chart1.Legends[0].Enabled = false;
Now we create a new one and a shortcut reference to it:
chart1.Legends.Add(new Legend("customLegend"));
Legend L = chart1.Legends[1];
Next we do some positioning:
L.DockedToChartArea = chart1.ChartAreas[0].Name; // the ca it refers to
L.Docking = Docking.Bottom;
L.IsDockedInsideChartArea = false;
L.Alignment = StringAlignment.Center;
Now we want to fill in one line for headers and one line per series.
I use a common function for both and pass in a flag to indicate whether the headers (x-values) or the cell data (y-values) should be filled in. Here is how I call the function:
addValuesToLegend(L, chart1.Series[0], false);
foreach (Series s in chart1.Series) addValuesToLegend(L, s, true);
Note that for this to work we need a few preparations in our Series:
We need to set the Series.Colors explicitly or else we can't refer to them.
I have added a format string to the Tag of each series; but maybe you find a better solution that avoids hard-coding the format for the header..
So here is the function that does all the filling and some styling:
void addValuesToLegend(Legend L, Series S, bool addYValues)
{
// create a new row for the legend
LegendItem newItem = new LegendItem();
// if the series has a markerstyle we show it:
newItem.MarkerStyle = S.MarkerStyle ;
newItem.MarkerColor = S.Color;
newItem.MarkerSize *= 2; // bump up the size
if (S.MarkerStyle == MarkerStyle.None)
{
// no markerstyle so we just show a colored rectangle
// you could add code to show a line for other chart types..
newItem.ImageStyle = LegendImageStyle.Rectangle;
newItem.BorderColor = Color.Transparent;
newItem.Color = S.Color;
}
else newItem.ImageStyle = LegendImageStyle.Marker;
// the rowheader shows the marker or the series color
newItem.Cells.Add(LegendCellType.SeriesSymbol, "", ContentAlignment.MiddleCenter);
// add series name
newItem.Cells.Add(LegendCellType.Text, addYValues ? S.Name : "",
ContentAlignment.MiddleLeft);
// combine the 1st two cells:
newItem.Cells[1].CellSpan = 2;
// we hide the first cell of the header row
if (!addYValues)
{
newItem.ImageStyle = LegendImageStyle.Line;
newItem.Color = Color.Transparent;
newItem.Cells[0].Tag = "*"; // we mark the 1st cell for not painting it
}
// now we loop over the points:
foreach (DataPoint dp in S.Points)
{
// we format the y-value
string t = dp.YValues[0].ToString(S.Tag.ToString());
// or maybe the x-value. it is a datatime so we need to convert it!
// note the escaping to work around my european locale!
if (!addYValues) t = DateTime.FromOADate(dp.XValue).ToString("M\\/d\\/yyyy");
newItem.Cells.Add(LegendCellType.Text, t, ContentAlignment.MiddleCenter);
}
// we can create some white space around the data:
foreach (var cell in newItem.Cells) cell.Margins = new Margins(25, 20, 25, 20);
// finally add the row of cells:
L.CustomItems.Add(newItem);
}
To draw the borders around the cells of our legend table we need to code the PrePaint event:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
LegendCell cell = e.ChartElement as LegendCell;
if (cell != null && cell.Tag == null)
{
RectangleF r = e.ChartGraphics.GetAbsoluteRectangle(e.Position.ToRectangleF());
e.ChartGraphics.Graphics.DrawRectangle(Pens.DimGray,Rectangle.Round(r));
// Let's hide the left border when there is a cell span!
if (cell.CellSpan != 1)
e.ChartGraphics.Graphics.DrawLine(Pens.White,
r.Left, r.Top+1, r.Left, r.Bottom-1);
}
}
You can add more styling although I'm not sure if you can match the example perfectly..
I have a charting application that has an overlay function which reassigns the 'from' chart series to the 'to' chart using this code :
chTo.Series.Add(chFrom.Series[s]); //Reassign series to new chart
chTo.Legends.Add(chFrom.Legends[s]); //Reassign legend to new chart
Works great. However, I am trying to implement tooltips for the legends and am running into an issue where only the first legend in the chart will show tooltips. When I do a hittest only the first legend is recognized. All subsequent legends, while visible on the chart, aren't 'seen' to the hittest method. I'm thinking this is why the tooltips aren't showing as there is no object to trigger the mouseover event for the tooltip.
I have been unable to find a way to 'expand' the legend area (as detected by the hittest method) to make this work.
Does anyone have any ideas? Thanks!
Responding to King King --
The original legend is created in the same method as the chart thus:
//Create the series legend
chartSel.Series[ySeries.Name].ChartArea = "ChartArea1";
chartSel.Legends.Remove(chartSel.Legends.FindByName("Legend1"));
chartSel.Legends.Add(ySeries.Name);
chartSel.Legends[0].Name = ySeries.Name;
//Format the series legend
chartSel.Legends[ySeries.Name].Docking = Docking.Right;
chartSel.Legends[ySeries.Name].DockedToChartArea = "ChartArea1";
chartSel.Legends[ySeries.Name].Alignment = StringAlignment.Near;
chartSel.Legends[ySeries.Name].IsDockedInsideChartArea = false;
chartSel.Legends[ySeries.Name].LegendStyle = LegendStyle.Table; //.Row;
chartSel.Legends[ySeries.Name].TableStyle = LegendTableStyle.Tall;
chartSel.Legends[ySeries.Name].IsEquallySpacedItems = false;
chartSel.Legends[ySeries.Name].Font = new Font("Segoe UI", 7, FontStyle.Bold);
//chartSel.Legends[ySeries.Name].TextWrapThreshold = 17; // 19;
chartSel.Legends[ySeries.Name].Position.Auto = false;
chartSel.Legends[ySeries.Name].Position.X = 80;
chartSel.Legends[ySeries.Name].Position.Y = 2;
chartSel.Legends[ySeries.Name].Position.Width = 18;
chartSel.Legends[ySeries.Name].Position.Height = 12;
//Format series data point value cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[0].Alignment = ContentAlignment.MiddleLeft; //.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[0].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(10, 10, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[0].MinimumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].MaximumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].BackColor = Color.FromArgb(120, chartSel.Series[ySeries.Name].Color);
//Format legend cell spacer
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[1].Alignment = ContentAlignment.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[1].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 0, 0);
chartSel.Legends[ySeries.Name].CellColumns[1].MinimumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].MaximumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].BackColor = Color.Black;
//Format series title cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ySeries.Name));
chartSel.Legends[ySeries.Name].CellColumns[2].Alignment = ContentAlignment.MiddleLeft;
chartSel.Legends[ySeries.Name].CellColumns[2].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[2].MinimumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].MaximumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].ToolTip = ySeries.Name;
After the series and legends have been reassigned (using the code in my original post) I then set the legend values based on the cursor position located by the following hittest in response to a mouse-down event:
pt = activePanel.PointToClient(Control.MousePosition);
ch = activePanel.GetChildAtPoint(pt) as Chart;
if (ch != null)
{
HitTestResult ht = ch.HitTest(e.X, e.Y, false);
if (ht.ChartElementType == ChartElementType.PlottingArea)
{
SetLegendValueText(ht, ch);
}
}
private void SetLegendValueText(HitTestResult ht, Chart ch)
{
//Get the datapoint 'x' index value
int dpIndex = 0;
if (ht != null)
{
switch (ht.ChartElementType)
{
case ChartElementType.DataPoint: //Cursor is on a series line
DataPoint dp = ht.Object as DataPoint;
if (dp != null)
{
dpIndex = ht.PointIndex;
}
break;
case ChartElementType.PlottingArea: //Cursor is somewhere in the plot area of the chart
dpIndex = (int)ht.ChartArea.CursorX.Position;
break;
}
}
//Set legend value and legend tooltip
for (int x = 0; x < ch.Legends.Count; x++) //foreach (Series s in ch.Series)
{
if (dpIndex > 0)
{
ch.Legends[x].Name = "Legend_" + x;
ch.Legends[x].CellColumns[0].Text = ch.Series[x].Points[dpIndex - 1].YValues[0].ToString();
ch.Legends[x].CellColumns[0].ToolTip = ch.Legends[x].CellColumns[0].Text;
}
}
}
So, I end up with the legends looking the way I want them, but the tooltips only show for the first legend item. I've tried to do custom items as well. With them I get the tooltips, but I lose the formatting. This has been driving me crazy for weeks (off and on) and I would really like to move on to other issues. Clearly (to me anyway), I am not doing something right simply because I don't know everything there is to know about the charts, and the MSChart Samples are of very limited benefit.
I'd be most grateful if I could be pointed in the right direction.
I've got the following UIActionSheet.
How do I add it to my top navigation bar?
Preferably as the far right button.
var sheet = new UIActionSheet ("");
sheet.AddButton ("Discard Picture");
sheet.AddButton ("Pick New Picture");
sheet.AddButton ("Cancel");
sheet.CancelButtonIndex = 2;
// Dummy buttons to preserve the space for the UIImageView
for (int i = 0; i < 4; i++) {
sheet.AddButton("");
sheet.Subviews[i+4].Alpha = 0; // And of course it's better to hide them
}
var subView = new UIImageView();
subView.ContentMode = UIViewContentMode.ScaleAspectFill;
subView.Frame = new RectangleF(23,185,275,210);
// Late Steve Jobs loved rounded corners. Let's have some respect for him
subView.Layer.CornerRadius = 10;
subView.Layer.MasksToBounds = true;
subView.Layer.BorderColor = UIColor.Black.CGColor;
sheet.AddSubview(subView);
NavigationController.Add(sheet);
You can show an ActionSheet using the ShowFrom methods.
In particular, ShowFromToolbar shows the sheet from the top toolbar button.
Here's an example which shows the sheet in different ways depending on whether you are on tablet or phone:
void ActionMenu()
{
//_actionSheet = new UIActionSheet("");
UIActionSheet actionSheet = new UIActionSheet (
"Customer Actions",
null,
"Cancel",
"Delete Customer",
new string[] {"Change Customer"});
actionSheet.Style = UIActionSheetStyle.Default;
actionSheet.Clicked += delegate(object sender, UIButtonEventArgs args) {
switch (args.ButtonIndex)
{
case 0: DeleteCustomer(); break;
case 1: ChangeCustomer(); break;
}
};
if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
actionSheet.ShowFromToolbar(NavigationController.Toolbar);
else
actionSheet.ShowFrom(NavigationItem.RightBarButtonItem, true);
}
https://github.com/slodge/MvvmCross-Tutorials/blob/master/Sample%20-%20CustomerManagement/CustomerManagement/CustomerManagement.Touch/Views/CustomerView.cs#L67
I have an app that is connected to a remote server and polling data when needed. It has a TreeView where the Nodes represent the objects that are available and the color of the text indicate whether the data has been loaded or not; gray-italicized indicates not loaded, black, regular text is loaded.
Currently I have set the TreeView to be OwnderDrawText and have the TreeView.DrawNode function simply draw the text as so:
private void TreeViewDrawNode(object sender, DrawTreeNodeEventArgs e)
{
if (!e.Node.IsVisible)
{
return;
}
bool bLoaded = false;
if (e.Bounds.Location.X >= 0 && e.Bounds.Location.Y >= 0)
{
if(e.Node.Tag != null)
{
//...
// code determining whether data has been loaded is done here
// setting bLoaded true or false
//...
}
else
{
e.DrawDefault = true;
return;
}
Font useFont = null;
Brush useBrush = null;
if (bLoaded)
{
useFont = e.Node.TreeView.Font;
useBrush = SystemBrushes.WindowText;
}
else
{
useFont = m_grayItallicFont;
useBrush = SystemBrushes.GrayText;
}
e.Graphics.DrawString(e.Node.Text, useFont, useBrush, e.Bounds.Location);
}
}
I figured that would be enough, however, this has been causing some issues;
When a node is selected, focused or not, it doesn't envelop all of the text, example (I hope imgur is ok).
When the node is focused, the dotted outline doesn't show either. If you compare it with this example. The nodes with the "log" in the text are using the e.DefaultDraw = true
I tried following the example given in this question. It looked something like this:
private void TreeViewDrawNode(object sender, DrawTreeNodeEventArgs e)
{
if (!e.Node.IsVisible)
{
return;
}
bool bLoaded = false;
if (e.Bounds.Location.X >= 0 && e.Bounds.Location.Y >= 0)
{
if(e.Node.Tag != null)
{
//...
// code determining whether data has been loaded is done here
// setting bLoaded true or false
//...
}
else
{
e.DrawDefault = true;
return;
}
//Select the font and brush depending on whether the property has been loaded
Font useFont = null;
Brush useBrush = null;
if (bLoaded)
{
useFont = e.Node.TreeView.Font;
useBrush = SystemBrushes.WindowText;
}
else
{
//member variable defined elsewhere
useFont = m_grayItallicFont;
useBrush = SystemBrushes.GrayText;
}
//Begin drawing of the text
//Get the rectangle that will be used to draw
Rectangle itemRect = e.Bounds;
//Move the rectangle over by 1 so it isn't on top of the check box
itemRect.X += 1;
//Figure out the text position
Point textStartPos = new Point(itemRect.Left, itemRect.Top);
Point textPos = new Point(textStartPos.X, textStartPos.Y);
//generate the text rectangle
Rectangle textRect = new Rectangle(textPos.X, textPos.Y, itemRect.Right - textPos.X, itemRect.Bottom - textPos.Y);
int textHeight = (int)e.Graphics.MeasureString(e.Node.Text, useFont).Height;
int textWidth = (int)e.Graphics.MeasureString(e.Node.Text, useFont).Width;
textRect.Height = textHeight;
//Draw the highlighted box
if ((e.State & TreeNodeStates.Selected) != 0)
{
//e.Graphics.FillRectangle(SystemBrushes.Highlight, textRect);
//use pink to see the difference
e.Graphics.FillRectangle(Brushes.Pink, textRect);
}
//widen the rectangle by 3 pixels, otherwise all of the text won't fit
textRect.Width = textWidth + 3;
//actually draw the text
e.Graphics.DrawString(e.Node.Text, useFont, useBrush, e.Bounds.Location);
//Draw the box around the focused node
if ((e.State & TreeNodeStates.Focused) != 0)
{
textRect.Width = textWidth;
Pen focusPen = new Pen(Color.Black);
focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
e.Graphics.DrawRectangle(focusPen, textRect);
}
}
}
However, the results were this. (Note, used pink to differentiate the colors). As you can see, the highlighted background doesn't extend all the way to where the focused dotted line is at. And there's also another box that is drawn as well.
I'm slightly stumped on how to fix this. All I want is to have gray italicized text when something is loaded. The first and simplest approach doesn't quite work and the second method feels like I'm doing way too much.
After all that, does anyone have any suggestions on how to do this properly, because there's got to be a simpler way.
Thank you in advance.
You'll need to use TextRenderer.DrawText(). That's what TreeView uses, it renders text slightly different from Graphics.DrawString().