Xamarin.Forms Collection View - c#

What would be the best way to go about re-creating a view like iOS' UICollectionView in Xamarin.Forms? I need it to be cross platform. The two options i thought of right off the bat are using Xamarin.Forms.Grid, and Xamarin.Forms.Listview (Customizing the cells to have 3 "Columns"). Any other ideas or input? This is going to be used for an image gallery by the way.
Thanks

You can use the following code to use the RelativeLayout to behave like a StackPanel
public class MyContentPage : ContentPage
{
public MyContentPage()
{
var layout = new RelativeLayout();
var box1 = new ContentView
{
BackgroundColor = Color.Gray,
Content = new Label
{
Text = "0"
}
};
double padding = 10;
layout.Children.Add( box1, () => new Rectangle(((layout.Width + padding) % 60) / 2, padding, 50, 50));
var last = box1;
for (int i = 0; i < 200; i++)
{
var relativeTo = last; // local copy
var box = new ContentView
{
BackgroundColor = Color.Gray,
Content = new Label
{
Text = (i + 1).ToString()
}
};
Func<View, bool> pastBounds = view => relativeTo.Bounds.Right + padding + view.Width > layout.Width;
layout.Children.Add(box, () => new Rectangle(pastBounds(relativeTo) ? box1.X : relativeTo.Bounds.Right + padding,
pastBounds(relativeTo) ? relativeTo.Bounds.Bottom + padding : relativeTo.Y,
relativeTo.Width,
relativeTo.Height));
last = box;
}
Content = new ScrollView { Content = layout, Padding = new Thickness(6) };
}
}

Related

Zoom Image using WPF MVVM, will move the top layer controls. How can it be fixed on image zoom time?

Currently i am working on image processing project, where i have one review screen in which i set the image on image container, and on top of the image, all four corners has meta data, which are fixed. Means while i zoom in/out the image, the top content will remain at the fixed position not move. This is my requirement.
Now i have set all content dynamically using the stack panel and all meta data will stay on fixed position except the bottom right position, which will be moved while image is zoomed in/out.
below is the reference screen shot where red mark shows the metadata will moved and hide at right side.
Here is the code for bottom right content
public async Task<ScrollViewer> CreateNewScrollViewer(ImageInfo item)
{
ScrollViewer scrollViewerObj = new ScrollViewer();
byte[] rawData;
try
{
//this will update the values of any older sharpness data to 5
if (item.AnnotationSchemaVersion != null && item.AnnotationSchemaVersion.Equals("1.0", StringComparison.InvariantCulture))
{
item.AdjustmentValue.Sharpness = item.BytePerPixel == 3 ? _samSettingsManager.GetTrueColorSharpnessDefaultValue() : _samSettingsManager.GetSingleColorSharpnessDefaultValue();
var saveSharpness = new Task(() => this.UpdateImageAnalysis(item));
saveSharpness.Start();
}
scrollViewerObj.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_ScrollViewer");
scrollViewerObj.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_ScrollViewer");
scrollViewerObj.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
scrollViewerObj.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
scrollViewerObj.Margin = new Thickness(5, 5, 5, 5);
scrollViewerObj.Focusable = true;
scrollViewerObj.SizeChanged += new SizeChangedEventHandler(ScrollViewerSizeChanged);
Point? fixationTarget = null;
Point? foveaXY = null;
Point? onhXY = null;
FixationType fixationMode = FixationType.Internal;
bool performDistortionCorrection = false;
// Provide fixation values only for WF and Non-External(Non-AnteriorSegment) scans, as distortion correction shall only be applied to WF and Non-External(Non-AnteriorSegment) images.
// All composite UWF/Montage/Auto-Montage images will be distortion corrected (Montage algorithm generates distortion corrected image)
if (Convert.ToInt32(item.FOV, CultureInfo.InvariantCulture) == FOVConstants.Widefield &&
item.ExamMode != ExamModes.AnteriorSegment && SAMConstants.SAMExamSourceUID == item.ExamSourceUID)
{
fixationTarget = item.FixationXY;
foveaXY = item.FoveaXY;
onhXY = item.ONHXY;
fixationMode = item.FixationMode;
performDistortionCorrection = true;
}
//bool isOD = item.Laterality == "OD" ? true : false;
string imageCacheFilePath = _imageCacheFilePath;
string imageFileName = System.IO.Path.GetFileName(item.ImagePath);
//creates the image cache folder if it doesn't exist
if (!Directory.Exists(imageCacheFilePath))
Directory.CreateDirectory(imageCacheFilePath);
ImageContainer imageObj = new ImageContainer(_pixelBufferSize);
await Task.Run(() =>
{
imageObj.Initialize(item.ImagePath, item.ImageCompressionType,
item.ImageWidth, item.ImageHeight, item.BytePerPixel, item.ExamSourceUID, item.Laterality, imageCacheFilePath, _pixelBufferSize, fixationTarget,
foveaXY, item.ProjectedXMin, item.ProjectedYMax, item.ProjectedXMax, item.ProjectedYMin, performDistortionCorrection,
onhXY, fixationMode, item.ONHIdentificationMode);
});
imageObj.InitializeZoomValues(((int)ActualHeight - 40) / 4, ((int)ActualWidth - 40) / 4);
PyramidTools.PyramidImageProcessing processImage = new PyramidTools.PyramidImageProcessing();
//Sets up the pyramid
if (!System.IO.File.Exists(imageCacheFilePath + imageFileName + ExtensionConstants.Raw) || (!System.IO.File.Exists(imageCacheFilePath + imageFileName + ExtensionConstants.Text) && SAMConstants.SAMExamSourceUID == imageObj.ExamSourceUID))
{
rawData = imageObj.ImageDataObj.GetData();
if (rawData != null)
{
processImage.CreatePyramidForGivenImage(rawData, item.BytePerPixel, (int)imageObj.ImageZoom.LowestZoomPercentage, imageFileName, imageObj.ImageDataObj.Width, imageObj.ImageDataObj.Height, imageCacheFilePath);
}
}
else if (!processImage.IsPyramidCreated(imageCacheFilePath + imageFileName, (int)imageObj.ImageZoom.LowestZoomPercentage))
{
rawData = File.ReadAllBytes(imageCacheFilePath + imageFileName + ExtensionConstants.Raw);
if (rawData != null)
{
processImage.CreatePyramidForGivenImage(rawData, item.BytePerPixel, (int)imageObj.ImageZoom.LowestZoomPercentage, imageFileName, imageObj.ImageDataObj.Width, imageObj.ImageDataObj.Height, imageCacheFilePath);
}
}
// For image sharpness
imageObj.ImageProcessing = imageProcessing;
imageObj.TrueColorSharpnessRadius = _trueColorSharpnessRadius;
imageObj.TrueColorSharpnessMinAmount = _trueColorSharpnessMinAmount;
imageObj.TrueColorSharpnessMaxAmount = _trueColorSharpnessMaxAmount;
imageObj.TrueColorSharpnessResizeFactor = _trueColorSharpnessResizeFactor;
imageObj.SingleColorSharpnessRadius = _singleColorSharpnessRadius;
imageObj.SingleColorSharpnessMinAmount = _singleColorSharpnessMinAmount;
imageObj.SingleColorSharpnessMaxAmount = _singleColorSharpnessMaxAmount;
imageObj.SingleColorSharpnessResizeFactor = _singleColorSharpnessResizeFactor;
imageObj.TrueColorSharpnessFactor = _trueColorSharpnessFactor;
imageObj.SingleColorSharpnessFactor = _singleColorSharpnessFactor;
imageObj.IsConstituteImage = item.IsConstituteImage;
imageObj.FOV = item.FOV;
imageObj.SelectedChannel = ChannelTypes.TrueColorChannel;
imageObj.TonalOptimizedValues = new Tonal(128, 128, 128);
imageObj.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_ImageContainer");
imageObj.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_ImageContainer");
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/SAMProduction.FundusImageDisplay;component/Images/RotateBlue.png");
logo.EndInit();
imageObj.ImageRotationShow = new System.Windows.Controls.Image();
imageObj.ImageRotationShow.Source = logo;
imageObj.ImageRotationShow.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_180DegreeIcon");
imageObj.ImageRotationShow.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_180DegreeIcon");
//imageObj.ImageRotationShow.SetResourceReference(Canvas.BackgroundProperty, "180DegreeIcon");
imageObj.ImageRotationShow.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
imageObj.ImageRotationShow.VerticalAlignment = System.Windows.VerticalAlignment.Top;
imageObj.ImageRotationShow.Visibility = System.Windows.Visibility.Collapsed;
imageObj.ImageRotationShow.Width = 30;
imageObj.ImageRotationShow.Height = 30;
imageObj.ImageRotationShow.Margin = new Thickness(0, 10, 0, 0);
imageObj.ImageFrameNoMessage = item.ImageFrameNoMessage;
Style textBlockStyle = this.TryFindResource("TextWhite16") as Style;
#region Top Left Panel Information
StackPanel topLeftPanelInfo = new StackPanel();
topLeftPanelInfo.Width = 160;
topLeftPanelInfo.Name = "TopLeftPanel";
topLeftPanelInfo.Uid = "TopLeftPanel";
topLeftPanelInfo.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
topLeftPanelInfo.VerticalAlignment = System.Windows.VerticalAlignment.Top;
topLeftPanelInfo.Margin = new Thickness(10, 5, 0, 0);
UpdateTopLeftPanel(item, imageObj, topLeftPanelInfo, textBlockStyle);
#endregion
#region Bottom Left Panel Information
StackPanel bottomLeftPanelInfo = new StackPanel();
bottomLeftPanelInfo.Name = "BottomLeftPanel";
bottomLeftPanelInfo.Uid = "BottomLeftPanel";
bottomLeftPanelInfo.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
bottomLeftPanelInfo.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
bottomLeftPanelInfo.Margin = new Thickness(10, 0, 0, 5);
UpdateBottomLeftPanel(item, imageObj, bottomLeftPanelInfo, textBlockStyle);
#endregion
#region Bottom Right Panel Information
StackPanel bottomRightPanelInfo = new StackPanel();
bottomRightPanelInfo.Name = "BottomRightPanel";
bottomRightPanelInfo.Uid = "BottomRightPanel";
bottomRightPanelInfo.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_BottomRightPanelInfo");
bottomRightPanelInfo.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_BottomRightPanelInfo");
bottomRightPanelInfo.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
bottomRightPanelInfo.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
bottomRightPanelInfo.Margin = new Thickness(0, 0, 10, 5);
UpdateBottomRightPanel(item, imageObj, bottomRightPanelInfo, textBlockStyle);
#endregion
#region Top Right Panel Information
StackPanel topRightPanelInfo = new StackPanel();
topRightPanelInfo.Name = "TopRightPanel";
topRightPanelInfo.Uid = "TopRightPanel";
topRightPanelInfo.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_TopRightPanelInfo");
topRightPanelInfo.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_TopRightPanelInfo");
topRightPanelInfo.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
topRightPanelInfo.VerticalAlignment = System.Windows.VerticalAlignment.Top;
topRightPanelInfo.Margin = new Thickness(0, 5, 10, 0);
UpdateTopRightPanel(item, imageObj, topRightPanelInfo, textBlockStyle);
#endregion
Grid gridObj = new Grid() { ClipToBounds = true };//
gridObj.SetValue(AutomationProperties.AutomationIdProperty, "ReviewImageDetailView_Grid");
gridObj.SetValue(AutomationProperties.NameProperty, "ReviewImageDetailView_Grid");
gridObj.Children.Add(imageObj);
if (topLeftPanelInfo != null)
{
gridObj.Children.Add(topLeftPanelInfo);
}
if (bottomLeftPanelInfo != null)
{
gridObj.Children.Add(bottomLeftPanelInfo);
}
if (topRightPanelInfo != null)
{
gridObj.Children.Add(topRightPanelInfo);
}
if (bottomRightPanelInfo != null)
{
gridObj.Children.Add(bottomRightPanelInfo);
}
gridObj.Children.Add(imageObj.SelectedBorder);
gridObj.Children.Add(imageObj.ImageRotationShow);
gridObj.Children.Add(imageObj.OverlayGrid);
imageObj.OverlayGrid.MouseLeftButtonUp += OverlayGrid_MouseLeftButtonUp;
imageObj.OverlayGrid.MouseMove += OverlayGrid_MouseMove;
imageObj.OverlayGrid.MouseLeftButtonDown += OverlayGrid_MouseLeftButtonDown;
imageObj.OverlayGrid.PreviewMouseRightButtonDown += OverlayGrid_PreviewMouseRightButtonDown;
imageObj.OverlayGrid.PreviewMouseRightButtonUp += OverlayGrid_PreviewMouseRightButtonUp;
scrollViewerObj.Content = gridObj;
// This binding required to align image properly when it is loading.
Binding HeightBinding = new Binding();
RelativeSource relativeheightSource = new RelativeSource();
relativeheightSource.Mode = RelativeSourceMode.FindAncestor;
relativeheightSource.AncestorType = typeof(ScrollViewer);
HeightBinding.RelativeSource = relativeheightSource;
HeightBinding.Path = new PropertyPath("ActualHeight");
HeightBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
HeightBinding.Mode = BindingMode.OneWay;
imageObj.SetBinding(System.Windows.Controls.Image.HeightProperty, HeightBinding);
Binding WidthBinding = new Binding();
RelativeSource relativeWidthSource = new RelativeSource();
relativeWidthSource.Mode = RelativeSourceMode.FindAncestor;
relativeWidthSource.AncestorType = typeof(ScrollViewer);
WidthBinding.RelativeSource = relativeWidthSource;
WidthBinding.Path = new PropertyPath("ActualWidth");
WidthBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
WidthBinding.Mode = BindingMode.OneWay;
imageObj.SetBinding(System.Windows.Controls.Image.WidthProperty, WidthBinding);
imageObj.ImageSourceChanged += this.ImageObj_ImageSourceChanged;
imageObj.ZoomValueChanged += this.ImageObj_ZoomValueChanged;
scrollViewerObj.AllowDrop = true;
scrollViewerObj.Drop += ScrollViewer_Drop;
imageObj.ContainerTonalValueChanged += ImageObj_ContainerTonalValueChanged;
// Previously MouseLeftButtonDown event was used.
// Change set no 141851 has change code of CreateNewScrollViewer(). He set Scrollviewer’s Focable property to true.
// It is required to set focusable true for that change set.
// In ScrollViewer's original code (.net code) it is handling event (e.Handled = true) if it can get focus.
// So side effect of 141851 change set is MouseLeftButtonDown event of scrollviewer do not get call when mouse down on it.
// So it misbehaves.
// So here PreviewMouseLeftButtonDown event used.
scrollViewerObj.PreviewMouseLeftButtonDown += ScrollViewerObj_PreviewMouseLeftButtonDown;
scrollViewerObj.MouseLeftButtonUp += this.ScrollViewerObj_MouseLeftButtonUp;
scrollViewerObj.PreviewMouseWheel += ScrollViewerObj_PreviewMouseWheel;
// No need to handle this event
//imageObj.SizeChanged += this.ImageObj_SizeChanged;
imageObj.MouseMove += this.ImageObj_MouseMove;
imageObj.MouseLeftButtonDown += this.ImageObj_MouseLeftButtonDown;
imageObj.MouseLeftButtonUp += this.ImageObj_MouseLeftButtonUp;
gridObj.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
gridObj.VerticalAlignment = System.Windows.VerticalAlignment.Center;
//It is not necessary to initalize the data
imageObj.ImageID = item.ImageId;
//imageObj.ImagePath = item.ImagePath;
if (imageObj.ExamSourceUID == SAMConstants.SAMExamSourceUID &&
imageObj.ExamMode != ExamModes.AnteriorSegment)
imageObj.OverlayGrid.IsHitTestVisible = IsOverlayGridEnable;
else
imageObj.OverlayGrid.IsHitTestVisible = false;
// imageObj.OverlayGrid.IsHitTestVisible = IsOverlayGridEnable;
//imageObj.ImageSource = imageObj.ImageDataObj;
scrollViewerObj.ContextMenu = CreateContextMenu(scrollViewerObj, imageObj); // set contextmenu
}
catch (Exception ex)
{
_services.Logger.Log(LogLevel.Error, "SAMProduction.FundusImageDisplay", "ImageEditViewerBase", "CreateNewScrollViewer: Error occurred while creating new scrollviewer. : " + ex);
}
finally
{
rawData = null;
}
return scrollViewerObj;
}
Please, let me help out to resolve the moved content on top layer of image, if any one can do. Thanks in Advance.
I find creating views in code very unreadable, so this is how to achieve this in XAML:
<Grid>
<Image .../> // or whatever custom control you use to allow zoom
<StackPanel Name="TopLeft HorizontalAligment="Left" VerticalAligment="Top">
<contente: labels etc ../>
</StackPanel>
...
<StackPanel Name="BottomRight" HorizontalAligment="Right" VerticalAligment="Bottom">
<contente: labels etc ../>
</StackPanel>
</Grid>
The trick is to wrap everything that you want to be in the same place and/or overlay in a Grid. If the StackPanels are after an Image in Grid content, their z-index will be higher and they will be displayed on top of the image.
The Image control by default will be streched to the whole grid, so you should manipulate the zoom with its content, not its size. If that's problematic, just wrap the Image control with another panel, or at best, custom UserControl and work out the size issues there.
Thanks to the Grid, the StackPanels will anchored to the corners, no matter the size and shape of the grid.
Also, you used tag MVVM and your code is almost textbook non-MVVM.
Maybe this will help you rewrite your code.

Xamarin.Forms loading large dataset with Android

I've been working on a Xamarin Forms project and I'm having trouble loading data into the Android application. It loads fine for iOS and UWP, but on Android it takes forever. This is happening on two different screens but I'm assuming the problem is the same. I'm sure there are better ways to load the data than what I am using.
This is the first screen I'm having trouble with on Android (screenshot from UWP):
It loads 15 products in each category into a horizontal scroller. The "See All" link loads all of the products in that category into a vertical scrolling grid. That's the second screen I'm having trouble with.
The code to load this view is:
var scroller = new TLScrollView
{
Orientation = ScrollOrientation.Horizontal,
ItemTemplate = new DataTemplate(() => ViewFactory.Merchandise.Invoke()),
HeightRequest = 320,
Padding = new Thickness(0, 0, 0, 10)
};
scroller.SetBinding(TLScrollView.ItemsSourceProperty, nameof(items));
scroller.ItemsSource = items.OrderByDescending(d => d.Cost).Take(15);
The ViewFactory code is:
public static Func<View> Merchandise { get; } = () =>
{
var mainGrid = new Grid() { WidthRequest = 250, Margin = 5 };
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(25, GridUnitType.Absolute) });
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(25, GridUnitType.Absolute) });
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(25, GridUnitType.Absolute) });
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(35, GridUnitType.Absolute) });
var nameLabel = new Label() { Text = "Casket Name", LineBreakMode = LineBreakMode.TailTruncation, HorizontalTextAlignment = TextAlignment.Center };
var materialLabel = new Label() { Text = "MaterialName", HorizontalTextAlignment = TextAlignment.Center };
var costLabel = new Label() { Text = "Cost", HorizontalTextAlignment = TextAlignment.Center };
nameLabel.SetBinding(Label.TextProperty, "ItemName");
materialLabel.SetBinding(Label.TextProperty, "Material");
costLabel.SetBinding(Label.TextProperty, "CostFormatted");
GlobalModel globalModel = App.Current.MainPage.BindingContext as GlobalModel;
var catalogImage = new CachedImage
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Aspect = Aspect.AspectFit,
LoadingPlaceholder = "MAFSLoader.gif",
CacheDuration = TimeSpan.FromDays(10),
RetryCount = 1,
RetryDelay = 100
};
catalogImage.BindingContextChanged += (s, o) => {
catalogImage.Source = null;
var item = catalogImage.BindingContext as CatalogItem;
if (item == null) return;
catalogImage.Source = item.SmallURL;
};
var buttonStyle = (Style)Application.Current.Resources["ButtonStyle"];
var addToCartButton = new Button { Text = "Add to Cart", BackgroundColor = Color.FromHex("#486E8E"), WidthRequest = 250, Style = buttonStyle };
addToCartButton.SetBinding(Button.CommandParameterProperty, "ItemID");
var imgTapGesture = new TapGestureRecognizer()
{
Command = new Command(() =>
{
if (addToCartButton.CommandParameter == null)
{
addToCartButton.SetBinding(Button.CommandParameterProperty, "ItemId");
}
var dictionary = new Dictionary<string, string>();
dictionary.Add(globalModel.CurrentFuneralHomeKey, addToCartButton.CommandParameter.ToString());
globalModel.ProductDetail.Execute(dictionary);
})
};
catalogImage.GestureRecognizers.Add(imgTapGesture);
addToCartButton.Clicked += new EventHandler(async delegate (Object o, EventArgs a)
{
var button = ((Button)o);
globalModel.AddToCart.Execute(button.BindingContext);
if (button.BindingContext.GetType() == typeof(Data.Corner))
{
await App.Current.MainPage.DisplayAlert("Shopping Cart", "You've added " + (button.BindingContext as Data.Corner).ItemName + " to your cart.", null, "OK");
}
});
mainGrid.Children.Add(catalogImage, 0, 0);
mainGrid.Children.Add(nameLabel, 0, 1);
mainGrid.Children.Add(materialLabel, 0, 2);
mainGrid.Children.Add(costLabel, 0, 3);
mainGrid.Children.Add(addToCartButton, 0, 4);
return mainGrid;
};
If anyone could point me in the right direction I'd be very grateful!
Interesting. I've just gone through the implementation of the TLSrollview. It looks like there is no recycling of elements and maybe lack some optimization (I can see that only if I debug). I see two options :
option 1 : Create a listview with recycling strategy on. For better performance, set also the row height. One item of your listview would be one row. The advantage of a listview is the performance gain from the recycling strategy (especially on Android). Note that on iOS and Android, it might be better to set it on or not.
option 2 : Create a custom layout. That might be a little overkill.

iOS Change badge color inside UIMoreNavigationController in UITabBarController

I customize the background color and its tint color with this code:
var blackBGColor = new UIColor(new nfloat(40) / 255f,
new nfloat(40) / 255f,
new nfloat(40) / 255f, 1f);
this.MoreNavigationController.TopViewController.View.BackgroundColor = blackBGColor;
var moreTableView = (UITableView)this.MoreNavigationController.TopViewController.View;
moreTableView.TintColor = UIColor.White;
foreach (var cell in moreTableView.VisibleCells)
{
cell.BackgroundColor = blackBGColor;
cell.TextLabel.TextColor = UIColor.White;
var selectedView = new UIView
{
BackgroundColor = UIColor.DarkGray
};
cell.SelectedBackgroundView = selectedView;
}
this.MoreNavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
this.MoreNavigationController.NavigationBar.Translucent = false;
this.MoreNavigationController.NavigationBar.TintColor = UIColor.White;
this.MoreNavigationController.NavigationBar.BarTintColor = new UIColor(24f / 255f, 24f / 255f, 24f / 255f, 1f);
But I couldn't change the badge color inside UIMoreNavigationController.
I've tried this:
this.MoreNavigationController.TopViewController.TabBarItem.BadgeColor = UIColor.White
but it's not working.
Tried this one too inside WillShowViewController:
this.ViewControllers[4].TabBarItem.BadgeColor = UIColor.White
but still not working.
Is there any way to change the badge color?
UPDATE:
After investigating the hierarchy of MoreNavigationController, apparently the badge value for Priority and DueBy tab is assign to a UILabel inside _UITableViewCellBadgeNeue. The hierarchy is:
this.MoreNavigationController.ViewControllers[0]: this is a UIMoreListController
Get the View and cast it to UITableView because that View is a _UIMoreListTableView
Then iterate inside that tableview VisibleCells, check the IEnumerator and in the forth object there is _UITableViewCellBadgeNeue
The SubViews[0] inside _UITableViewCellBadgeNeue is a UILabel and the label's text is the badge value.
Based on that, I change the label TextColor and BackgroundColor in WillShowViewController. It works but I need to go back and forth from Priority/DueBy tab to More tab. It never works on the first time.
[Export("navigationController:willShowViewController:animated:")]
public void WillShowViewController(UINavigationController navigationController, UIViewController viewController, bool animated)
{
if (this.MoreNavigationController.ViewControllers.Contains(viewController))
{
this.ViewControllers[4].TabBarItem.BadgeValue = this.ViewModel.SelectedPrioritiesFilter.Count > 0 ? this.ViewModel.SelectedPrioritiesFilter.Count.ToString() : null;
this.ViewControllers[5].TabBarItem.BadgeValue = this.ViewModel.SelectedDueByFilter != null ? "1" : null;
//this is the code to change the color
var vc = this.MoreNavigationController.ViewControllers[0];
var moreTableView = (UITableView)vc.View;
foreach (var cell in moreTableView.VisibleCells)
{
var enumerator = cell.GetEnumerator();
var i = 0;
while (enumerator.MoveNext())
{
//_UITableViewCellBadgeNeue is in the forth object
if(i == 3)
{
//_UITableViewCellBadgeNeue is a UIView
if (enumerator.Current is UIView)
{
var current = (UIView)enumerator.Current;
if (current != null)
{
if (current.Subviews.Length > 0)
{
var label = (UILabel)current.Subviews[0];
label.TextColor = UIColor.White;
label.BackgroundColor = UIColor.Clear;
}
}
}
}
i++;
}
}
}
}
I reproduced your issue and figured the reason out.
It is because the TableView has not finished rendering(drawing) when you retrieve the view hierarchy in WillShowViewController.
My test
View Hierarchy in WillShowViewController
UITableViewCellContentView
UIButton
View Hierarchy in DidShowViewController
UITableViewCellContentView
UIButton
_UITableViewCellBadgeNeue
_UITableViewCellSeparatorView
UITableViewCellContentView
UIButton
_UITableViewCellSeparatorView
So you just need replace the WillShowViewController with DidShowViewController.
PS: Small Suggestion
To avoid the change of View Hierarchy by Apple, you can use the condition like if (view.Class.Name.ToString() == "_UITableViewCellBadgeNeue") instead of judging which level the _UITableViewCellBadgeNeue is.

Programmatically Added Textboxes not Appearing

I'm using WinForms in VS15 with C#.
I'm dynamically adding TextBoxs and Labels to my Form based upon a user selected value in a ComboBox (essentially this looks up a value in a data collection which tells my UI what controls it needs).
When I attempt to generate the controls, the Labels appear and layout just fine, however, the TextBoxs are remarkable in there absence.
I've tried fidelling with the MaximumSize and MinimumSize properties to see if they could be messing with something but it doesn't seem to be making any difference.
The code I use for doing this is below (I know the use of the List<Pair<Label,TextBox>> is pretty unecessary but I find it helps readability):
private void GenerateControls(string formType)
{
string[] formParameters = engine.GetFormParameters(formType);
if (formParameters == null) return;
SplitterPanel panel = splitContainer.Panel1;
panel.Controls.Clear();
List<Pair<Label, TextBox>> controlPairs = new List<Pair<Label, TextBox>>();
int tabIndex = 0;
Point labelPoint = panel.Location + new Size(20, 20);
Size initialOffset = new Size(0, 30);
Size horizontalOffset = new Size(40, 0);
Size tBoxSize = new Size(40,20);
foreach (string parameter in formParameters)
{
Label label = new Label
{
Text = parameter,
Tag = "Parameter Label",
Name = $"lbl{parameter}",
Location = (labelPoint += initialOffset)
};
TextBox textBox = new TextBox
{
AcceptsTab = true,
TabIndex = tabIndex++,
Text = "",
Tag = parameter,
Name = $"txt{parameter}",
MaximumSize = tBoxSize,
MinimumSize = tBoxSize,
Size = tBoxSize,
Location = labelPoint + horizontalOffset
};
controlPairs.Add(new Pair<Label, TextBox>(label, textBox));
}
foreach (Pair<Label, TextBox> pair in controlPairs)
{
panel.Controls.Add(pair.First);
panel.Controls.Add(pair.Second);
}
}
I don't believe that my use of Point + Size is the issue as the Point class overrides the + operator like so:
Unfortunately for me the issue appears to be simply that the dX was not a big enough value to prevent the text boxes from being hidden under the labels, I forgot that labels don't have transparent backgrounds.
While I was at it: I've removed the redundant List<Pair<<>>; added support for dynamically adjusting TextBox location based on Label size; and split it out into two separate loops, so my code now looks as below and works just fine:
private void GenerateControls(string formType)
{
string[] formParameters = engine.GetFormParameters(formType);
if (formParameters == null) return;
SplitterPanel panel = splitContainer.Panel1;
panel.Controls.Clear();
int tabIndex = 0;
Point labelPoint = panel.Location + new Size(20, 20);
Size verticalOffset = new Size(0, 30);
Size tBoxSize = new Size(200,20);
int maxLabelLength = 0;
foreach (string parameter in formParameters)
{
Label label = new Label
{
Text = parameter,
Tag = "Parameter Label",
Name = $"lbl{parameter}",
Location = (labelPoint += verticalOffset),
AutoSize = true
};
panel.Controls.Add(label);
if (label.Size.Width > maxLabelLength)
{
maxLabelLength = label.Size.Width;
}
}
Size horizontalOffset = new Size(maxLabelLength + 30, 0);
labelPoint = panel.Location + new Size(20, 20) + horizontalOffset;
foreach (string parameter in formParameters)
{
TextBox textBox = new TextBox
{
AcceptsTab = true,
TabIndex = tabIndex++,
Text = "",
Tag = parameter,
Name = $"txt{parameter}",
MaximumSize = tBoxSize,
MinimumSize = tBoxSize,
Size = tBoxSize,
Location = labelPoint += verticalOffset
};
panel.Controls.Add(textBox);
}
}
Thanks everyone who helped!

Arranging images in Stacklayout added through a loop

i am using a for loop to add 198 items, half numbers and half images for an app just to get familiar with xamarin forms but am unable to switch from vertical to horizontal. the desired output is to have 4 images with their serial number in one line.
EDIT: i am unable to get it to display in multiple lines
Image[] show = new Image[100];
for(int i=0;i<100;i++)
{
show[i]=new Image{ Aspect = Aspect.AspectFit };
show[i].WidthRequest = 30;
show[i].HeightRequest = 30;
show[i].Source = images[i];
}
for (int i=0;i<99;i++)
{
if (i % 4 == 0)
{
layout.HorizontalOptions = LayoutOptions.Start;
layout.Spacing = 15;
layout.Orientation = StackOrientation.Vertical;
layout.Children.Add(new Label { Text = (i+1).ToString() });
layout.Spacing = 10;
layout.Children.Add(show[i]);
}
else
{
layout.HorizontalOptions = LayoutOptions.StartAndExpand;
layout.Spacing = 10;
layout.Orientation = StackOrientation.Horizontal;
layout.Children.Add(new Label { Text = (i+1).ToString() });
layout.Children.Add(show[i]);
}
}
This is the current outputOutput
I would recommend using a grid, but to use layouts you would need something more like this:
var outerLayout = new StackLayout {Orientation = StackOrientation.Vertical};
var innerLayout = new StackLayout();
for (int i = 0; i < 99; i++)
{
if (i%4 == 0)
{
if (i != 0)
outerLayout.Children.Add(innerLayout);
innerLayout = new StackLayout {Orientation = StackOrientation.Horizontal};
}
innerLayout.Children.Add(new Label { Text = (i + 1).ToString() });
innerLayout..Children.Add(show[i]);
}
outerLayout.Children.Add(innerLayout);
MainPage = new ContentPage {Content = outerLayout};
This creates an outer vertical layout to hold all the horizontal layouts, then creates a horizontal layout for each group that you want displayed horizontally.

Categories