Picker text goes out of picker box - xamarin

I am working in Xamarin.Forms where I have a picker like this:
<Picker Grid.Row="1" Grid.Column="1" x:Name="pickerForSearchMode"
Title="Search Mode" BackgroundColor="White" >
<Picker.Items>
<x:String>Mode 1</x:String>
<x:String>Mode 2</x:String>
<x:String>Mode 3</x:String>
<x:String>Mode 4</x:String>
</Picker.Items>
It displays perfect on iOS and Android, but in a UWP app it has an issue. The text is displaying outside of the picker like this:

You will need to use a Custom Renderer, see the official Xamarin documentation.
Once the element is rendered, you will need to use the value of the Title property and set it as the PlaceholderText of the underlying UWP ComboBox.
Check the official renderer here which is in Xamarin Forms, you can see that the UpdateTitle method sets the Header property of the ComboBox. All you will need to do is to replace it with the PlaceholderText property in your own renderer.
Required steps
First you create a custom Picker in your PCL project:
public class PlaceholderPicker : Picker
{
}
Replace your Picker reference in XAML with the PlaceholderPicker:
<local:PlaceholderPicker VerticalOptions="Center" x:Name="PickerForSearchMode"
Title="Search Mode" BackgroundColor="White" >
<Picker.Items>
<x:String>Mode 1</x:String>
<x:String>Mode 2</x:String>
<x:String>Mode 3</x:String>
<x:String>Mode 4</x:String>
</Picker.Items>
</local:PlaceholderPicker>
You will have to add a namespace import to the page element - xmlns:local="clr-namespace:NamespaceWithTheCustomControl".
Now in the UWP project you now create a custom renderer for the control and add a ExportRenderer attribute for the assembly to indicate Xamarin Forms to use this renderer in UWP.
[assembly: ExportRenderer(
typeof( PlaceholderPicker ),
typeof( PlaceholderPickerRenderer ) )]
namespace FormsApp.UWP
{
public class PlaceholderPickerRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.PlaceholderText = Element.Title;
Control.Header = null;
}
}
}
}
Note that we first set the PlaceholderText and then clear the Header, which was already set by the default renderer.
I have created a sample solution with this here on my GitHub, you can clone it and try it out yourself :-) .

Related

Xamarin - Binding to a Property of a child control inside a custom control

I am working on a Xamarin App. I am making a custom control with a Label control, a DatePicker control and an Entry control. I had to create quite a few BindableProperties for the Date Control in the Custom Control such as MaximumDate, MinimumDate Property among many other properties. As far as I understand, the reason I have to create these BindableProperty members in my Custom control are because I have no access to the properties of the child controls when the custom control is used on a view. Is there a way to access the properties of the child control that is embedded in a custom control? I could save a lot of lines of code defining the BindableProperties and their CLR properties and other things.
Here is my Custom control XAML (I have removed all the elements except for a Label element in the posted code to make the code more readable and for brevity.
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DPSReminders.Controls.CustomLabel"
xmlns:controls="clr-namespace:DPSReminders.Controls"
xmlns:sfLayout="clr-namespace:Syncfusion.XForms.TextInputLayout;assembly=Syncfusion.Core.XForms"
xmlns:sfPicker="clr-namespace:Syncfusion.XForms.Pickers;assembly=Syncfusion.SfPicker.XForms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:fai="clr-namespace:FontAwesome">
<StackLayout.Resources>
<ResourceDictionary>
</ResourceDictionary>
</StackLayout.Resources>
<StackLayout Orientation="Horizontal" Margin="10">
<Label x:Name="myLabel"
Text=""
FontFamily="FASolid"
VerticalOptions="Center"
HorizontalOptions="Start"
Margin="10">
</Label>
</StackLayout>
The code behind file:
public class CustomLabel : StackLayout
{
public static readonly BindableProperty LabelTextProperty =
BindableProperty.Create(nameof(LabelText), typeof(string), typeof(CustomLabel),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: LabelTextPropertyChanged);
public string LabelText
{
get => GetValue(LabelTextProperty)?.ToString();
set => SetValue(LabelTextProperty, value);
}
private static void LabelTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = bindable as CustomLabel;
control.myLabel.Text = newValue?.ToString();
}
public CustomLabel()
{
InitializeComponent();
}
}
Here is the page where I am trying to use the custom control.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DPSReminders.Views.DateTimeTest"
xmlns:fai="clr-namespace:FontAwesome"
xmlns:vm="clr-namespace:DPSReminders.ViewModels"
xmlns:controls="clr-namespace:DPSReminders.Controls"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
>
<controls:CustomLabel LabelText = "{Binding MyLabelText}"/>
</ContentPage>
I am wondering if I could do a line like this in my label, that would make my life much easier.
<controls:CustomLabel:myLabel.Text = "{Binding MyLabelText}"/>
Then, I can remove all the code creating the BindableProperties and the supporting CLR properties etc. when a built-in bindable property for the same purpose is already available in the child control. Is this something we can do?
Try using template instead.
Xamarin.Forms control templates enable you to define the visual structure of ContentView derived custom controls, and ContentPage derived pages. Control templates separate the user interface (UI) for a custom control, or page, from the logic that implements the control or page. Additional content can also be inserted into the templated custom control, or templated page, at a pre-defined location.
Doc link:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/control-template

Xamarin Forms Android Autosize Label TextCompat pre android 8 doesn't autosize text

I want to utilise the auto-sizing feature of android textviews in my xamarin forms solution so that as the text length grows, the font sizes shrinks to never overflow the bounds of the label, and doesn't get truncated. I've created a custom Label control to do so and added an android custom renderer. It's not working in Android 7 and below. It is working in Android 8 and above.
According to the docs autosize support was introduced in android 8, but can be supported back to Android 4 with AppCompat.v4. However, my custom rendered label just renders the default font size in Android pre 8. It works fine in 8+ devices, the label text resizes as needed to not overflow the bounds. The accepted answer to this question with a similar issue on native android says it can be to do with not setting a width and height, I've tried setting widthrequest and heightrequest explicitly and it doesn't change anything. Also setting maxlines=1 doesn't change anything. An alternative thread suggests that custom fonts are the culprit. I created a vanilla forms solution using the default device font, and get the same effect.
My code:
internal class AutosizeLabelRenderer : LabelRenderer
{
#region constructor
public AutosizeLabelRenderer(Context context) : base(context)
{
}
#endregion
#region overridable
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize,
autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}
#endregion
}
public class AutoSizeLabel : Label
{
public int AutoSizeMaxTextSize
{
get => (int)GetValue(AutoSizeMaxTextSizeProperty);
set => SetValue(AutoSizeMaxTextSizeProperty, value);
}
public static readonly BindableProperty AutoSizeMaxTextSizeProperty = BindableProperty.Create(
nameof(AutoSizeMaxTextSize), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
public int AutoSizeMinTextSize
{
get => (int)GetValue(AutoSizeMinTextSizeProperty);
set => SetValue(AutoSizeMinTextSizeProperty, value);
}
public static readonly BindableProperty AutoSizeMinTextSizeProperty = BindableProperty.Create(
nameof(AutoSizeMinTextSize), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
public int AutoSizeStepGranularity
{
get => (int)GetValue(AutoSizeStepGranularityProperty);
set => SetValue(AutoSizeStepGranularityProperty, value);
}
public static readonly BindableProperty AutoSizeStepGranularityProperty = BindableProperty.Create(
nameof(AutoSizeStepGranularity), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
//
}
Not working: Android 7 - text does not shrink
Working as expected: Android 8 and above
Xaml for above images:
<StackLayout HeightRequest="200" WidthRequest="100">
<Label Text="Fixed width and height, sentences get longer, text should shrink" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="A small sentence"
WidthRequest="200" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="A larger sentence that shrinks"
WidthRequest="200" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="An even larger sentence that shrinks more."
WidthRequest="200" />
</StackLayout>
TextView font size changes with the size of the control, which is new in Android 8.0 (API26),therefore, compatibility issues need to be considered when using the previous version.You could change the TextView to AppCompatTextView.
Change your
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
AppCompatTextView appCompatTextView = new AppCompatTextView(_context);
appCompatTextView.Text = Element.Text;
appCompatTextView.SetMaxLines(1);
SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control,autoLabel.AutoSizeMinTextSize,autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}
Leo Zhu's answer got me most of the way there. There were a couple of extra steps I needed to take to get it fully working, so I'm posting the code as a separate answer here.
Differences between mine and Leo's answer:
Creating a new native control in scope like Leo suggested meant that it worked for a while but got disposed by the garbage collector and caused an exception when returning to the page after navigating away. To fix this I needed to override a property called ManageNativeControlLifetime to return false, and then manually manage disposing the object by overriding the dispose method and calling Control.RemoveFromParent();. This advice comes from a xamarin staff member in this thread.
Formatting and binding context are not automatically inherited when creating the new native control and need to be set manually. I needed to add those based on my needs using the android specific binding syntax. You may need to add other formatting and binding code based on your needs, I'm just doing font colour, gravity and binding context here.
I set the binding context with
appCompatTextView.SetBindingContext(autoLabel.BindingContext);
Once the binding context was set, I needed to add a new string property to my XF AutoSizeLabel class to pass in through XAML, then use it to set the binding path for the relevant property (In my case the text property). If more than one binding is required, you would need to add multiple new binding path properties for each required property. I set a specific binding like this:
appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));
To facilitate this in my Xamarin Forms Xaml, my Xaml went from <Label Text="{Binding MyViewModelPropertyName}" /> to <controls:AutoSizeLabel TextBindingPath="MyViewModelPropertyName" />
Here's the full code of the renderer:
protected override bool ManageNativeControlLifetime => false;
protected override void Dispose(bool disposing)
{
Control.RemoveFromParent();
base.Dispose(disposing);
}
private AppCompatTextView appCompatTextView;
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(
autoLabel.AutoSizeMinTextSize,
autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity,
(int)ComplexUnitType.Sp);
return;
}
appCompatTextView = new AppCompatTextView(Context);
appCompatTextView.SetTextColor(Element.TextColor.ToAndroid());
appCompatTextView.SetMaxLines(1);
appCompatTextView.Gravity = GravityFlags.Center;
appCompatTextView.SetBindingContext(autoLabel.BindingContext);
appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));
SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize, autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}

How to change Picker background Color in Xamarin MAC Platform

After updating the Xamarin Library and MAC OS to latest system, facing issue in Picker Background color and color of item that is selected/focused in picker for MAC Platform in my visual studio 2017.
Picker that is filled with multiple values
On opening the picker can not able to set the background color and the item that is selected is also not visible because of its color
How can I set the background color of that picker and the color of that focused/selected item of picker?
This seems to be caused for the new Theme used by Mojave.
One way to overcome this issue is by setting a value that will be visible on both Light and Dark them, for me it worked the Green.
Adding this to your XAML should be sufficient
<Picker.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="macOS" Value="Green"/>
</OnPlatform>
</Picker.TextColor>
Making the change only on your MacOs project leaving the others as they are.
<Picker HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Picker.Items>
<x:String>Dell</x:String>
<x:String>HP</x:String>
<x:String>Mac</x:String>
<x:String>Asus</x:String>
<x:String>Lenovo</x:String>
<x:String>Acer</x:String>
<x:String>Micrsoft</x:String>
</Picker.Items>
<Picker.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="macOS" Value="Green"/>
</OnPlatform>
</Picker.TextColor>
</Picker>
Note: The TextColor will only affect the selected item text color.
Hope this helps.-
To change BackGroundColor of all Picker items for iOS you need to use custom renderer.
Your Picker in shared project
<StackLayout>
<Picker x:Name="picCities" ></Picker>
</StackLayout>
In your ContentPage cs file assign ItemsSource to Picker
public MainPage()
{
InitializeComponent();
picCities.ItemsSource = new List<string> { "Hyderabad", "Bhopal", "Indore", "Jabalpur", "Mumbai", "Ahmedabad" };
picCities.SelectedIndex = 0;
}
Now in your iOS project add one .cs file name PickerCustomRenderer & add this code
[assembly: ExportRendererAttribute(typeof(Picker), typeof(PickerCustomRenderer))]
namespace picker.iOS
{
public class PickerCustomRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
UITextField textField = Control;
UIPickerView pickerView = textField.InputView as UIPickerView;
pickerView.BackgroundColor = UIColor.Red;
}
}
}
Output

how to use radio buttons in xamarin forms

Creating a Registration page, I need to get the following data from user.
First Name
Last Name
Username
Email
Password
Date of Birth
Gender
User Role
For the last two parameters, I am unable to find how to use radio buttons in Xamarin.Forms. Following is my code for the Registration Page.
<StackLayout BackgroundColor="#30af91" Padding="60">
<Entry Text="{Binding FirstName}" Placeholder="First Name"/>
<Entry Text="{Binding LastName}" Placeholder="Last Name"/>
<Entry Text="{Binding UserName}" Placeholder="Last Name"/>
<Entry Text="{Binding Email}" Placeholder="Email" />
<Entry Text="{Binding Password}" Placeholder="Password" IsPassword="True"/>
<Entry Text="{Binding ConfirmPassword}" Placeholder="Confirm Password" IsPassword="True"/>
<DatePicker MinimumDate="1/1/1948" MaximumDate="12/31/2007"/>
<!--Radio buttons for Gender
1. Male 2.Female-->
<!--Radio Buttons for UserRole
1. Admin 2.Participant-->
<Button Command="{Binding RegisterCommand}" Text="Register"/>
<Label Text="{Binding Message}" />
</StackLayout>
Xamarin forms does not provide Radio Button.
You can either use
1)Switch
2)Picker
or any other component to fulfill your requirement
UPDATE
The xamarin forms update version 4.6 has introduced the Radio button control, Here is the official documentation
I think there is a simpler solution that is fairly easy and requires no libraries. Really a a radio group is just a fancy ListView. You would just need to create a viewModel for each radio button that has a IsSelected flag and switch between 2 images. I had a need to allow a user to select how long a token persisted:
XAML
<ListView
HasUnevenRows="True"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Durations}"
ItemSelected="ListView_ItemSelected"
SelectedItem="{Binding SelectedDuration}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Horizontal">
<Image
HeightRequest="18"
IsVisible="{Binding IsSelected}"
Source="radioButtonChecked.png"
WidthRequest="18"/>
<Image
HeightRequest="18"
IsVisible="{Binding IsUnselected}"
Source="radioButtonUnchecked.png"
WidthRequest="18"/>
<Label
Margin="8,0,0,0"
Text="{Binding Caption}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
We create a listview in our content page and listen for the ItemSelected event. Each list item is a horizontal stack panel where we flip between two images depending on the selected state
Code Behind
public partial class LoginPage : ContentPage
{
LoginPageViewModel LoginPageViewModel { get; }
public LoginTwoFactorFrequencyPage ()
{
BindingContext = LoginPageViewModel = new LoginPageViewModel();
InitializeComponent ();
}
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
LoginPageViewModel.UpdateSelected(e.SelectedItem as PersistenceDuration);
}
}
The page's code behind instantiates a view model and calls an UpdateSelected method with the newly selected item on the page's view model*
RadioButton ViewModel
The view model for each radio button:
public class PersistenceDuration : ViewModelBase
{
bool isSelected;
public string Caption { get; set; }
public TwoFactorTokenPersistenceDuration Duration { get; set; }
public bool IsSelected
{
get => isSelected;
set
{
isSelected = value;
OnPropertyChanged();
OnPropertyChanged("IsUnselected");
}
}
public bool IsUnselected => !IsSelected;
public PersistenceDuration(string caption, TwoFactorTokenPersistenceDuration duration)
{
Caption = caption;
Duration = duration;
IsSelected = false;
}
}
The radio button view model holds selection info and the caption. We make sure to fire OnPropertyChanged whenever the selected state changes
Page ViewModel
public class LoginPageViewModel : ViewModelBase
{
PersistenceDuration duration;
PersistenceDuration selectedDuration;
public ObservableCollection<PersistenceDuration> Durations { get; }
public PersistenceDuration SelectedDuration
{
get => selectedDuration;
set
{
if (value != null)
{
duration = value;
UpdateSelected(duration);
}
OnPropertyChanged();
}
}
public LoginTwoFactorFrequencyViewModel()
{
Durations = new ObservableCollection<PersistenceDuration>(
new List<PersistenceDuration>()
{
new PersistenceDuration(AppResources.Save_code__forever, TwoFactorTokenPersistenceDuration.Forever),
new PersistenceDuration(AppResources.ChatRequireEvery30Days, TwoFactorTokenPersistenceDuration.ThirtyDays),
new PersistenceDuration(AppResources.ChatRequireEveryLogin, TwoFactorTokenPersistenceDuration.None),
});
}
public void UpdateSelected(PersistenceDuration persistenceDuration)
{
foreach (var item in Durations)
item.IsSelected = persistenceDuration == item;
}
}
In the page view model we create a list of radio button view models that the XAML binds to. When we UpdateSelected() all the IsSelected states are updated which trigger binding updates which flip the image.
You will still need to do something about the highlight when someone selects an item, but that is easy enough to find on the internet :)
You can use XLabs plugin from manage NuGets package. After installing you can use like this:
In Xaml:
controls:BindableRadioGroup x:Name="Radiobtn"
In C#:
string[] gender = {"MAlE","FEMALE"}
Radiobtn.Add(gender)
Refer Link
https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/samples/XLabs.Samples/XLabs.Samples/Pages/Controls
You can get the radio button effect without a package. Use Labels with text unicode circle \u26AA or \u25CB. Attach a tab gesture recognizer to each label.
When tapped, change the text of the selected button to unicode circle bullet \u29BF and change the text of the other button(s) back to unicode circle \u26AA.
Test on your preferred platforms as each platform may display somewhat differently. You may need to adjust the font size as you change the text.
If you want real radiobuttons you can xlabs their package (https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/src/Forms/XLabs.Forms/Controls/RadioButton)
Personally I'd just use a picker, Xlabs package hasn't been updated in a while so their might be some bugs in the radiobutton
You can use image as a radio button. When tou you click on it, it can change. It is not a good way to do it though.
This is xaml code:
<Image Scale="0.7" HorizontalOptions="Start" x:Name="radioButton" Source="unRadioBtn.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="radioButton_Clicked"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
And this is .cs:
private void radioButton_Clicked(object sender, EventArgs e)
{
radioButton.Source = "radioBtn.png";
}
Xamarin.Forms 4.6 introduced a new RadioButton control. You can find the documentation here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
You can use the switch component. Also you can see the implementation for a checkbox component from the XLabs project which is now discontinued, get the code and modify it as you need.
Hint: You're gonna need the custom renderers per platform for it to work .
You need to use Picker
https://developer.xamarin.com/api/type/Xamarin.Forms.Picker/
Actually it is the best alternative to RadionButton On Xamarin.forms
XLabs RadioButton and BindableRadioGroup work well: XLabs RadioButton for Xamarin Forms
Here's a simple Yes/No radio using the BindableRadioGroup:
var answers = new List<string>();
answers.Add("Yes");
answers.Add("No");
var RadioGroup = new XLabs.Forms.Controls.BindableRadioGroup()
{
ItemsSource = answers,
Orientation = StackOrientation.Horizontal
};
Xamarin Forms now provides a Radio Button control.
See docs here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
As of XF 4.8 this is still experimental and I've not yet used this feature so can't comment on its stability.

UWP User Controls expose internal StackPanel

I'm relatively new to UWP being a developer used to using Windows forms.
In windows forms what I'm attempting to do I would do quite easily through form inheritance.
UWP seems to have no form of layout inheritance, it appears that you have to design a UserControl to achieve this and then place it on the page and fill the page with the control.
I have a control which contains a 2 column grid. In the first column I have placed a StackPanel and at the moment the second column is empty.
<UserControl x:Name="userControl"
x:Class="App1.BaseLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="11*"/>
<ColumnDefinition Width="29*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0" Background="#FF0B1E70"/>
</Grid>
</UserControl>
And the code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using App1.Annotations;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace App1
{
public sealed partial class BaseLayoutControl : INotifyPropertyChanged
{
public BaseLayoutControl()
{
InitializeComponent();
}
public StackPanel LeftStackPanel
{
get { return ( StackPanel ) GetValue( LeftStackPanelProperty ); }
set
{
SetValue( LeftStackPanelProperty, value );
OnPropertyChanged( nameof( LeftStackPanel ) );
}
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LeftStackPanelProperty =
DependencyProperty.Register( "LeftStackPanel", typeof( StackPanel ), typeof( BaseLayoutControl ), null );
public event PropertyChangedEventHandler PropertyChanged;
[ NotifyPropertyChangedInvocator ]
private void OnPropertyChanged( [ CallerMemberName ] string propertyName = null )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
Now what I want to do is expose the StackPanel as a property "LeftStackPanel" and also at design time where I have placed the control on the page I would like to be able to drag and drop Buttons, CheckBoxes, Text etc into the StackPanel inside the control. Also I would like to expose the second unused grid column so that items can also be place in it.
All the examples I can find seem to only deal with simple object types like ints and strings.
Any help appreciated.
Terry
This is a longtime challenge when migrating from WinForms to XAML.
The short answer is that you should be looking at creating a CustomControl and not a UserControl.
A UserControl is a self-contained UI element that you provide data and it displays it.
When you need to customize not only the information displayed but also the way you display it, you need to use a CustomControl. That will enable the scenarios that you are trying to achieve in your case.
Here is a very good visual tutorial on how to author your first CustomControl. Also, for mode details on the differences between User and Custom control check this post. In fact there are many articles and tutorials out there on this topic. Most of them are from the WPF days, but everything still applies in UWP.

Resources