Xamarin Slider Control - Line not showing - xamarin.forms

Relatively new to Xamarin.
I have a Slider control on a page in my app. In Android (only O/S I have tested so far). But I just see the thumbnail. I do not see the horizontal line. This means it is not obvious to the User that it is a Slider!
I have found this https://github.com/opendatakit/collect/pull/2085 It seems to indicate it is a bug on later versions of Android, but there is a workaround.
Quote: "the only thing we need to do is to add a android:layerType="software" attribute to our layout file"
I am unsure how to do that. What layout file? Where do I create the "layout file". Can somebody help? Thanks.

After searching the web, I created a customer Slider Renderer
public class CustomSliderRenderer : SliderRenderer
{
/// <summary>
/// Instantiate a CustomSliderRenderer
/// </summary>
/// <param name="context">The context to use</param>
public CustomSliderRenderer(Context context)
: base(context) { }
/// <summary>
/// Called when the Slider changes
/// </summary>
/// <param name="e">The details of the change</param>
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
{
base.OnElementChanged(e);
if (!(e.OldElement is null) || e.NewElement is null)
return;
// The line colour to the left of the image
Control.ProgressTintList = ColorStateList.ValueOf(Color.Black.ToAndroid());
Control.ProgressTintMode = PorterDuff.Mode.SrcIn;
// The line colour to the right of the image
Control.ProgressBackgroundTintList = ColorStateList.ValueOf(Color.Black.ToAndroid());
Control.ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
}
/// <summary>
/// Called when the Slider is layed out.
/// </summary>
/// <param name="changed">True if the layout has changed, else false</param>
/// <param name="left">The left of the Slider</param>
/// <param name="top">The top of the Slider</param>
/// <param name="right">The right of the Slider</param>
/// <param name="bottom">The bottom of the Slider</param>
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
base.OnLayout(changed, left, top, right, bottom);
if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBean && !(Control is null))
{
var thumb = Control.Thumb;
int thumbTop = Control.Height / 2 - thumb.IntrinsicHeight / 2;
thumb.SetBounds(thumb.Bounds.Left, thumbTop, thumb.Bounds.Left + thumb.IntrinsicWidth, thumbTop + thumb.IntrinsicHeight);
}
}
}
Then in the XAML
<Slider
Grid.Row="7"
Grid.Column="0"
Grid.ColumnSpan="2"
x:Name="DaysAbandonedVisible"
Value="{Binding DaysAbandonedVisible}"
Maximum="60"
Minimum="0"
ThumbImageSource="Slider.png"/>
Slider.png is just a black circle, larger than the little default pink one. This has the effect of showing the line (black to the left, 'light' black to the right). These colours are in the Control.ProgressTintList and Control.ProgressBackgroundTintList respectively.

Related

Unable To Retrieve Custom Control Value from View

Xamarin Forms Android Autosize Label TextCompat pre android 8 doesn't autosize text
I unfortunately do not have a high enough rep to comment on anyones post.
I was trying some things out and came across the post linked which got me very close to the solution after experimenting with other posts. I am also trying to autosize text within an app, but inside of an MVVM Master Detail project. If I enter values directly in the Droid renderer it works as expected, but that defeats the purpose when I have fonts of all sizes needed.
I have already made sure my return type is correct.
The code behind is initialized prior to the get value.
The fields are public.
There are no other issues by plugging in numeric values instead of bindable properties.
I am not receiving any values from the view. I would assume the view has not been created yet but the code behind has initialized. I am pretty sure I have done everything mostly right but I mostly deal with stock Xamarin so expanding functionality is still pretty new to me. All help is appreciated.
Custom Control (edit: changed default value from default(int) to an integer value to get rid of exception)
/// <summary>Auto scale label font size class.</summary>
public class AutoSizeLabel : Label
{
/// <summary>Minimum font size property.</summary>
public static readonly BindableProperty MinimumFontSizeProperty = BindableProperty.Create(
propertyName: nameof(MinimumFontSize),
returnType: typeof(int),
declaringType: typeof(AutoSizeLabel),
defaultValue: 17);
/// <summary>Maximum font size property.</summary>
public static readonly BindableProperty MaximumFontSizeProperty = BindableProperty.Create(
propertyName: nameof(MaximumFontSize),
returnType: typeof(int),
declaringType: typeof(AutoSizeLabel),
defaultValue: 24);
/// <summary>Gets or sets minimum font size.</summary>
public int MinimumFontSize
{
get
{
return (int)this.GetValue(MinimumFontSizeProperty);
}
set
{
this.SetValue(MinimumFontSizeProperty, value);
}
}
/// <summary>Gets or sets maximum font size.</summary>
public int MaximumFontSize
{
get
{
return (int)this.GetValue(MaximumFontSizeProperty);
}
set
{
this.SetValue(MaximumFontSizeProperty, value);
}
}
}
Droid Renderer
public class AutoSizeLabelRenderer : LabelRenderer
{
protected override bool ManageNativeControlLifetime => false;
protected override void Dispose(bool disposing)
{
Control.RemoveFromParent();
base.Dispose(disposing);
}
private AutoSizeLabel bindingValue = new AutoSizeLabel();
private AppCompatTextView appCompatTextView;
public AutoSizeLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
//v8 and above supported natively, no need for the extra stuff below.
if (DeviceInfo.Version.Major >= 8)
{
Control?.SetAutoSizeTextTypeUniformWithConfiguration(bindingValue.MinimumFontSize, bindingValue.MaximumFontSize, 2, (int)ComplexUnitType.Sp);
return;
}
appCompatTextView = new AppCompatTextView(Context);
appCompatTextView.SetTextColor(Element.TextColor.ToAndroid());
appCompatTextView.SetMaxLines(1);
appCompatTextView.SetBindingContext(autoLabel.BindingContext);SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, bindingValue.MinimumFontSize, bindingValue.MaximumFontSize, 2, (int)ComplexUnitType.Sp);
}
}
XAML Call
<renderer:AutoSizeLabel MinimumFontSize="17"
MaximumFontSize="24"
Style="{StaticResource SomeStyle}"
Text="{Binding SomeText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SomeCommand}"></TapGestureRecognizer>
</Label.GestureRecognizers>
</renderer:AutoSizeLabel>
This line is unnecessary.
private AutoSizeLabel bindingValue = new AutoSizeLabel();
Instead reference autoLabel. Alternatively I changed the check to
if (e.NewElement == null || Control == null) { return; }
and cast in the following line using
var autoSizeLabel = e.NewElement as AutoSizeLabel;

How to handle navigation back button event in xamarin iOS/Android?

I am creating xamarin form app and I want to perform some action on navigation back button.Is there any approach to do that?
P.S :> Software back button, not Hardware back button.
In this article, you'll find everything.
IF the article disappears or changes at some point, here you will find the specifics of the solution.
You'll need to create a Custom ContentPage
namespace WhateverYourNamespace
{
public class CoolContentPage : ContentPage
{
/// <summary>
/// Gets or Sets the Back button click overriden custom action
/// </summary>
public Action CustomBackButtonAction { get; set; }
public static readonly BindableProperty EnableBackButtonOverrideProperty =
BindableProperty.Create(
nameof(EnableBackButtonOverride),
typeof(bool),
typeof(CoolContentPage),
false);
/// <summary>
/// Gets or Sets Custom Back button overriding state
/// </summary>
public bool EnableBackButtonOverride
{
get
{
return (bool)GetValue(EnableBackButtonOverrideProperty);
}
set
{
SetValue(EnableBackButtonOverrideProperty, value);
}
}
}
}
As you see, there's an action event that we are going to subscribe to in our Xamarin Forms code level and to be invoked from Xamarin native project level.
There is also a bool property to enable or disable the overriding of the Back Button click event so that you can decide whether to subscribe to the overriding event or not as a page property.
Android :
You'll need to override the OnOptionsItemSelected() event in our MainActivity class in order to capture the nav bar back button click in Android for Xamarin Forms.
public override bool OnOptionsItemSelected(IMenuItem item)
{
// check if the current item id
// is equals to the back button id
if (item.ItemId == 16908332)
{
// retrieve the current xamarin forms page instance
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
// check if the page has subscribed to
// the custom back button event
if (currentpage?.CustomBackButtonAction != null)
{
// invoke the Custom back button action
currentpage?.CustomBackButtonAction.Invoke();
// and disable the default back button action
return false;
}
// if its not subscribed then go ahead
// with the default back button action
return base.OnOptionsItemSelected(item);
}
else
{
// since its not the back button
//click, pass the event to the base
return base.OnOptionsItemSelected(item);
}
}
public override void OnBackPressed()
{
// this is not necessary, but in Android user
// has both Nav bar back button and
// physical back button its safe
// to cover the both events
// retrieve the current xamarin forms page instance
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
// check if the page has subscribed to
// the custom back button event
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
}
else
{
base.OnBackPressed();
}
}
iOS :
iOS you need to override the ViewWillAppear() method in your CoolContentPageRenderer class.
So the below code should be placed inside your CoolContentPageRenderer class.
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (((CoolContentPage)Element).EnableBackButtonOverride)
{
SetCustomBackButton();
}
}
private void SetCustomBackButton()
{
// Load the Back arrow Image
var backBtnImage =
UIImage.FromBundle("iosbackarrow.png");
backBtnImage =
backBtnImage.ImageWithRenderingMode
(UIImageRenderingMode.AlwaysTemplate);
// Create our Button and set Edge
// Insets for Title and Image
var backBtn = new UIButton(UIButtonType.Custom)
{
HorizontalAlignment =
UIControlContentHorizontalAlignment.Left,
TitleEdgeInsets =
new UIEdgeInsets(11.5f, 15f, 10f, 0f),
ImageEdgeInsets =
new UIEdgeInsets(1f, 8f, 0f, 0f)
};
// Set the styling for Title
// You could set any Text as you wish here
backBtn.SetTitle("Back", UIControlState.Normal);
// use the white color in ios back button text
backBtn.SetTitleColor(UIColor.White,
UIControlState.Normal);
backBtn.SetTitleColor(UIColor.LightGray,
UIControlState.Highlighted);
backBtn.Font = UIFont.FromName("HelveticaNeue",
(nfloat)17);
// Set the Image to the button
backBtn.SetImage(backBtnImage, UIControlState.Normal);
// Allow the button to Size itself
backBtn.SizeToFit();
// Add the Custom Click event you would like to
// execute upon the Back button click
backBtn.TouchDown += (sender, e) =>
{
// Whatever your custom back button click handling
if(((CoolContentPage)Element)?.
CustomBackButtonAction != null)
{
((CoolContentPage)Element)?.
CustomBackButtonAction.Invoke();
}
};
//Set the frame of the button
backBtn.Frame = new CGRect(
0,
0,
UIScreen.MainScreen.Bounds.Width / 4,
NavigationController.NavigationBar.Frame.Height);
// Add our button to a container
var btnContainer = new UIView(
new CGRect(0, 0,
backBtn.Frame.Width, backBtn.Frame.Height));
btnContainer.AddSubview(backBtn);
// A dummy button item to push our custom back button to
// the edge of screen (sort of a hack)
var fixedSpace =
new UIBarButtonItem(UIBarButtonSystemItem.FixedSpace)
{
Width = -16f
};
// wrap our custom back button with a UIBarButtonItem
var backButtonItem = new UIBarButtonItem("",
UIBarButtonItemStyle.Plain, null)
{
CustomView = backBtn
};
// Add it to the ViewController
NavigationController.TopViewController.
NavigationItem.LeftBarButtonItems
= new[] { fixedSpace, backButtonItem };
}
How to use it :
<WhateverYourNamespace:CoolContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:WhateverYourNamespace=
"clrnamespace:XFNavBarBackBtnClickOverride;
assembly=XFNavBarBackBtnClickOverride"
x:Class="XFNavBarBackBtnClickOverride.Page2"
Title="Page 3"
EnableBackButtonOverride="True"
BackgroundColor="#00bfff">
<StackLayout
Spacing="20"
Padding="20,10,20,10"
VerticalOptions="Center"
HorizontalOptions="Center" >
<Label Text="This is the cool page,
which has the Navigation Bar Back button
click overriden. How go ahead and click that Back
button! ;)"
FontSize="20"
HorizontalTextAlignment="Center"
TextColor="White"/>
</StackLayout>
</WhateverYourNamespace:CoolContentPage>
For ANDROID add this in you main activity OnCreate() after LoadApplication(new App()) line.
Android.Support.V7.Widget.Toolbar toolbar
= this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbarSetSupportActionBar(toolbar);

UWP - bind element to main window size AND also update the value as window size changes

I want to bind to a text block the current size of the window.
In the current implementation the size of the main window is set at run time BUT if I resize the window after the application has launched the new size is not updated in the text block.
<Grid x:Name="grid" Background="#FFE8E8E8">
<TextBox x:Name="textBoxSample" Width="300" Height="200" Text="{Binding ActualWidth, ElementName=grid}"></TextBox>
</Grid>
You shouldn't bind to ActualWidth. The remarks of the FrameworkElement.ActualWidth documentation says:
Although it has an ActualWidthProperty backing field, ActualWidth does not raise property change notifications and it should be thought of as a regular CLR property and not a dependency property.
For purposes of ElementName binding, ActualWidth does not post updates when it changes (due to its asynchronous and run-time calculated nature). Do not attempt to use ActualWidth as a binding source for an ElementName binding. If you have a scenario that requires updates based on ActualWidth, use a SizeChanged handler.
You will need some other way of determining the size of the window, such as by subscribing to the SizeChanged event.
In UWP, the Grid controls normally automatically resize to fit its parent container.
Your Textbox however has a set Height and Width, this will prevent it from resizing when its parent grid is resized.
In the scenario you described, a workaround that I've implemented was adding ScreenHeight and ScreenWidth properties to my view model that are updated when the screen size is changed. Then, you can bind the height/width of whatever control you are wanting to be resized to those properies. Here is a sample implementation:
Your XAML File:
<Page x:Name="mainPage"
x:Class="YourApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:YourApp.ViewModels"
SizeChanged="MainPage_SizeChanged">
<Page.DataContext>
<vm:ViewModel x:Name="dataContext"/>
</Page.DataContext>
<YourControl Height="{Binding ScreenHeight}" Width="{Binding ScreenWidth}"/>
</Page>
Your ViewModel
public class ViewModel: INotifyPropertyChanged
{
private double _screenWidth;
private double _screenHeight;
public double ScreenWidth { get { return _screenWidth; } set { _screenWidth = value; OnPropertyChanged("ScreenWidth"); } }
public double ScreenHeight { get { return _screenHeight; } set { _screenHeight = value; OnPropertyChanged("ScreenHeight"); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Your Code Behind
private void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
{
dataContext.ScreenHeight = this.ActualHeight;
dataContext.ScreenWidth = this.ActualWidth;
}

Xamarin.Forms XAML Rounded Button

I'm trying to place a rounded Button in my Xamarin.Forms application, but I can't do it.
I read something about a custom controller to the button, but I didn't find any docs about rounded buttons in Xamarin.Forms.
Does anyone know how to do it? I'm just building an Android and iOS application.
You can use the BorderRadius property to create rounded corners on a Button
<Button Text="BlueButton"
BorderColor="Blue"
BorderRadius="5"
BorderWidth="2"/>
You need to use CornerRadius instead of BorderRadius because:
'Button.BorderRadius' is obsolete: 'BorderRadius is obsolete as of
2.5.0. Please use CornerRadius instead.'
Example: XButton.CornerRadius = 5;
If you are trying to have a Round button, use the below code. The height and width needs to be same and also proportionate to Border Radius.
<Button HorizontalOptions="Fill" VerticalOptions="Fill" Text="+">
<Button.WidthRequest>
<OnIdiom x:TypeArguments="x:Double" Phone="60" Tablet="80" />
</Button.WidthRequest>
<Button.HeightRequest>
<OnIdiom x:TypeArguments="x:Double" Phone="60" Tablet="80" />
</Button.HeightRequest>
<Button.BorderRadius>
<OnIdiom x:TypeArguments="x:Int32" Phone="30" Tablet="40" />
</Button.BorderRadius>
</Button>
You can ignore the different size for tablets if you are fine in having the same size on phone and tablets.
Note : This won't work on Windows. You will get a square button.
In Android, if your mainactivity is inheriting from AppCompact you will have to add this too.
The side of xaml the property is ConerRadius, Example:
<Button
CornerRadius="20"
Text="{i18n:Translate Cancel}"
Command="{Binding CancelarCommand}"
BackgroundColor="{StaticResource ButtonBackgroundColorbuscar}"
TextColor="White" />
If you want an image button you can use this ButtonCirclePlugin for Xamarin Forms.
Or an ImageCircle such as this ImageCirclePlugin for Xamarin Forms and add a TapGestureRecognizer.
There is no BorderRadius Property in the current version of Xamarin Forms. An alternative is the CornerRadius Property.
example:
<Button Text="Submit"
FontSize="Large"
TextColor="White"
BackgroundColor="Green"
CornerRadius="100"
To create rounded (circular) button try this...
<Button WidthRequest = 100,
HeightRequest = 100,
BorderRadius = 50 />
In general, WidthRequest=x, HeightRequest=x, BorderRadius=x/2
If you do not wish to drop down to using a renderer, and you don't mind not having a circular button on Windows Phone, you can use this code:
private const int BUTTON_BORDER_WIDTH = 1;
// Normal button height
//private const int BUTTON_HEIGHT = 44;
//private const int BUTTON_HEIGHT_WP = 72;
//private const int BUTTON_HALF_HEIGHT = 22;
//private const int BUTTON_HALF_HEIGHT_WP = 36;
//private const int BUTTON_WIDTH = 44;
//private const int BUTTON_WIDTH_WP = 72;
// Large button Height
private const int BUTTON_HEIGHT = 88;
private const int BUTTON_HEIGHT_WP = 144;
private const int BUTTON_HALF_HEIGHT = 44;
private const int BUTTON_HALF_HEIGHT_WP = 72;
private const int BUTTON_WIDTH = 88;
private const int BUTTON_WIDTH_WP = 144;
public RoundButtonPage()
{
var button = new Button
{
HorizontalOptions = LayoutOptions.Center,
BackgroundColor = Color.Accent,
BorderColor = Color.Black,
TextColor = Color.White,
BorderWidth = BUTTON_BORDER_WIDTH,
BorderRadius = Device.OnPlatform(BUTTON_HALF_HEIGHT, BUTTON_HALF_HEIGHT, BUTTON_HALF_HEIGHT_WP),
HeightRequest = Device.OnPlatform(BUTTON_HEIGHT, BUTTON_HEIGHT, BUTTON_HEIGHT_WP),
MinimumHeightRequest = Device.OnPlatform(BUTTON_HEIGHT, BUTTON_HEIGHT, BUTTON_HEIGHT_WP),
WidthRequest = Device.OnPlatform(BUTTON_WIDTH, BUTTON_WIDTH, BUTTON_WIDTH_WP),
MinimumWidthRequest = Device.OnPlatform(BUTTON_WIDTH, BUTTON_WIDTH, BUTTON_WIDTH_WP),
Text = "ClickMe"
};
var stack = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Orientation = StackOrientation.Vertical,
Children = { button },
};
Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
Content = stack;
}
It will make a button with rounded corners. To make a button totally round you just set the border radius to be half of the height.
The only thing to remember is that your button has to be large enough to contain the contents. You can see what I mean by commenting/uncommenting out the two constant sections at the top. The first set is good for a number or letter, and the second one is good for a phrase, like "ClickMe."
Again, this uses the native buttons of the platform and since WP doesn't support a border radius all buttons on WP will be rectangular so you'll need to use the technique that James shows in the CircularImage control.
Try this C# code
private const int BUTTON_BORDER_WIDTH = 1;
private const int BUTTON_HEIGHT = 65;
private const int BUTTON_HEIGHT_WP = 40;
private const int BUTTON_HALF_HEIGHT = 33;
private const int BUTTON_HALF_HEIGHT_WP = 20;
private const int BUTTON_WIDTH = 65;
private const int BUTTON_WIDTH_WP = 20;
var chkIm = new Button()
{
BackgroundColor = Color.Black,
HorizontalOptions = LayoutOptions.Center,
TextColor = Color.White,
BorderRadius = Device.OnPlatform(BUTTON_HALF_HEIGHT, BUTTON_HALF_HEIGHT, BUTTON_HALF_HEIGHT_WP),
HeightRequest = Device.OnPlatform(BUTTON_HEIGHT, BUTTON_HEIGHT, BUTTON_HEIGHT_WP),
MinimumHeightRequest = Device.OnPlatform(BUTTON_HEIGHT, BUTTON_HEIGHT, BUTTON_HEIGHT_WP),
WidthRequest = Device.OnPlatform(BUTTON_WIDTH, BUTTON_WIDTH, BUTTON_WIDTH_WP),
MinimumWidthRequest = Device.OnPlatform(BUTTON_WIDTH, BUTTON_WIDTH, BUTTON_WIDTH_WP),
};
Yo can use this style and converter to get General Circular Button.
Style in App.xaml
<Style x:Key="CircleButton" TargetType="{x:Type Button}">
<Setter Property="CornerRadius" Value="{Binding Source={RelativeSource Self}, Path=WidthRequest, Converter={StaticResource NumberDivideConverter}, ConverterParameter=2}" />
<Setter Property="HeightRequest" Value="{Binding Source={RelativeSource Self}, Path=WidthRequest}" />
</Style>
Don't forget to add below line to your head of App.xaml:
xmlns:converters="clr-namespace:AlarteInclinometer.Converters"
and
<converters:NumberDivideConverter x:Key="NumberDivideConverter" />
to in App.xaml
Your converter class which divides corner radius to WidthRequest / 2 is:
NumberDivideConverter.cs:
public class NumberDivideConverter : IValueConverter
{
/// <summary>
/// Converts binding property to calculated new property
/// </summary>
/// <param name="value">Source value</param>
/// <param name="targetType">Target type of to be calculated (return) value.</param>
/// <param name="parameter">Converter parameter.</param>
/// <param name="culture">Converter culture.</param>
/// <returns>New calculated value.</returns>
/// <exception cref="ArgumentNullException">If value is null, throws ArgumentNullException</exception>
/// <exception cref="ArgumentException">If value cannot be converted to a integer, throws ArgumentException</exception>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Check is value not null
if (value == null)
throw new ArgumentNullException($"Value is null");
// Check is value integer
if (int.TryParse(value.ToString(), out int intValue))
{
// If there is no parameter value, return same value
if (parameter == null)
return intValue;
// If there is converter parameter, divide number with it and return new result
if (int.TryParse(parameter.ToString(), out int param))
return intValue / param;
}
// Throw an error if value is not an integer
else
{
throw new ArgumentException($"The value must be a integer but it is a/an {value}");
}
return 0;
}
/// <summary>
/// Converts calculated property to binding property
/// </summary>
/// <param name="value">Source value</param>
/// <param name="targetType">Target type of to be calculated (return) value.</param>
/// <param name="parameter">Converter parameter.</param>
/// <param name="culture">Converter culture.</param>
/// <returns>New calculated value.</returns>
/// <exception cref="ArgumentNullException">If value is null, throws ArgumentNullException</exception>
/// <exception cref="ArgumentException">If value cannot be converted to a integer, throws ArgumentException</exception>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Check is value not null
if (value == null)
throw new ArgumentNullException($"Value is null");
// Check is value integer
if (int.TryParse(value.ToString(), out int intValue))
{
// If there is no parameter value, return same value
if (parameter == null)
return intValue;
// If there is converter parameter, divide number with it and return new result
if (int.TryParse(parameter.ToString(), out int param))
return intValue * param;
}
// Throw an error if value is not an integer
else
{
throw new ArgumentException($"The target must be a integer but it is a/an {value}");
}
return 0;
}
}
After that, you can use this style in buttons where you want like:
<Button Text="Circular" WidthRequest="120" Style="{StaticResource CircleButton}" />
This is the best solution I think :)

PagedCollectionView memory leak Silverlight 4

I have observable collection with some items.
/// <summary>
/// The <see cref="Items" /> property's name.
/// </summary>
public const string ItemsPropertyName = "Items";
private ObservableCollection<SomeItem> _items = new ObservableCollection<BrandItem>();
/// <summary>
/// Gets the Items property.
/// </summary>
public ObservableCollection<SomeItem> Items
{
get
{
return _items;
}
set
{
if (_items == value)
{
return;
}
_items = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemsPropertyName);
}
}
and also pagedCollectionView because I have to group items in datagrid
public const string ItemGroupedPropertyName = "ItemGrouped";
private PagedCollectionView _itemsGrouped;
/// <summary>
/// Gets the ItemSpecificationsGrouped property.
/// </summary>
public PagedCollectionView ItemSpecificationsGrouped
{
get { return _itemsGrouped; }
set
{
if (_itemsGrouped == value)
{
return;
}
_itemsGrouped = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemGroupedPropertyName);
}
}
#endregion
in viewmodel constructor I set
ItemGrouped = new PagedCollectionView(Items);
ItemGrouped.GroupDescriptions.Add(new PropertyGroupDescription("GroupName"));
and in view a have datagrid that bind ItemsGrouped
<data:DataGrid ItemsSource="{Binding ItemsGrouped}" AutoGenerateColumns="False">
<data:DataGrid.Columns >
<data:DataGridTextColumn IsReadOnly="True"
Binding="{Binding ItemAttribut1}" Width="*"/>
<data:DataGridTextColumn IsReadOnly="True"
Binding="{Binding Attribute2}" Width="*" />
</data:DataGrid.Columns>
</data:DataGrid>
when i change items in Items (clear and add new) after many times I have a memory leak.. When I remove ItemsSource everything is fine.. So I know that PagedCollectionView causing memory leak but i don't know why. Any idea, please? Or another solution to group items inside datagrid by some property in collection.. Thank you!!
The issue is the paged collection view hooked up the the NotifyPropertyChanged from your RaisePropertyChanged(ItemsPropertyName); and never releases the event hook... I solved this because I did not need grouping by returning an ICollectionView. When you bind the grid to an ObservableCollection the datagrid will create the PagedCollectionView for you. When you bind to ICollectionView the grid will use the ICollectionView without creating the PagedCollectionView. Hope this helps...
public ICollectionView UserAdjustments
{
get { return _userAdjustmentsViewSource.View; }
}
private void SetCollection(List<UserAdjustment> adjustments)
{
if(_userAdjustmentsViewSource == null)
{
_userAdjustmentsViewSource = new CollectionViewSource();
}
_userAdjustmentsViewSource.Source = adjustments;
RaisePropertyChanged("UserAdjustments");
}

Resources