How to implement custom BindableProperty from XAML local:imageresource - c#

(EDITED: clarity)
I want a BindableProperty for a custom control which will ultimately display an image. In my implementation (extracts below), the property Source gets assigned the value
Xamarin.Forms.StreamImageSource
instead of something resembling a path to the file.
My XAML has the following:
<controls:StretchImage
x:Name="Folder"
HeightRequest="25"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
BackgroundColor="Gold"
Source="{local:ImageResource CloudTest.Assets.icons.folder_tab.png}" />
In StretchImage.cs I have:
class StretchImage : SKCanvasView
{
public static readonly BindableProperty LabelProperty =
BindableProperty.Create(nameof(Source), typeof(string), typeof(StretchImage),
null, Xamarin.Forms.BindingMode.OneWay);
public string Source
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
}
What am I not understanding ?

Related

Custom control bindable Property not getting hit when set from a view

I've created a control and it has a bindable property, but when I try to set its value, it does not set, when I check its setter, it's not getting hit while debugging, not sure what am I doing wrong.
public decimal MetricValue
{
get => (decimal)GetValue(MetricValueProperty);
set => SetValue(MetricValueProperty, value);
}
public static readonly BindableProperty MetricValueProperty =
BindableProperty.Create(
propertyName: nameof(MetricValue),
returnType: typeof(decimal),
declaringType: typeof(HeightSelection),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: MetricValuePropertyChanged);
I also have a propertychanged, which is not getting raised
<controls:customControl
CurrentSystemOfMeasure="{Binding CurrentSystemOfMeasure}"
MetricValue="{Binding CurrentHeight}"
TextAlignment="Start"
OnHeightSelectedCommand="{Binding HeightSelectionCommand}"
IsValid="True" />
any inputs would be helpful
have you set context in the xaml view?
Here my way:
public static readonly BindableProperty CancelButtonVisibleAvailableProperty = BindableProperty.Create(
propertyName: "CancelButtonVisible",
returnType: typeof(bool),
declaringType: typeof(SelphiDetailPhotosView),
defaultValue: true);
public bool CancelButtonVisible
{
get { return (bool)GetValue(CancelButtonVisibleAvailableProperty); }
set { SetValue(CancelButtonVisibleAvailableProperty, value); }
}
ContentView needs to have a x:name to set the context in the property IsVisible in your case you need to set the context in Text Property of label to show decimal value
I have set the context making reference to my custom view selphiDetailPhotosView
<Button IsVisible="{Binding Source={x:Reference selphiDetailPhotosView}, Path=CancelButtonVisible}"
Margin="27,0,27,18"
BackgroundColor="{StaticResource PlatinumColor}"
Command="{Binding CancelCommand}"
Text="Cancelar"
TextColor="White"
TextTransform="None" />
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="confia.Views.Views.OnBoardingAndLivenessSharedViews.SelphiDetailPhotosView"
xmlns:svg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
xmlns:transformation="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
xmlns:views="clr-namespace:confia.Views.Views"
xmlns:converters="clr-namespace:confia.Views.Converters"
x:Name="selphiDetailPhotosView"
>
Finally call your customcontrol in your ContentPage and bind with your ViewModel
<customControl:SelphiDetailPhotosView CancelButtonVisible="{Binding CanCancel}"/>
I have replicated your scenario and this work for me.
CodeBehind Custom Control
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty MetricValueProperty = BindableProperty.Create(
propertyName: nameof(MetricValue),
returnType: typeof(decimal),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.TwoWay,
defaultValue: 0m);
public decimal MetricValue
{
get { return (decimal)this.GetValue(MetricValueProperty); }
set { this.SetValue(MetricValueProperty, value); }
}
}
CustomControl Xaml
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ferreplace.Views.Controls.CustomControl"
x:Name="this"
>
<ContentView.Content>
<StackLayout HorizontalOptions="CenterAndExpand" >
<Label Text="Metric Value" FontSize="40"/>
<Label FontSize="20" Text="{Binding Source={x:Reference this}, Path=MetricValue}" ></Label>
</StackLayout>
</ContentView.Content>
ContenPage to call Custom control
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ferreplace.Views.Controls"
x:Class="ferreplace.MainPage">
<StackLayout>
<controls:CustomControl MetricValue="{Binding MetricValueBinding}"/>
</StackLayout>
</ContentPage>
ViewModel
public class MainPageViewModel: BindableBase, IInitializeAsync
{
private decimal _metricValueBinding;
public decimal MetricValueBinding
{
get
{
return _metricValueBinding;
}
set
{
SetProperty(ref _metricValueBinding, value);
}
}
public MainPageViewModel()
{
}
public async Task InitializeAsync(INavigationParameters parameters)
{
MetricValueBinding = 30m;
}
}
Decimal Binding Result
Maybe you forgot default value in bindable property creation

Binding a custom Entry in Xamarin Forms

I'm trying to extract this Entry in a Frame into a custom element in Xamarin, to get a reusable Entry with border only on top and on bottom:
<Frame xmlns="..."
HasShadow="False"
CornerRadius="0"
Padding="0, 1, 0, 1"
BackgroundColor="#c0c0c0">
<Entry Padding="20, 10, 20, 10"
Placeholder="{Binding Placeholder}"
Text="{Binding Text}"
BackgroundColor="#ffffff" />
</Frame>
Code Behind:
public partial class CbSingleEntry : Frame
{
public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(CbSingleEntry));
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create("Placeholder", typeof(string), typeof(CbSingleEntry));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string Placeholder
{
get { return (string)GetValue(PlaceholderProperty); }
set { SetValue(PlaceholderProperty, value); }
}
public CbSingleEntry()
{
InitializeComponent();
BindingContext = this;
}
}
When I try to use this custom field, the Placeholder and Text property are correctly set, but I can't bind them to attributes in my class:
// this one works fine
<local:CbSingleEntry Placeholder="Company" Text="My Company" />
// Placeholder works, but Text is always empty
<local:CbSingleEntry Placeholder="Company" Text="{Binding Company}" />
I can confirm that Company has a value, because with a normal text field it works correctly:
// This one works as expected, Text is displayed from binded attribute
<Entry Placeholder="Company" Text="{Binding Company}" />
Cause : in your case , you set the BindingContext in CbSingleEntry
BindingContext = this;
So the binding in ContentPage will not work any more .
Solution:
You could modify the code in CbSingleEntry
in xaml
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="CustomView" // set the name here
x:Class="xxx">
<Entry
Placeholder="{Binding Source={x:Reference CustomView},Path=Placeholder}"
Text="{Binding Source={x:Reference CustomView},Path=Text}"
BackgroundColor="#ffffff" />
</Frame>
in code behind
public partial class CbSingleEntry : Frame
{
public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(CbSingleEntry));
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create("Placeholder", typeof(string), typeof(CbSingleEntry));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string Placeholder
{
get { return (string)GetValue(PlaceholderProperty); }
set { SetValue(PlaceholderProperty, value); }
}
public CbSingleEntry()
{
InitializeComponent();
// BindingContext = this; don't need to set it any more
}
}

Xamarin Forms bind a command in a custom controler with Prism

I'm facing an issue and I can't figure it out. I have a custom control, to simplify it let's say I have a button inside of a frame inside of a frame. I want the command of the button to be bindable and the button is private. So, here is my code :
CustomControl.cs :
public System.Windows.Input.ICommand CommandInButton
{
get { return ButtonInFrame.Command; }
set { ButtonInFrame.Command = value; }
}
public static readonly BindableProperty CommandInButtonProperty =
BindableProperty.Create(
propertyName:"CommandInButton",
returnType: typeof(System.Windows.Input.ICommand),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.TwoWay);
private Button ButtonInFrame;
Myview.xaml :
<local:FrameButtonImage Grid.Column="0" Grid.Row="0"
ColorInButton="LightBlue"
SourceImageInButton="male.png"
IsSelected="{Binding IsMenSelected}"
CommandInButton="{Binding SelectMenCommand}"
/>
MyViewModel.cs : (I'm using Prism)
public DelegateCommand SelectMenCommand { get; private set; }
public MainPageViewModel()
{
SelectMenCommand = new DelegateCommand(SelectMen, CanSelectMen);
}
private void SelectMen()
{
System.Diagnostics.Debug.WriteLine("Hello men");
}
private bool CanSelectMen()
{
return !IsMenSelected;
}
My problem : it's never trigger SelectMen().
If I bind the command in a simple button like that :
<Button Grid.Column="1" Grid.Row="0" Grid.RowSpan="3"
Text=">"
FontSize="Large"
BackgroundColor="Transparent"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding SelectMenCommand}"/>
It's work like a charm ! So I supposed I do mess in the CustomControl.cs... Maybe someone can help me ? Thanks !
I found a workaround but I'm sure it's possible to do better. I set my command to be a property of my custom control and add a method to set it to be the command of the button when the command is set.
CustomControl.cs :
public System.Windows.Input.ICommand CommandInButton
{
get; set;
}
public static readonly BindableProperty CommandInButtonProperty =
BindableProperty.Create(
propertyName: "CommandInButton",
returnType: typeof(System.Windows.Input.ICommand),
declaringType: typeof(CustomControl),
defaultValue: null,
propertyChanged: CommandInButtonPropertyChanged);
private static void CommandInButtonPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (CustomControl)bindable;
control.ButtonInFrame.Command = (System.Windows.Input.ICommand)newValue;
}

WPF: DependencyProperty works on TextBlock but not on custom control

Edit: a sample project can be found here.
I am using a ListBox inside my main window, which I later bind to an ObservableCollection. I use both a TextBlock and a custom control which I bind to the same property of the collection. My problem is that the TextBlock gets properly updated, whereas the custom control doesn’t (it gets default constructed but its Text property is never updated by the binding).
<ListBox Name="MyCustomItemList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ItemText}"/>
<local:MyCustomBlock Text="{Binding ItemText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I implemented MyCustomBlock as a child of System.Windows.Controls.Canvas with a Text dependency property:
public class MyCustomBlock : Canvas
{
public MyCustomBlock() => Text = "<default>";
public MyCustomBlock(string text) => Text = text;
private static void TextChangedCallback(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
...
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("", TextChangedCallback));
}
Finally, this is the data I bind to the ListBox in the MainWindow constructor:
public class MyCustomItem
{
public MyCustomItem(string text) => ItemText = text;
public string ItemText { get; set; }
}
public MainWindow()
{
InitializeComponent();
var list = new ObservableCollection<MyCustomItem>();
list.Add(new MyCustomItem("Hello"));
list.Add(new MyCustomItem("World"));
MyCustomItemList.ItemsSource = list;
}
Did I forget something in my setup? How come TextBlock.Text is seemingly properly updated but not MyCustomBlock.Text?
Dependency properties can get their value from several sources and so WPF employs a precedence system to determine which value applies. "Local" values (provided using SetValue or SetBinding) will override anything provided by the creating template.
In your case, your setting a "local" value in the constructor (presumably intending it to behave as a default value). A better way to set a default value is by providing it in the PropertyMetadata.
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("<default>", TextChangedCallback));

error on accessing a property of a control in a wpf user control

i have created a wpf user control with a text box and a combo box.
for accessing the text property of the text box i have used the below code
public static readonly DependencyProperty TextBoxTextP = DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor));
public string TextBoxText
{
get { return txtValue.Text; }
set { txtValue.Text = value; }
}
in another project i have used the control and bind the text as below:
<textboxunitconvertor:TextBoxUnitConvertor Name="wDValueControl" TextBoxText="{Binding _FlClass.SWa_SC.Value , RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="161" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top"/>
i am certain that the class that is used for binding is properly working because when i used it to bing with a text box directly in my project it works properly but when i bind it to the text property of textbox in usercontrol it brings null and the binding does not work.
can any one help me?
Your dependency property declaration is wrong. It has to look like shown below, where the getter and setter of the CLR property wrapper call the GetValue and SetValue methods:
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor));
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
In the XAML of your UserControl, you would bind to the property like this:
<TextBox Text="{Binding TextBoxText,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
If you need to get notified whenever the TextBoxText property changes, you could register a PropertyChangedCallback with PropertyMetadata passed to the Register method:
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor),
new PropertyMetadata(TextBoxTextPropertyChanged));
private static void TextBoxTextPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TextBoxUnitConvertor t = (TextBoxUnitConvertor)o;
t.CurrentValue = ...
}
You are not creating the dependency property right. Use this code:
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register("TextBoxText", typeof(string), typeof(TextBoxUnitConvertor), new PropertyMetadata(""));
Then in your custom control Bind the TextBoxText to the value of txtValue.Text

Categories