public Canvas(CreateParams parentParams, XmlReader reader) { Initialize( ); // Always get our style first mStyle = parentParams.Style; Styles.Style.ParseStyleAttributesWithDefaults(reader, ref mStyle, ref ControlStyles.mCanvas); // check for attributes we support RectangleF bounds = new RectangleF( ); SizeF parentSize = new SizeF(parentParams.Width, parentParams.Height); ParseCommonAttribs(reader, ref parentSize, ref bounds); // Get margins and padding RectangleF padding; RectangleF margin; GetMarginsAndPadding(ref mStyle, ref parentSize, ref bounds, out margin, out padding); // apply margins to as much of the bounds as we can (bottom must be done by our parent container) ApplyImmediateMargins(ref bounds, ref margin, ref parentSize); Margin = margin; // check for border styling int borderPaddingPx = 0; if (mStyle.mBorderColor.HasValue) { BorderView.BorderColor = mStyle.mBorderColor.Value; } if (mStyle.mBorderRadius.HasValue) { BorderView.CornerRadius = mStyle.mBorderRadius.Value; } if (mStyle.mBorderWidth.HasValue) { BorderView.BorderWidth = mStyle.mBorderWidth.Value; borderPaddingPx = (int)Rock.Mobile.Graphics.Util.UnitToPx(mStyle.mBorderWidth.Value + PrivateNoteConfig.BorderPadding); } if (mStyle.mBackgroundColor.HasValue) { BorderView.BackgroundColor = mStyle.mBackgroundColor.Value; } // // now calculate the available width based on padding. (Don't actually change our width) float availableWidth = bounds.Width - padding.Left - padding.Width - (borderPaddingPx * 2); // now read what our children's alignment should be // check for alignment string result = reader.GetAttribute("ChildAlignment"); if (string.IsNullOrEmpty(result) == false) { switch (result) { case "Left": ChildHorzAlignment = Alignment.Left; break; case "Right": ChildHorzAlignment = Alignment.Right; break; case "Center": ChildHorzAlignment = Alignment.Center; break; default: ChildHorzAlignment = mStyle.mAlignment.Value; break; } } else { // if it wasn't specified, use OUR alignment. ChildHorzAlignment = mStyle.mAlignment.Value; } // Parse Child Controls bool finishedParsing = false; while (finishedParsing == false && reader.Read( )) { switch (reader.NodeType) { case XmlNodeType.Element: { // let each child have our available width. Style style = new Style( ); style = mStyle; style.mAlignment = ChildHorzAlignment; IUIControl control = Parser.TryParseControl(new CreateParams(this, availableWidth, parentParams.Height, ref style), reader); if (control != null) { ChildControls.Add(control); } break; } case XmlNodeType.EndElement: { // if we hit the end of our label, we're done. //if( reader.Name == "Canvas" || reader.Name == "C" ) if (ElementTagMatches(reader.Name)) { finishedParsing = true; } break; } } } // layout all controls float yOffset = bounds.Y + padding.Top + borderPaddingPx; //vertically they should just stack float height = 0; // now we must center each control within the stack. foreach (IUIControl control in ChildControls) { RectangleF controlFrame = control.GetFrame( ); RectangleF controlMargin = control.GetMargin( ); // horizontally position the controls according to their // requested alignment Alignment controlAlignment = control.GetHorzAlignment( ); // adjust by our position float xAdjust = 0; switch (controlAlignment) { case Alignment.Center: xAdjust = bounds.X + ((availableWidth / 2) - (controlFrame.Width / 2)); break; case Alignment.Right: xAdjust = bounds.X + (availableWidth - (controlFrame.Width + controlMargin.Width)); break; case Alignment.Left: xAdjust = bounds.X; break; } // adjust the next sibling by yOffset control.AddOffset(xAdjust + padding.Left + borderPaddingPx, yOffset); // track the height of the grid by the control lowest control height = (control.GetFrame( ).Bottom + +controlMargin.Height) > height ? (control.GetFrame( ).Bottom + +controlMargin.Height) : height; } // we need to store our bounds. We cannot // calculate them on the fly because we // would lose any control defined offsets, which would throw everything off. bounds.Height = height + padding.Height + borderPaddingPx; // setup our bounding rect for the border bounds = new RectangleF(bounds.X, bounds.Y, bounds.Width, bounds.Height); // and store that as our bounds BorderView.Frame = bounds; Frame = bounds; // store our debug frame SetDebugFrame(Frame); // sort everything ChildControls.Sort(BaseControl.Sort); }
void ParseNote(XmlReader reader, float parentWidthUnits, float parentHeightUnits) { DeviceHeight = parentHeightUnits; // get the style first Styles.Style.ParseStyleAttributesWithDefaults(reader, ref mStyle, ref ControlStyles.mMainNote); // check for attributes we support RectangleF bounds = new RectangleF( ); SizeF parentSize = new SizeF(parentWidthUnits, parentHeightUnits); Parser.ParseBounds(reader, ref parentSize, ref bounds); // Parent note doesn't support margins. // PADDING float leftPadding = Styles.Style.GetValueForNullable(mStyle.mPaddingLeft, parentWidthUnits, 0); float rightPadding = Styles.Style.GetValueForNullable(mStyle.mPaddingRight, parentWidthUnits, 0); float topPadding = Styles.Style.GetValueForNullable(mStyle.mPaddingTop, parentHeightUnits, 0); float bottomPadding = Styles.Style.GetValueForNullable(mStyle.mPaddingBottom, parentHeightUnits, 0); Padding = new RectangleF(leftPadding, rightPadding, topPadding, bottomPadding); // now calculate the available width based on padding. (Don't actually change our width) float availableWidth = parentWidthUnits - leftPadding - rightPadding; // A "special" (we won't call this a hack) attribute that will enable the user // to have a header container that spans the full width of the note, which allows // it to be unaffected by the padding. string result = reader.GetAttribute("FullWidthHeader"); if (string.IsNullOrEmpty(result) == false) { mStyle.mFullWidthHeader = bool.Parse(result); } // begin reading the xml stream bool finishedReading = false; while (finishedReading == false && reader.Read( )) { switch (reader.NodeType) { case XmlNodeType.Element: { float workingWidth = availableWidth; if (Header.ElementTagMatches(reader.Name) == true && mStyle.mFullWidthHeader == true) { workingWidth = parentWidthUnits; } IUIControl control = Parser.TryParseControl(new BaseControl.CreateParams(this, workingWidth, parentHeightUnits, ref mStyle), reader); ChildControls.Add(control); break; } case XmlNodeType.EndElement: { if (reader.Name == "Note") { finishedReading = true; } break; } } } // lay stuff out vertically. If the notes were built by hand, like a stackPanel. // if not by hand, like a canvas. float noteHeight = bounds.Y + topPadding; foreach (IUIControl control in ChildControls) { RectangleF controlFrame = control.GetFrame( ); RectangleF controlMargin = control.GetMargin( ); // horizontally position the controls according to their // requested alignment Alignment controlAlignment = control.GetHorzAlignment( ); // adjust by our position float xAdjust = 0; switch (controlAlignment) { case Alignment.Center: { xAdjust = bounds.X + ((availableWidth / 2) - (controlFrame.Width / 2)); break; } case Alignment.Right: { xAdjust = bounds.X + (availableWidth - (controlFrame.Width + controlMargin.Width)); break; } case Alignment.Left: { xAdjust = bounds.X; break; } } // place this next control at yOffset. yOffset should be the current noteHeight, which makes each control relative to the one above it. float yOffset = noteHeight; // if it's the header and full width is specified, don't apply padding. if (control as Header != null && mStyle.mFullWidthHeader == true) { control.AddOffset(xAdjust, yOffset); } else { control.AddOffset(xAdjust + leftPadding, yOffset); } // update the note height noteHeight = control.GetFrame( ).Bottom + controlMargin.Height; } bounds.Width = parentWidthUnits; bounds.Height = (noteHeight - bounds.Y) + bottomPadding; Frame = bounds; AddControlsToView( ); }