Bindable property return only default value - c#

I want create user control (user view) in xamarin.forms. My contol has one property. Control choose what the element would be add to the page (Entry or Label). For it I use BindableProperty, but it return only default value. I dont understand what wrong?
Here my user control code:
public partial class UserView : ContentView
{
public UserView()
{
InitializeComponent();
StackLayout stackLayout = new StackLayout();
if (TypeElement == "label") //TypeElement return only "default value"
stackLayout.Children.Add(new Label { Text = "LABEL" });
else
stackLayout.Children.Add(new Entry { Text = "ENTRY" });
Content = stackLayout;
}
public static readonly BindableProperty TypeProperty = BindableProperty.CreateAttached("TypeElement", typeof(string), typeof(UserView), "default value");
public string TypeElement
{
get
{
return (string)GetValue(TypeProperty);
}
set
{
SetValue(TypeProperty, value);
}
}
}
Here I use my control on page:

Your TypeElement property is getting set after the constructor completes, you should be watching for when this property changes and then do what you need to do, for example:
public static readonly BindableProperty TypeProperty = BindableProperty.CreateAttached("TypeElement", typeof(string), typeof(UserView), "default value", propertyChanged:OnTypeElementChanged);
private static void OnTypeElementChanged(BindableObject bindable, object oldValue, object newValue)
{
var userView = bindable as UserView;
StackLayout stackLayout = new StackLayout();
if (userView.TypeElement == "label") //TypeElement return only "default value"
stackLayout.Children.Add(new Label { Text = "LABEL" });
else
stackLayout.Children.Add(new Entry { Text = "ENTRY" });
userView.Content = stackLayout;
}
I have tested this and it works, there are a couple of things about your implementation that confuse me though, such as why you are using an attached property instead of a regular bindable property, and also why you seem to have a XAML file associated with UserView if you're just going to replace the content anyway.

Related

Properties of custom control can only be filled by StaticResource but not with values defined in XAML

I've created my own ExpandableView based on this https://www.clearpeople.com/insights/blog/2019/February/how-to-create-a-contentview-with-expandable-functionality
but as all C# code.
My control looks like this (without the animation part)
public class ExpandableView : ContentView
{
public static readonly BindableProperty ExpandableContentProperty = BindableProperty.Create(nameof(ExpandableContent), typeof(View), typeof(ExpandableView));
public static readonly BindableProperty TitleTextProperty = BindableProperty.Create(nameof(TitleText), typeof(string), typeof(ExpandableView));
public View ExpandableContent
{
get => this._content;
set
{
if (this._content == value)
{
return;
}
OnPropertyChanging();
if (this._content != null)
{
this._ContentLayout.Children.Remove(this._content);
}
this._content = value;
this._ContentLayout.Children.Add(this._content);
OnPropertyChanged();
}
}
public string TitleText
{
get => this._Title.Text;
set
{
if (this._Title.Text == value)
{
return;
}
OnPropertyChanging();
this._Title.Text = value;
OnPropertyChanged();
}
}
private readonly StackLayout _OuterLayout;
private readonly StackLayout _ContentLayout;
private readonly StackLayout _TitleLayout;
private View _content;
private readonly Label _Title;
public ExpandableView()
{
this._OuterLayout = new StackLayout();
this._ContentLayout = new StackLayout();
this._TitleLayout = new StackLayout
{
Orientation = StackOrientation.Horizontal,
};
this._Title = new Label
{
HorizontalOptions = new LayoutOptions(LayoutAlignment.Start, true),
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
Text = "Title",
};
this._Title.FontSize = Device.GetNamedSize(NamedSize.Medium, this._Title);
this._TitleLayout.Children.Add(this._Title);
this._OuterLayout.Children.Add(this._TitleLayout);
this._OuterLayout.Children.Add(this._ContentLayout);
Content = this._OuterLayout;
}
}
But now, when I try to use it in XAML as I normally would:
<controls:ExpandableView TitleText="Equipment">
<controls:ExpandableView.ExpandableContent>
<StackLayout>
<Label Text="EQ_12345" />
<Button Command="{Binding ShowDatacommand}" />
</StackLayout>
</controls:ExpandableView.ExpandableContent>
</controls:ExpandableView>
Setting the properties to some values results in the title still showing me "Title" and no content being shown. If I instead put everything into the StaticResource Everything works just fine:
<controls:ExpandableView ExpandableContent="{StaticResource ExpendableViewContent}"
TitleText="{StaticResource EquiString}" />
While testing, I set some breakpoints within the properties, and only when I used {StaticResource} the properties were set. All values defined directly in XAML were never passed to the properties. What am I doing wrong here?
When defining your own BindableProperty properties, the definitive source for the values is expected to be accessed via BindableObject.SetValue/BindableObject.GetValue. The Xamarin runtime can directly use that rather than going through your get/set methods.
Using TitleText as an example, the implementation should be something like:
public string TitleText
{
get => (string)GetValue(TitleTextProperty);
set
{
SetValue(TitleTextProperty, value);
}
}
The linked article does do this.
In order to create the link between the property and the displayed title, establish data binding in the constructor to link the Text of the title label to the TitleText property in the ExpandableView constructor:
_Title.SetBinding(Label.TextProperty, new Binding(nameof(TitleText)) { Source = this });

Why won't checkmarks appear in an TreeListView checkbox when CheckedAspectName name is set (and how do I programmatically set checkboxes)?

My question is based on the ObjectListView gettingstarted code (GettingStartedTree project ) referred to in the Getting Started section of the ObjectListView sourceforge online docs.
My goal is to add checkboxes to the Title column of the TreeListView in the GettingStartedTree project.
I was able to add checkboxes simply by setting treeListView1.CheckBoxes to true and setting treeListView1.CheckedAspectName to Title (see After making changes below) as described in the instructions. Problem: However, when I run the program and click on a checkbox, a checkmark does not appear in the checkbox. I expect that a user should be able to "check" a checkbox on the UI.
Note: If I leave treeListView1.CheckBoxes set to true and set treeListView1.CheckedAspectName to null, then a checkmark does appear in the checkbox.
Am I configuring the TreeListView correctly?
Before making any changes
treeListView1:
CheckBoxes = false
CheckedAspectName = null
OLVColumn Collection, olvColumn1(Title):
Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)
After making changes
treeListView1:
CheckBoxes = true
CheckedAspectName = Title
OLVColumn Collection, olvColumn1(Title):
Name = olvColumn1
AspectName = Title
CheckBoxes = false (Title column)
HierarchicalCheckboxes doesn't work with CheckedAspectName (or CheckedAspectGetter), although finding the documentation on this isn't easy.
Some additional information here:
Hierarchy-aware checkboxes
Just to make this clear...
To enable checkboxes for the TreeListView (or any other version of ObjectListView), just set CheckBoxes to true on the control itself.
That's all you need to do.
If you have a property on your model that indicates whether a row should be checked or not, you can automatically hook that value onto the checkboxes by setting CheckedAspectName to the name of that property. For example, if your model has an IsSelected property, then this will hook the checkbox to that property:
treeListView1.CheckedAspectName = "IsSelected";
The type of that property must be bool (or bool? if you have a tri-state check box). So, in the question, setting the CheckedAspectName to "Title" cannot work because Title is not a boolean.
Don't change any value in any column to get checkboxes to work.
If you are using hierarchical checkboxes, you can't use CheckedAspectName or anything else that eventually installs a CheckStateGetter. The docs on the Hierarchy-aware checkboxes page that Patrick mentioned explain why.
Update
After posting this answer, I came across a TreeViewList property
named HierarchicalCheckboxes that I hadn't noticed before and that I
don't see discussed/described anywhere in the ObjectListView
sourceforge docs. I now believe this is what Grammarian was referring
to in his comment under Patricks answer. In my code (in this answer),
this property is set false, so I assume I'm NOT using
HierarchicalCheckboxes. I thought hierarchical checkboxes were checkboxes in a model that has a hierarchical structure like the model in my code in this answer.
I guess/assume HierarchicalCheckboxes is
associated with the write-up on Hierarchy-aware checkboxes ,
although I'm not sure. ObjectListView is a great control. I just wish the ObjectListView sourceforge docs did a better job differentiating the explaining the collection of lists required to draw the treeview portion of a TreeListView along with adding and managing checkboxes to such a model both with with and without the use of the hierarchical checkboxes feature (enabled by the property HierarchicalCheckboxes). I guess all the pieces are in the docs, but its spread out and a bit disjointed.
End of Update
For the sake of clarity I was kind of forced to answer this myself.....
Reminder: all this is based on the gettingstartedcode described in the original question.
First, the answer to my initial problem was to clear treeview1.CheckedAspectName as in the original example code. Doing so resulted in a checkmark appearing in the checkbox (as expected) when the checkbox is clicked.
Grammarian kindly described how to programmatically check/uncheck a checkbox for (I assume) a non-hierarchical checkbox scenario, i.e., add a bool? or bool property to the model and add the property's name to the TreeListView's CheckedAspectName property.
I came to the conclusion that this won't work for hierarchical checkboxes because Grammarian said the following and loading the bool? or bool property's name into CheckedAspectName is required based on his explanation.
If you are using hierarchical checkboxes, you can't use
CheckedAspectName or anything else that eventually installs a
CheckStateGetter
So, ignoring the advice that setting the CheckedAspectName property for hierarchical checkboxes won't work, I implemented a bool? isChecked property at all levels in my hierarchical model. It works fine, i.e., I can now programmatically check a checkbox at any level in the hierarchy via the model.
Code for a simple example follows (I need to write the code to set the tri-state checkbox for parent rows based on the checkedness or uncheckedness of child rows.). The column "# To Keep" is designed to be relevant only for the level-2 items in the hierarchy
The form has only a FooTreeListView : BrightIdeasSoftware.TreeListView control on it
FooTreeListView.cs
using System;
using System.Collections.Generic;
namespace ObjectListView_TreeListView
{
class FooTreeListView : BrightIdeasSoftware.TreeListView
{
private List<Categories> categoriesList;
private readonly string[] categoryDescriptors = { "Cat A", "Cat B", "Cat C", "Cat D" };
internal List<Categories> CategoriesList { get => categoriesList; set => categoriesList = value; }
public enum CategoryEnum
{
CategoryA = 0,
CategoryB = 1,
CategoryC = 2,
CategoryD = 3
}
public FooTreeListView() : base()
{
CategoriesList = new List<Categories>();
CategoriesList.Clear();
CanExpandGetter = delegate (Object x)
{
if (x is Categories && ((Categories)x).ItemList.Count > 0)
{
return true;
}
if (x is Categories.Item && ((Categories.Item)x).ActionList.Count > 0)
{return true;
}
return false;
};
ChildrenGetter = delegate (Object x)
{
if (x is Categories)
return ((Categories)x).ItemList;
if (x is Categories.Item)
return ((Categories.Item)x).ActionList;
throw new ArgumentException("Should be Categories or Categories.Item");
};
//Load the 4 top-level categories into the tree
CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryA],false));
CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryB], false));
CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryC], false));
CategoriesList.Add(new Categories(categoryDescriptors[(int)CategoryEnum.CategoryD], false));
}
internal class Categories
{
private string action;
private bool? isChecked;
public string Action { get { return action; } set { action = value; } }
public bool? IsChecked { get => isChecked; set => isChecked = value; }
private List<Item> itemList;
internal List<Item> ItemList { get => itemList; set => itemList = value; }
public Categories(string action, bool? isChecked)
{
this.action = action;
this.isChecked = isChecked;
ItemList = new List<Item>();
}
internal class Item
{
private string action;
private bool? isChecked;
private int numberToKeep = 0;
private List<ItemAction> actionList;
public string Action { get { return action; } set { action = value; } }
public int NumberToKeep { get => numberToKeep; set => numberToKeep = value; }
public bool? IsChecked { get => isChecked; set => isChecked = value; }
internal List<ItemAction> ActionList { get => actionList; set => actionList = value; }
internal Item(string action, bool? isChecked, int numberToKeep)
{
this.action = action;
this.isChecked = isChecked;
this.NumberToKeep = numberToKeep;
ActionList = new List<ItemAction>();
}
internal class ItemAction
{
private string action;
private bool? isChecked;
public string Action { get { return action; } set { action = value; } }
public bool? IsChecked { get { return isChecked; } set { isChecked = value; } }
internal ItemAction(string action, bool? isChecked)
{
this.action = action;
this.isChecked = isChecked;
}
}
}
}
public void AddCategoryItemName(CategoryEnum category, string itemName, bool? isChecked, int numberToKeep)
{
CategoriesList[(int)category].ItemList.Add(new Categories.Item(itemName, isChecked, numberToKeep));
}
public void AddItemAction(CategoryEnum category, string itemName, string action, Boolean isChecked)
{
Categories.Item itemMatch = CategoriesList[(int)category].ItemList.Find(x => x.Action.Equals(itemName));
if (itemMatch != null)
{
itemMatch.ActionList.Add(new Categories.Item.ItemAction(action, isChecked));
}
else
{
throw new ArgumentException(String.Format("Can't find treeviewlist item '{0}'->'{1}'", categoryDescriptors[(int)category], itemName));
}
}
public void AddItemAction(CategoryEnum category, string itemName, string action)
{
Categories.Item itemMatch = CategoriesList[(int)category].ItemList.Find(x => x.Action.Equals(itemName));
if (itemMatch != null)
{
itemMatch.ActionList.Add(new Categories.Item.ItemAction(action, false));
}
else
{
throw new ArgumentException(String.Format("Can't find treeviewlist item '{0}'->'{1}'", categoryDescriptors[(int)category], itemName));
}
}
public void LoadTree()
{
Roots = CategoriesList;
ExpandAll();
}
}
}
Form1.cs
using System.Windows.Forms;
using static ObjectListView_TreeListView.FooTreeListView;
namespace ObjectListView_TreeListView
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SuspendLayout();
xenSnapshotsTreeListView1.AddCategoryItemName(CategoryEnum.CategoryA, "Item A", true, 0);
xenSnapshotsTreeListView1.AddCategoryItemName(CategoryEnum.CategoryA, "Item B", false, 1);
xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item A", "Item A foo", true);
xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item A", "Item A bar", false);
xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item B", "Item B foo");
xenSnapshotsTreeListView1.AddItemAction(CategoryEnum.CategoryA, "Item B", "Item B bar", true);
xenSnapshotsTreeListView1.LoadTree();
ResumeLayout();
}
}
}
Form1.Designer.cs
namespace ObjectListView_TreeListView
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.xenSnapshotsTreeListView1 = new ObjectListView_TreeListView.FooTreeListView();
this.olvColumnAction = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
this.olvColumnNumbSsToKeep = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
((System.ComponentModel.ISupportInitialize)(this.xenSnapshotsTreeListView1)).BeginInit();
this.SuspendLayout();
//
// xenSnapshotsTreeListView1
//
this.xenSnapshotsTreeListView1.AllColumns.Add(this.olvColumnAction);
this.xenSnapshotsTreeListView1.AllColumns.Add(this.olvColumnNumbSsToKeep);
this.xenSnapshotsTreeListView1.CellEditUseWholeCell = false;
this.xenSnapshotsTreeListView1.CheckBoxes = true;
this.xenSnapshotsTreeListView1.CheckedAspectName = "IsChecked";
this.xenSnapshotsTreeListView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.olvColumnAction,
this.olvColumnNumbSsToKeep});
this.xenSnapshotsTreeListView1.Cursor = System.Windows.Forms.Cursors.Default;
this.xenSnapshotsTreeListView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.xenSnapshotsTreeListView1.GridLines = true;
this.xenSnapshotsTreeListView1.Location = new System.Drawing.Point(0, 0);
this.xenSnapshotsTreeListView1.MultiSelect = false;
this.xenSnapshotsTreeListView1.Name = "xenSnapshotsTreeListView1";
this.xenSnapshotsTreeListView1.ShowGroups = false;
this.xenSnapshotsTreeListView1.ShowImagesOnSubItems = true;
this.xenSnapshotsTreeListView1.Size = new System.Drawing.Size(800, 450);
this.xenSnapshotsTreeListView1.TabIndex = 0;
this.xenSnapshotsTreeListView1.UseAlternatingBackColors = true;
this.xenSnapshotsTreeListView1.UseCompatibleStateImageBehavior = false;
this.xenSnapshotsTreeListView1.View = System.Windows.Forms.View.Details;
this.xenSnapshotsTreeListView1.VirtualMode = true;
//
// olvColumnAction
//
this.olvColumnAction.AspectName = "Action";
this.olvColumnAction.Text = "Action";
this.olvColumnAction.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.olvColumnAction.Width = 200;
//
// olvColumnNumbSsToKeep
//
this.olvColumnNumbSsToKeep.AspectName = "NumberToKeep";
this.olvColumnNumbSsToKeep.Text = "# To Keep";
this.olvColumnNumbSsToKeep.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.olvColumnNumbSsToKeep.Width = 65;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.xenSnapshotsTreeListView1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.xenSnapshotsTreeListView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private FooTreeListView xenSnapshotsTreeListView1;
private BrightIdeasSoftware.OLVColumn olvColumnAction;
private BrightIdeasSoftware.OLVColumn olvColumnNumbSsToKeep;
}
}

How to programmatically create WPF scrollable StackPanel list in C#

My Situation:
I'm developing a C# WPF Application (on Windows) where I need to dynamically create a lot of my controls at runtime. Because of the nature of the application, I'm not able to use standard XAML (with Templates) for many aspects of my WPF windows. This is a very unique case, and no, I'm not going to reconsider the format of my application.
What I want to accomplish:
I would like to programmatically create a control that displays a scrollable list of StackPanels (or any other effecient control group) which, for one use case, will each consist of an Image control (picture) on top of a TextBlock control (title/caption):
I would prefer to do all this without any data bindings (See below for reasoning). Because the items are being defined at runtime, I should be able to do this without them via iteration.
The control/viewer should be able to have multiple columns/rows, so it's not one dimensional (like a typical ListBox control).
It should also interchangeable so that you can modify (add, remove, etc.) the items in the control.
I've included a picture (below) to give you an example of a possible use case.
In the past, I have been able to accomplish all this by using a ListView with an ItemTemplate (wrapped in a ScrollViewer) using XAML. However, doing this entirely with C# code makes it a bit more difficult. I've recently made ControlTemplates in plain c# code (with FrameworkElementFactorys. It can get a bit complicated, and I'm not sure it's really the best practice. Should I try to go the same route (using a ListView with a template)? If so, how? Or is there a simpler, more elegant option to implement with C# code?
Edit: I would really prefer not to use any data bindings. I just want to create a (scrollable) 'list' of StackPanels that I can easily modify/tweak. Using data bindings feels like a backwards implementation and defeats the purpose of the dynamic nature of runtime.
Edit 2 (1/25/2018): Not much response. I simply need a uniform, scrollable list of stackpanels. I can tweak it to suit my needs, but it needs to be all in C# (code-behind). If anyone needs more information/clarification, please let me know. Thanks.
LINK TO XAML POST
Here's a way to do it in code using a ListBox with UniformGrid as ItemsPanelTemplate. Alternatively, you can only use a UniformGrid and put it inside a ScrollViewer, but as the ListBox already handles selection and all that stuff, you probably better stick with that one. This code will automatically adjust the number of items in a row depending on the available width.
MoviePresenter.cs :
public class MoviePresenter : ListBox
{
public MoviePresenter()
{
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(UniformGrid));
factory.SetBinding(
UniformGrid.ColumnsProperty,
new Binding(nameof(ActualWidth))
{
Source = this,
Mode = BindingMode.OneWay,
Converter = new WidthToColumnsConverter()
{
ItemMinWidth = 100
}
});
ItemsPanel = new ItemsPanelTemplate()
{
VisualTree = factory
};
}
}
internal class WidthToColumnsConverter : IValueConverter
{
public double ItemMinWidth { get; set; } = 1;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double? actualWidth = value as double?;
if (!actualWidth.HasValue)
return Binding.DoNothing;
return Math.Max(1, Math.Floor(actualWidth.Value / ItemMinWidth));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
MovieItem.cs :
public class MovieItem : Grid
{
public MovieItem()
{
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
Image image = new Image();
image.Stretch = Stretch.UniformToFill;
image.SetBinding(Image.SourceProperty, new Binding(nameof(ImageSource)) { Source = this });
Children.Add(image);
TextBlock title = new TextBlock();
title.FontSize += 1;
title.FontWeight = FontWeights.Bold;
title.Foreground = Brushes.Beige;
title.TextTrimming = TextTrimming.CharacterEllipsis;
title.SetBinding(TextBlock.TextProperty, new Binding(nameof(Title)) { Source = this });
Grid.SetRow(title, 1);
Children.Add(title);
TextBlock year = new TextBlock();
year.Foreground = Brushes.LightGray;
year.TextTrimming = TextTrimming.CharacterEllipsis;
year.SetBinding(TextBlock.TextProperty, new Binding(nameof(Year)) { Source = this });
Grid.SetRow(year, 2);
Children.Add(year);
TextBlock releaseDate = new TextBlock();
releaseDate.Foreground = Brushes.LightGray;
releaseDate.TextTrimming = TextTrimming.CharacterEllipsis;
releaseDate.SetBinding(TextBlock.TextProperty, new Binding(nameof(ReleaseDate)) { Source = this });
Grid.SetRow(releaseDate, 3);
Children.Add(releaseDate);
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty YearProperty =
DependencyProperty.Register("Year", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public static readonly DependencyProperty ReleaseDateProperty =
DependencyProperty.Register("ReleaseDate", typeof(string), typeof(MovieItem), new PropertyMetadata(null));
public string ImageSource
{
get { return (string)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public string Year
{
get { return (string)GetValue(YearProperty); }
set { SetValue(YearProperty, value); }
}
public string ReleaseDate
{
get { return (string)GetValue(ReleaseDateProperty); }
set { SetValue(ReleaseDateProperty, value); }
}
}
MainWindow.xaml :
<Grid>
<local:MoviePresenter x:Name="moviePresenter"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 20; i++)
{
DateTime dummyDate = DateTime.Now.AddMonths(-i).AddDays(-(i * i));
MovieItem item = new MovieItem()
{
ImageSource = $"http://fakeimg.pl/100x200/?text=Image_{i}",
Title = $"Dummy movie {i}",
Year = $"{dummyDate.Year}",
ReleaseDate = $"{dummyDate.ToLongDateString()}"
};
moviePresenter.Items.Add(item);
}
}
}

How can i serialize a WPF custom control which has a property of type ObservableCollection?

I'm programming a WPF-window-designer application.
In the designer i can add customcontrols to the window and save the window by serializing the Canvas panel on which the added customcontrols are lying to XAML.
public string SerializeControlToXaml(FrameworkElement control)
{
StringBuilder outstr = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
XamlDesignerSerializationManager dsm =
new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings));
dsm.XamlWriterMode = XamlWriterMode.Expression;
System.Windows.Markup.XamlWriter.Save(control, dsm);
string xaml = outstr.ToString();
return xaml;
}
After that serialization i get the xaml as string and save it in a file which i can reload later.
Now my problem is that i added a new dependency property to one of my customcontrols which is of type ObservableCollection.
Each time i have set that property and try to serialize the canvas to XAML i get an error message:
"Invalid initials in 'ObservableCollection 1'. The sign '', hexadecimal value 0x60, may not be contained in a name."
I never give the collection a name.
What's going wrong?
This is the viewmodel-property to which i bind the control:
public ObservableCollection<string> SelectedFormulas
{
get
{
return selectedFormulas;
}
set
{
selectedFormulas = value;
RaisePropertyChanged("SelectedFormulas");
}
}
And this is my dependencyproperty:
public static readonly DependencyProperty SelectedFormulasProperty =
DependencyProperty.Register("SelectedFormulas", typeof(ObservableCollection<string>), typeof(CustomNumericField));
public ObservableCollection<string> SelectedFormulas
{
get { return GetValue(SelectedFormulasProperty) as ObservableCollection<string>; }
set { SetValue(SelectedFormulasProperty, value); }
}
The answer from dbc is the solution.
What i needed was a non-generic class instead of the ObservableCollection.
This is the new class:
public class SelectedFormulaCollection : ObservableCollection<string>
{
}
And here the DependencyProperty in the customcontrol (Type of the property in the viewmodel must be changed too!):
public static readonly DependencyProperty SelectedFormulasProperty =
DependencyProperty.Register("SelectedFormulas", typeof(SelectedFormulaCollection), typeof(CustomNumericField));
public SelectedFormulaCollection SelectedFormulas
{
get { return GetValue(SelectedFormulasProperty) as SelectedFormulaCollection; }
set { SetValue(SelectedFormulasProperty, value); }
}

Get Dependency Property Value in UserControl Code Behind

I'm new to WPF. I'm facing the following issue. I have a User Control, Here is the Code of User Control
CustomControl Code
public partial class CustomCanvas : UserControl
{
public CustomCanvas()
{
InitializeComponent();
DrawCanvas();
}
private void DrawCanvas()
{
//TODO:
//Get the Dictionary Value from Parent Bound Property
}
public Dictionary<string, List<Shapes>> ShapesData
{
get { return (Dictionary<string, List<Shapes>>)GetValue(ShapesDataProperty); }
set { SetValue(ShapesDataProperty, value); }
}
public static readonly DependencyProperty ShapesDataProperty =
DependencyProperty.Register("ShapesData", typeof(Dictionary<string, List<Shapes>>), typeof(CustomCanvas), new PropertyMetadata(ShapesDataChanged));
private static void ShapesDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var value = e.NewValue;
}
}
Main Window Code
<Grid>
<uc:CustomCanvas ShapesData="{Binding ShData}" ></uc:CustomCanvas>
</Grid>
Value of ShapesData is bounded to following code.
ShData = new Dictionary<string, List<Shapes>>()
{
{"Week 1", new List<Shapes>() {Shapes.Circle, Shapes.Rectangle}},
{"Week 2", new List<Shapes>() {Shapes.Circle}},
{"Week 3", new List<Shapes>() {Shapes.Rectangle}}
};
Now My question is that in CustomControl DrawCanvas Method i want to fetch the bounded value in parent. Could any one guide me regarding that.
P.S: I know how to bound this value in child using relative RelativeSource and Mode as FindAncestor. Here i want to just fetch the value and process that Dictionary data. In ShapesDataChanged i can easily access the data but the issue is in fetching it in DrawCanvas Function.
Thanks in advance.
You can use DependencyObject's GetValue() method.
var theValueYouNeeded = CustomCanvas.GetValue(ShapesDataProperty);
Dictionary<string, List<Shapes>> value = (Dictionary<string, List<Shapes>>)theValueYouNeeded;
....

Categories