This commit is contained in:
PROGRESS\doyordan 2021-01-30 09:55:57 +02:00
Родитель 7d917c24b8
Коммит a815185b16
3 изменённых файлов: 502 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,161 @@
---
title: Minor Ticks and Title in Gauges for Xamarin
description: how to add minor ticks inside RadGauge for Xamarin
type: how-to
page_title: Add minor ticks and title to the Gauge for Xamarin
slug: gauge-minor-ticks
position:
tags: gauge-minor-ticks-and-title
ticketid: 1502506
res_type: kb
---
## Environment
<table>
<tbody>
<tr>
<td>Product Version</td>
<td>2021.1.119.1</td>
</tr>
<tr>
<td>Product</td>
<td>Chart for Xamarin Cross-Platform</td>
</tr>
</tbody>
</table>
## Description
This article will show you how to add minor ticks and title to the RadGauge control. The solution can be created with a little clever use of existing indicators and some custom development.
![Gauge minor ticks and title](images/gauge-minor-ticks-title.png)
## Solution
1. Use [GaugeShapeIndicator]({%slug gauge-indicators%})
2. Draw the rectangle shape using [RadPathGeometry]({%slug path-structure%}). This is a very simple shape path just like you're familiar with on any platform that uses X,Y coordinates to draw a line. You will need to create one GaugeShapeIndicator for every square.
3. Create Custom Algorithm to draw the shapes.
Here is the RadGauge definition. The labels inside the stackLayout will be used for displaying a title for the Gauge control
```XAML
<Grid x:Name="MyGaugeGrid" WidthRequest="400" HeightRequest="400" VerticalOptions="Start" HorizontalOptions="Center" Margin="50" >
<telerikGauges:RadRadialGauge x:Name="gauge"
Margin="10"
StartAngle="225"
SweepAngle="270">
<telerikGauges:RadRadialGauge.Axis>
<telerikGauges:GaugeLinearAxis Minimum="0" Maximum="100"
ShowLabels="True"
StrokeThickness="0"
Step="10"
/>
</telerikGauges:RadRadialGauge.Axis>
<telerikGauges:RadRadialGauge.Indicators>
<telerikGauges:GaugeBarIndicator StartCap="Flat"
EndCap="Flat"
Fill="LightGray"
Offset="3"
Value="100" />
<telerikGauges:GaugeBarIndicator StartCap="Flat"
EndCap="Flat"
Fill="Gold"
Offset="3"
Value="{Binding OtherValue}" />
</telerikGauges:RadRadialGauge.Indicators>
</telerikGauges:RadRadialGauge>
<!-- for the Gauge Title-->
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" Margin="0,40,0,0" Spacing="0">
<Label x:Name="OtherValueLabel"
Text="{Binding OtherValue, StringFormat='{0}%'}"
TextColor="Gold"
HorizontalTextAlignment="Center"
FontSize="22" />
<Label Text="Other Value"
TextColor="LightGray"
HorizontalTextAlignment="Center"
FontSize="8" />
<Label x:Name="Label"
TextColor="GreenYellow"
Text="{Binding MyPoints, StringFormat='{0}%'}"
HorizontalTextAlignment="Center"
FontSize="22" />
<Label Text="My Points"
HorizontalTextAlignment="Center"
FontSize="8"
TextColor="LightGray" />
</StackLayout>
</Grid>
```
and the code behind and the custom algorithm in action
```C#
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
var avgPercentage = (BindingContext as MainViewModel).MyPoints;
DrawRectangleIndicators(avgPercentage);
}
private void DrawRectangleIndicators(double totalValue)
{
for (int i = 0; i < totalValue; i++)
{
// Draw an indicator at every 3rd slot
if (i % 3 == 0)
{
gauge.Indicators.Add(CreateGaugeShapeIndicator(i));
}
}
// draw final indicator to represent the total value
gauge.Indicators.Add(CreateGaugeShapeIndicator(totalValue));
}
private GaugeShapeIndicator CreateGaugeShapeIndicator(double indicatorValue)
{
var pathGeometry = new RadPathGeometry();
var pathFigure = new RadPathFigure { StartPoint = new Point(1, 0.2) };
pathFigure.Segments.Add(new RadLineSegment { Point = new Point(0, 0.2) });
pathFigure.Segments.Add(new RadLineSegment { Point = new Point(0, 0.8) });
pathFigure.Segments.Add(new RadLineSegment { Point = new Point(1, 0.8) });
pathGeometry.Figures.Add(pathFigure);
return new GaugeShapeIndicator
{
Value = indicatorValue,
Fill = Color.YellowGreen,
Size = 5,
Offset = 30,
Position = GaugeElementPosition.Start,
Shape = pathGeometry
};
}
}
public class MainViewModel
{
public MainViewModel()
{
}
public double OtherValue { get; set; } = 68;
public double MyPoints { get; set; } = 72;
}
```

Двоичные данные
knowledge-base/images/gauge-minor-ticks-title.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Просмотреть файл

@ -0,0 +1,341 @@
---
title: TreeView Disable horizontal scroll caused by long item names
description: This article will show you how to disable the horizontal scroll inside the TreeView control.
type: how-to
page_title: Disable TreeView hrizontal scroll caused with items with long text
slug: treeview-disable-horizontal-scroll
position:
tags: treeview, scrolling, horizontal scroll, long text, xamarin, xamarin.forms
ticketid: 1503952
res_type: kb
---
## Environment
<table>
<tbody>
<tr>
<td>Product Version</td>
<td>2021.1.119.1</td>
</tr>
<tr>
<td>Product</td>
<td>TreeView for Xamarin</td>
</tr>
</tbody>
</table>
## Description
For this purpose, we will need to implement a custom renderer for android and for iOS.
## Solution
Here is a sample TreeView definition:
```XAML
<ContentPage.Resources>
<ResourceDictionary>
<telerikTreeView:LevelToMarginConverter x:Key="levelToMarginConverter" />
<DataTemplate x:Key="CustomControlTemplate">
<StackLayout Orientation="Horizontal"
Margin="{Binding Path=Level, Converter={StaticResource levelToMarginConverter}}">
<StackLayout.HeightRequest>
<OnPlatform x:TypeArguments="x:Double">
<On Platform="iOS" Value="44"></On>
<On Platform="Android" Value="40"></On>
<On Platform="UWP" Value="40"></On>
</OnPlatform>
</StackLayout.HeightRequest>
<telerikTreeView:ExpandCollapseIndicator FontSize="Medium"
WidthRequest="10"
Margin="15,0"
VerticalTextAlignment="Center"
IsLoading="{Binding Path=IsLoading}"
IsLoadOnDemandEnabled="{Binding Path=IsLoadOnDemandEnabled}"
IsExpanded="{Binding Path=IsExpanded}"
IsLeaf="{Binding Path=IsLeaf}"/>
<telerikPrimitives:RadCheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"
IsVisible="{Binding Path=IsCheckBoxVisible}"
VerticalOptions="Center"/>
<telerikTreeView:ItemText Text="{Binding Path=Header}"
VerticalTextAlignment="Center"
MaxLines="1"
LineBreakMode="TailTruncation"/>
</StackLayout>
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<telerikDataControls:RadTreeView x:Name="treeView" ItemsSource="{Binding Source}">
<telerikDataControls:TreeViewDescriptor DisplayMemberPath="Name"
ItemsSourcePath="Children"
ItemTemplate="{StaticResource CustomControlTemplate}"
TargetType="{x:Type local:Item}" />
</telerikDataControls:RadTreeView>
</Grid>
```
and the used ViewModel
```C#
public class Item
{
public Item(string name)
{
this.Name = name;
this.Children = new ObservableCollection<Item>();
}
public string Name { get; set; }
public IList<Item> Children { get; set; }
}
public class ViewModel
{
public ViewModel()
{
Source = new ObservableCollection<Item>();
Source.Add(new Item("My Documents")
{
Children = new List<Item>()
{
new Item("Xamarin Projects")
{
Children = new ObservableCollection<Item>()
{
new Item("TreeView Examples"),
new Item("Calendar & Scheduling QSF")
}
},
new Item("Documentation Drafts")
}
});
Source.Add(new Item("Shared Documents")
{
Children = new List<Item>()
{
new Item("Reports")
{
Children = new List<Item>()
{
new Item("Long text text text text text text text text text text text text text text end"),
new Item("November"),
new Item("December")
}
}
}
});
}
public ObservableCollection<Item> Source { get; set; }
}
```
then the BindingContext set
```C#
this.BindingContext = new ViewModel();
```
### Custom Renderer for Android
Custom Renderer implementation on Android
```C#
using System;
using System.Reflection;
using Android.Content;
using AndroidX.RecyclerView.Widget;
using RadTreeViewXF.Droid;
using Telerik.XamarinForms.DataControls;
using Telerik.XamarinForms.DataControls.TreeView;
using Telerik.XamarinForms.DataControlsRenderer.Android;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.Android;
using AndroidViews = Android.Views;
[assembly: ExportRenderer(typeof(RadTreeView), typeof(CustomTreeViewRenderer))]
namespace RadTreeViewXF.Droid
{
public class CustomTreeViewRenderer : TreeViewRenderer
{
public CustomTreeViewRenderer(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<RadTreeView> e)
{
base.OnElementChanged(e);
var nativeTreeView = this.Control;
if (nativeTreeView != null)
{
Registrar.Registered.Register(typeof(TreeViewTemplateCell), typeof(CustomTreeViewTemplateCellRenderer));
nativeTreeView.SetLayoutManager(new LinearLayoutManager(this.Context));
}
}
class CustomTreeViewTemplateCellRenderer : CellRenderer
{
protected override AndroidViews.View GetCellCore(Cell item, AndroidViews.View convertView, AndroidViews.ViewGroup parent, Context context)
{
var viewCell = (ViewCell)item;
IVisualElementRenderer renderer = Platform.GetRenderer(viewCell.View);
if (renderer == null || renderer.Element == null)
{
renderer = Platform.CreateRendererWithContext(viewCell.View, context);
Platform.SetRenderer(viewCell.View, renderer);
}
return new TreeViewTemplateCellContainer(context, renderer, viewCell);
}
}
class TreeViewTemplateCellContainer : FormsViewGroup
{
private static Type invalidationEventArgsType;
private static PropertyInfo triggerProperty;
protected readonly ViewCell viewCell;
protected readonly IVisualElementRenderer view;
private MethodInfo invalidateMeasureMethod;
public TreeViewTemplateCellContainer(Context context, IVisualElementRenderer view, ViewCell viewCell)
: base(context)
{
this.viewCell = viewCell;
this.view = view;
this.AddView(view.View);
this.viewCell.View.MeasureInvalidated += this.OnMeasureInvalidated;
this.invalidateMeasureMethod = this.view.Element.GetType().GetMethod("InvalidateMeasure", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new System.Type[] { }, new ParameterModifier[] { });
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
var treeView = (RadExtendedListView)this.Parent;
var width = treeView.Width;
this.invalidateMeasureMethod.Invoke(this.view.Element, new object[] { });
var sizeRequest = this.view.Element.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
var height = (int)this.Context.ToPixels(this.viewCell.Height > 0 ? this.viewCell.Height : sizeRequest.Request.Height);
this.SetMeasuredDimension(width, height);
}
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
var context = base.Context;
var width = context.FromPixels(right - left);
var height = context.FromPixels(bottom - top);
Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(this.view.Element, new Rectangle(0, 0, width, height));
this.view.UpdateLayout();
}
private void OnMeasureInvalidated(object sender, System.EventArgs e)
{
var trigger = TryGetInvalidationTrigger(e);
if (trigger.HasValue && trigger.Value.HasFlag(InvalidationTrigger.SizeRequestChanged) ||
trigger.Value.HasFlag(InvalidationTrigger.MarginChanged) ||
trigger.Value.HasFlag(InvalidationTrigger.MeasureChanged))
{
this.RequestLayout();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (this.viewCell != null && this.viewCell.View != null)
{
this.viewCell.View.MeasureInvalidated -= this.OnMeasureInvalidated;
this.viewCell.View = null;
}
}
base.Dispose(disposing);
}
internal static InvalidationTrigger? TryGetInvalidationTrigger(EventArgs e)
{
Type type = e.GetType();
if (invalidationEventArgsType == null)
{
if (type.FullName == "Xamarin.Forms.InvalidationEventArgs")
{
invalidationEventArgsType = type;
triggerProperty = type.GetRuntimeProperty("Trigger");
}
}
if (type != invalidationEventArgsType)
{
return null;
}
object propertyValue = triggerProperty.GetValue(e);
InvalidationTrigger actualTrigger = (InvalidationTrigger)propertyValue;
return actualTrigger;
}
}
}
}
```
### Custom Renderer for iOS
Custom Renderer implementation on iOS
```C#
using CoreGraphics;
using Foundation;
using RadTreeViewXF.iOS;
using Telerik.XamarinForms.DataControls;
using Telerik.XamarinForms.DataControlsRenderer.iOS;
using TelerikUI;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(RadTreeView), typeof(CustomTreeViewRenderer))]
namespace RadTreeViewXF.iOS
{
public class CustomTreeViewRenderer : TreeViewRenderer
{
protected override TKTreeViewListView CreateNativeControl()
{
return new CustomTKTreeViewListView();
}
class CustomTKTreeViewListView : TKTreeViewListView
{
public override TKListViewLinearLayout CreateLayout()
{
return new CustomTreeViewLayout(this);
}
}
}
public class CustomTreeViewLayout : TreeViewLayout
{
TKListView listView;
public CustomTreeViewLayout(TKListView listView)
: base(listView)
{
this.listView = listView;
}
public override CGSize GetItemSizeForIndexPath(NSIndexPath indexPath)
{
return this.Delegate.SizeForItem(this.listView, this, indexPath);
}
}
}
```