Initialize Collection of DataTemplates in XAML - c#

I have this DependencyProperty
public ObservableCollection<DataTemplate> WizardTemplateCollection
{
get { return (ObservableCollection<DataTemplate>)GetValue(WizardTemplateCollectionProperty); }
set { SetValue(WizardTemplateCollectionProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty WizardTemplateCollectionProperty =
DependencyProperty.Register("WizardTemplateCollection", typeof(ObservableCollection<DataTemplate>), typeof(CustomWizardControl), new PropertyMetadata(new ObservableCollection<DataTemplate>()));
And want to do this:
<custom:CustomWizardControl>
<custom:CustomWizardControl.WizardTemplateCollection>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
</custom:CustomWizardControl.WizardTemplateCollection>
</custom:CustomWizardControl>
What DataType do i need? Or how can i initialize a ObservableCollection in XAML.
Additional:
public class CustomWizardControl : Control {}

Your CustomWizardControl class must inherit from DepenedencyObject or one of its derived types like for example UIElement or Control:
public class CustomWizardControl : Control
{
public ObservableCollection<DataTemplate> WizardTemplateCollection
{
get { return (ObservableCollection<DataTemplate>)GetValue(WizardTemplateCollectionProperty); }
set { SetValue(WizardTemplateCollectionProperty, value); }
}
...
}
This works:
public class CustomWizardControl : Control
{
public CustomWizardControl()
{
WizardTemplateCollection = new ObservableCollection<DataTemplate>();
}
public ObservableCollection<DataTemplate> WizardTemplateCollection
{
get { return (ObservableCollection<DataTemplate>)GetValue(WizardTemplateCollectionProperty); }
set { SetValue(WizardTemplateCollectionProperty, value); }
}
public static readonly DependencyProperty WizardTemplateCollectionProperty =
DependencyProperty.Register("WizardTemplateCollection", typeof(ObservableCollection<DataTemplate>), typeof(CustomWizardControl), new PropertyMetadata(null));
}
<local:CustomWizardControl x:Name="ctrl">
<local:CustomWizardControl.WizardTemplateCollection>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
</local:CustomWizardControl.WizardTemplateCollection>
</local:CustomWizardControl>
<TextBlock Text="{Binding WizardTemplateCollection.Count, ElementName=ctrl}" />

You cannot set the generic parameter of ObservableCollection<T> directly in XAML.
Instead you should create your custom DataTemplateCollection inherited from ObservableCollection<DataTemplate>. Then you will be able to use your collection as usual.
public class DataTemplateCollection : ObservableCollection<DataTemplate>
{
}
<custom:CustomWizardControl>
<custom:CustomWizardControl.WizardTemplateCollection>
<custom:DataTemplateCollection>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
<DataTemplate>
<Rectangle></Rectangle>
</DataTemplate>
</custom:DataTemplateCollection>
</custom:CustomWizardControl.WizardTemplateCollection>
</custom:CustomWizardControl>
Additional note: NEVER initialize the default value of dependency properties with mutable objects, because this single mutable instance will be used by every control instance. Instead you must set the default value to null and assing the initial value in the constructor.

Related

Xamarin Forms - Make custom cell bind to original listview itemsource when calling event "ItemSelected"

I have searched around and I dont think I am finding the answer to my question. I am new to xamarin so i hope I am using the correct terminology. I am experimenting with custom cells in listviews. My aim is to reuse the custom cell throughout multiple parts of my application but when I use the event "ItemSelected" it comes back with the bindings to the custom cell and not my original listview itemsource bindings. I understand why I think but I am unsure how to bind the ItemSelected to the original source. Am I using the right method here? I am completely lost if I am honest.
This is my custom cell code:
public partial class ListCell : ViewCell
{
public static readonly BindableProperty LabelHeaderProperty = BindableProperty.Create("LabelHeader", typeof(string), typeof(ListCell));
public string LabelHeader
{
get { return (string)GetValue(LabelHeaderProperty); }
set { SetValue(LabelHeaderProperty, value); }
}
public static readonly BindableProperty LabelSmallProperty = BindableProperty.Create("LabelSmall", typeof(string), typeof(ListCell));
public string LabelSmall
{
get { return (string)GetValue(LabelSmallProperty); }
set { SetValue(LabelSmallProperty, value); }
}
public ListCell()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = new
{
LabelHeader = this.LabelHeader,
LabelSmall = this.LabelSmall
};
}
}
Here is my ListView
<ListView x:Name="MyListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
IsPullToRefreshEnabled="true"
ItemSelected="OnItemSelected"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<extensions:ListCell LabelHeader="{Binding Description}"
LabelSmall="{Binding Description}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Thank you very much in advance :)
According to your code, when binding to a custom cell type's BindableProperty instances, the UI controls displaying the BindableProperty values should use the OnBindingContextChanged override to set the data to be displayed in each cell.
public class ListCell:ViewCell
{
Label headerLabel, smallLabel;
public static readonly BindableProperty LabelHeaderProperty = BindableProperty.Create("LabelHeader", typeof(string), typeof(ListCell),"name");
public string LabelHeader
{
get { return (string)GetValue(LabelHeaderProperty); }
set { SetValue(LabelHeaderProperty, value); }
}
public static readonly BindableProperty LabelSmallProperty = BindableProperty.Create("LabelSmall", typeof(string), typeof(ListCell),"small label");
public string LabelSmall
{
get { return (string)GetValue(LabelSmallProperty); }
set { SetValue(LabelSmallProperty, value); }
}
public ListCell()
{
StackLayout stack = new StackLayout { Orientation=StackOrientation.Horizontal};
headerLabel = new Label { FontAttributes = FontAttributes.Bold };
smallLabel = new Label();
stack.Children.Add(headerLabel);
stack.Children.Add(smallLabel);
View = stack;
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null)
{
headerLabel.Text = LabelHeader;
smallLabel.Text = LabelSmall;
}
}
}
<ListView
x:Name="listView"
ItemSelected="listView_ItemSelected"
ItemsSource="{Binding items}">
<ListView.ItemTemplate>
<DataTemplate>
<local:ListCell LabelHeader="{Binding Name}" LabelSmall="{Binding description}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
But You can use TextCell in ListView's DataTemplate directly, don't need to create custom viewcell.
<ListView ItemsSource="{Binding items}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Detail="{Binding description}" Text="{Binding name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
About using TextCell, you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/data-and-databinding#binding-cells

WPF ItemsControl data binding with variable path

I am trying to create a UserControl that contains an ItemsControl which should display Parameters and their values. The values must be editable and these values should be transferred back to the ViewModel.
It should be possible to define which property represents the parameter name and which property the parameter value.
Parameter class:
public class Parameter
{
public string Name { get; set; }
public string Value { get; set; }
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
...
public ObservableCollection<Parameter> Parameters { get; set; }
...
}
UserControl ("ParameterList.xaml"):
<UserControl x:Name="ParameterList" ...>
<Border BorderBrush="Black" BorderThickness="1" Height="100">
<!-- I don't know if this binding expression is correct -->
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:ParameterList}}, Path=Parameters}">
<ItemsControl.ItemTemplate>
<Border>
<!-- The property path defined via "ParameterNameMember" should be bound. -->
<TextBlock Text="{Binding ???}" />
<!-- The property path defined via "ParameterValueMember" should be bound. -->
<!-- The value edited in this TextBox should be transferred to the ViewModel. -->
<TextBox Text="{Binding ???}" />
</Border>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</UserControl>
UserControl code behind:
public partial class ParameterList : UserControl
{
public IEnumerable Parameters
{
get => (IEnumerable)GetValue(ParametersProperty);
set => SetValue(ParametersProperty, value);
}
public string ParameterNameMember
{
get => (string)GetValue(ParameterNameMemberProperty);
set => SetValue(ParameterNameMemberProperty, value);
}
public string ParameterValueMember
{
get => (string)GetValue(ParameterValueMemberProperty);
set => SetValue(ParameterValueMemberProperty, value);
}
public static readonly DependencyProperty ParametersProperty =
DependencyProperty.Register("Parameters", typeof(object),
typeof(ParameterList), new PropertyMetadata(default(IEnumerable)));
public static readonly DependencyProperty ParameterNameMemberProperty =
DependencyProperty.Register("ParameterNameMember", typeof(string),
typeof(ParameterList), new PropertyMetadata(""));
public static readonly DependencyProperty ParameterValueMemberProperty =
DependencyProperty.Register("ParameterValueMember", typeof(string),
typeof(ParameterList), new PropertyMetadata(""));
public ParameterList()
{
InitializeComponent();
}
}
I want to use the control as follows:
<uc:ParameterList
x:Name="ParameterList"
Parameters="{Binding Parameters}"
ParameterNameMember="Name"
ParameterValueMember="Value" />
Since I don't have that much experience with WPF, I need some help with the data binding. I would be very grateful if I could get some useful suggestions.

WPF custom ContentPresenter does not bind properties

I have a custom class derived from ContentPresenter which has a DepenedencyProperty. However, when I attempt to bind it, nothing happens (Source's getter is not called, content is not updated, no errors are logged in Trace):
public class IsReadOnlyCellPresenter : ContentPresenter
{
[Bindable(true)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IsReadOnlyCellPresenter), new FrameworkPropertyMetadata(null));
}
xaml:
<local:IsReadOnlyCellPresenter Text="{Binding Path=MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<local:IsReadOnlyCellPresenter.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Text, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</local:IsReadOnlyCellPresenter.ContentTemplate>
</local:IsReadOnlyCellPresenter>
If I use static value (e.g. Text="Foo") it works as expected. If I derive from ContentControl (class IsReadOnlyCellPresenter : ContentControl), the Text binding works but not Mode=TemplatedParent which points to nowhere.
What is the issue with ContentPresenter here? Can ContentPresenter be derived from?

How to set custom properties in an inherited control in xaml

I built a custom control TabularListView based on ListView with a new Property, an ObservableCollection of TabularListViewHeaderColumn.
TabularListView:
class TabularListView : ListView
{
public TabularListView()
{
Columns = new ObservableCollection<TabularListViewHeaderColumn>();
}
public ObservableCollection<TabularListViewHeaderColumn> Columns
{
get { return (ObservableCollection<TabularListViewHeaderColumn>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", typeof(ObservableCollection<TabularListViewHeaderColumn>), typeof(TabularListView), new PropertyMetadata(new ObservableCollection<TabularListViewHeaderColumn>()));
}
TabularListViewHeaderColumn:
class TabularListViewHeaderColumn : ContentControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
public string MappingName
{
get { return (string)GetValue(MappingNameProperty); }
set { SetValue(MappingNameProperty, value); }
}
public static readonly DependencyProperty MappingNameProperty =
DependencyProperty.Register("MappingName", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
}
I try to use it like shown below, setting the TabularListViewHeaderColumns directly in XAML:
<Controls:TabularListView>
<Controls:TabularListView.Header>
<ItemsControl>
<DataTemplate>
<Button Content="{Binding Title}" Width="{Binding Width}"
FontWeight="Bold"/>
</DataTemplate>
</ItemsControl>
</Controls:TabularListView.Header>
<Controls:TabularListView.Columns>
<Controls:TabularListViewHeaderColumn Title="Test" MappingName="Inactive" Width="60"/>
<Controls:TabularListViewHeaderColumn Title="Articlenumber" MappingName="DeviceType.ArticleNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Serialnumber" MappingName="SerialNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Inventorynumber" MappingName="EndCustNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Description" MappingName="DeviceType.Description1" Width="300"/>
</Controls:TabularListView.Columns>
</Controls:TabularListView>
Usualy when creating a regular Custom Control like in this example https://social.technet.microsoft.com/wiki/contents/articles/32828.uwp-how-to-create-and-use-custom-control.aspx , I need to bind the respected property to the DependencyProperty, but since I only inherit from ListView and don't have any Xaml, I'm quiet confused how to do so.

wpf custom control local property binding

I have custom control named- RoomAndPersonsAccommodationItem and inside I have Property Room:
public static readonly DependencyProperty roomProperty =
DependencyProperty.Register("Room", typeof(RoomData),
typeof(RoomAndPersonsAccommodationItem), new FrameworkPropertyMetadata(null));
public RoomData RoomProperty
{
get { return GetValue(roomProperty) as RoomData; }
set { SetValue(roomProperty, value); }
}
private RoomData room = null;
public RoomData Room
{
get { return room; }
set { room = value; }
}
RoomAndPersonsAccommodationItem is set like DataTemplate on ListView:
<ListView Name="RoomsListView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Controls:RoomAndPersonsAccommodationItem Room="{Binding Path=., Mode=TwoWay}" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In class where is the above ListView- RoomsListView I have this code:
RoomsListView.ItemsSource = reservation.Rooms;
//reservation.Rooms is List<RoomData>
The problem is here:
<Controls:RoomAndPersonsAccommodationItem Room="{Binding Path=., Mode=TwoWay}" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
I want to bind Room property to room object from ItemSource on the ListView but I receive null.
Does anyone know where I'm wrong? Maybe here: Room="{Binding Path=., Mode=TwoWay}" or on DependencyProperty declaration?
Thanks
Martin
UPDATE:
Still doesn't work... The result always is null. I tried a lot of options like that to bind the Tag or DataContext but nothing happens. I suppose that the problem is not in the DependencyProperty. It is somewhere in binding I thing...
This is the code again:
private ReservationData reservation = null;
//Methods
private void fillForms()
{
//Logic
RoomsListView.ItemsSource = reservation.Rooms;
//Logic
}
This is the ListView with DataTemplate:
<ListView Name="RoomsListView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Controls:RoomAndPersonsAccommodationItem
Room="{Binding}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And DependencyProperty in RoomAndPersonsAccommodationItem:
public static readonly DependencyProperty RoomProperty =
DependencyProperty.Register(
"Room", typeof(RoomData), typeof(RoomAndPersonsAccommodationItem),
new FrameworkPropertyMetadata(null));
public RoomData Room
{
get { return (RoomData)GetValue(RoomProperty); }
set {
SetValue(RoomProperty, value); }
}
UPDATE 2
It's SOLVED
The above source is correct. The wrong result came from that I have checked the Room property before constructor has been finished. When I checked the result after constructor all was OK! Thank you!
Try this, one property and one dependency property for this property:
public static readonly DependencyProperty RoomProperty =
DependencyProperty.Register(
"Room", typeof(RoomData), typeof(RoomAndPersonsAccommodationItem),
new FrameworkPropertyMetadata(null));
public RoomData Room
{
get { return (RoomData)GetValue(RoomProperty); }
set { SetValue(RoomProperty, value); }
}
You can also remove the "Path" from the binding, it is not necessary.

Categories

Resources