WPF custom composite user controls - c#

First of all, I am a WPF beginner! My approach is potentially not the right way to do what I want so do not hesitate to tell me if that is the case. What I want to do is a composite user control in WPF, using MVVM.
Some classes will do a better presentation than I, here are my view models:
interface IParameter : INotifyPropertyChanged
{
string Name { get; set;}
string Value { get; set;}
}
class TextParameter : ViewModelBase, IParameter
{
private string _value;
public string Name { get; private set; }
public string Value
{
get { return _value; }
set
{
_value = value;
RaisePropertyChanged();
}
}
public TextParameter (string name)
{
this.Name = name;
}
}
class ParameterList : ViewModelBase, IParameter
{
private string _value;
public string Name { get; private set; }
public string Value
{
get { return _value; }
set
{
_value = value;
RaisePropertyChanged();
}
}
ObservableCollection<IParameter> Parameters { get; set; }
public ParameterList (string name, IEnumerable<IParameter> parameters = null)
{
this.Name = name;
this.Parameters = new ObservableCollection<IParameter>(parameters ?? new List<IParameter>());
}
}
I am using MVVM Light, so all the PropertyChanged stuff is managed into ViewModelBase. Also, this is not an exhaustive list of all the parameters, there is some others, more complex but the issue is about these ones.
Here are my custom user controls:
TextParameterControl.xaml:
<UserControl x:Class="Stuff.TextParameterControl" [..] x:Name="parent">
<StackPanel DataContext="{Binding ElementName=parent}" Orientation="Horizontal">
<TextBlock Text="{Binding Path=ParamName, StringFormat='{}{0}:'}" Width="100"></TextBlock>
<TextBox Text="{Binding Path=Value}" Width="100"></TextBox>
</StackPanel>
</UserControl>
TextParameterControl.xaml.cs :
public class TextParameterControl : UserControl
{
#region param name
public string ParamName
{
get { return (string)GetValue(ParamNameProperty); }
set { SetValue(ParamNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParamNameProperty =
DependencyProperty.Register("ParamName", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty));
#endregion
#region value
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty));
#endregion
public TextParameterControl()
{
InitializeComponent();
}
}
ParameterListControl.xaml:
<UserControl x:Class="Stuff.ParameterListControl" [..] x:Name="parent">
<UserControl.Resources>
<DataTemplate x:Key="TextParameterTemplate">
<c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="ParameterListTemplate">
<c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" />
</DataTemplate>
<s:ParameterTemplateSelector x:Key="ParameterSelector"
TextParameterTemplate="{StaticResource TextParameterTemplate}"
ParameterListTemplate="{StaticResource ParameterListTemplate}"/>
</UserControl.Resources>
<Expander DataContext="{Binding ElementName=parent}" Header="{Binding Path=ParamName}" IsExpanded="True" ExpandDirection="Down">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=Items}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl>
</StackPanel>
</Expander>
</UserControl>
ParameterListControl.xaml.cs:
public partial class ParameterListControl: UserControl
{
#region param name
public string ParamName
{
get { return (string)GetValue(ParamNameProperty); }
set { SetValue(ParamNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParamNameProperty =
DependencyProperty.Register("ParamName", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty));
#endregion
#region value
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty));
#endregion
#region items
public IList<string> Items
{
get { return (List<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IList<string>), typeof(ParameterListControl), new PropertyMetadata(new List<string>()));
#endregion
public ParameterListControl()
{
InitializeComponent();
}
}
Here is my custom template selector:
class ParameterTemplateSelector : DataTemplateSelector
{
public DataTemplate ParameterListTemplate { get; set; }
public DataTemplate TextParameterTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is TextParameter)
{
return this.TextParameterTemplate;
}
else if (item is ParameterList)
{
return this.ParameterListTemplate;
}
throw new Exception(String.Format("This parameter ({0}) is not handled in the application", item.GetType().Name));
}
}
And here is the calling View and ViewModel:
ViewModel:
public class MainViewModel : ViewModelBase
{
public ObservableCollection<IParameter> Parameters { get; set; }
public MainViewModel()
{
this.Parameters = new ObservableCollection<IParameter>();
this.Parameters.Add(new TextParameter("Customer"));
// here I am building my complex composite parameter list
}
View:
<UserControl.Resources>
<DataTemplate x:Key="TextParameterTemplate">
<c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="ParameterListTemplate">
<c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" />
</DataTemplate>
<s:ParameterTemplateSelector x:Key="ParameterSelector"
TextParameterTemplate="{StaticResource TextParameterTemplate}"
ParameterListTemplate="{StaticResource ParameterListTemplate}"/>
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding Parameters}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl>
When I run the application, the TextParameter in the MainViewModel.Parameters are well loaded (VM.Name and VM.Value properties are well binded to UC.ParamName and UC.Value. Contrariwise, the ParameterList in MainViewModel.Parameters are partially loaded. UC.Name is well binded to the UC.ParamName but the VM.Parameters is not binded to the UC.Items (the UC.DataContext is the VM, the VM.Parameters is well defined, but the UC.Items is desperately null).
Do you have any idea of what I am missing ?
(I am not a native speaker, excuse me if my english hurts you)

I see you have a binding MainViewModel.Parameters -> ParameterListControl.Items but you might be missing the binding from ParameterListControl.Items -> ParameterList.Parameters. (That's assuming ParameterList is the ViewModel for the ParameterListControl - you provide the code for DataContext bindings.)
See the accepted answer on this question. (Ignore the comment on Caliburn.Micro - same solution worked for me in MVVM Light.)
Essentially, in the constructor of ParameterListControl you create an extra binding between the dependency property of the view and the viewmodel's property.
(Also, Dbl is right in the comments that when debugging binding problems, the "unimportant" "plumbing" code that you omitted is very important.)

I finally find out:
The Items dependency property of ParameterListControl was a IList<string>. It was a copy/paste mistake from another UC. I changed it to IEnumerable and everything works fine now:
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ParameterListControl), new PropertyMetadata(new List<object>()));
I continued to work on the code and it is now finished and truly composite compared to the sample I posted earlier. If someone is interested in seeing/using this code, you can find it on github.

Related

Unable to bind properties using custom controls in UWP in a ListView

I have a ListView in UWP which displays a list of custom controls CustomControl. Reading around I have seen that other users have face similar issues and their solution mostly revolved around setting the DataContext of their controls, but I cannot understand how I can do that in my example. In order to dynamically update the view I used DependencyProperties in my model which is the following:
public class DataObject : DependencyObject
{
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
// Using a DependencyProperty as the backing store for name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty nameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DataObject), new PropertyMetadata("Name"));
}
Then in my main page I implemented the following logic to change the Name of my third element:
public sealed partial class MainPage : Page
{
private readonly ObservableCollection<DataObject> dataList;
public MainPage()
{
this.InitializeComponent();
this.dataList = new ObservableCollection<DataObject>();
for (int i = 0; i < 5; i++)
{
DataObject dataObject = new DataObject();
dataObject.Name = "Item " + i.ToString();
this.dataList.Add(dataObject);
}
DataListView.ItemsSource = dataList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var obj = dataList.ElementAt(2);
obj.Name = "Hello!";
}
}
The XAML for the main page is the following:
<Page
x:Class="ListViewTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:ListViewTest.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<controls:CustomControl DisplayName="{Binding Name}"></controls:CustomControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Button" HorizontalAlignment="Center" Height="92" Width="238"
Click="Button_Click"/>
</Grid>
</Page>
The custom control CustomControl is this:
namespace ListViewTest.Controls
{
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set {
SetValue(DisplayNameProperty, value);
DisplayText.Text = value;
}
}
// Using a DependencyProperty as the backing store for DisplayName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register("DisplayName", typeof(string), typeof(CustomControl), new PropertyMetadata("DisplayText"));
public CustomControl()
{
this.InitializeComponent();
}
}
Its structure is very simple:
<Grid>
<Button Name="ClickButton" Content="Button" Margin="171,165,0,0" VerticalAlignment="Top"/>
<TextBlock Name="DisplayText" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Grid>
The problem is that when I click the button nothing happens and I am struggling to understand why.
DataObject shouldn't inherit from DependencyObject. It should be defined as a CLR object that implements INotifyPropertyChanged:
public class DataObject : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, the setter of the CLR wrapper for the dependency property in CustomControls should only set the value of the dependency property. You could set the value of the TextBlock using a PropertyChangedCallback:
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set { SetValue(DisplayNameProperty, value); }
}
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(CustomControl),
new PropertyMetadata("DisplayText", new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl customControl = (CustomControl)d;
d.DisplayText = e.NewValue as string;
}
public CustomControl()
{
this.InitializeComponent();
}
}

XAML: Property on DependecyObject not updating when it is an item in an attached collection

I have a DependencyObject that's inside an attached dependency property (that's a collection). Binding to that object does not work for some reason.
In my example, I am binding two things, a basic attached property (local:CollHolder.BasicProperty) and a regular dependent property (local:MyItem.MyData) - both are bound to the Text of a TextBox control.
The XAML looks like this:
<ListView ItemsSource="{x:Bind Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<StackPanel x:Name="stack" local:CollHolder.BasicProperty="{Binding ElementName=text, Path=Text}" VerticalAlignment="Center" HorizontalAlignment="Center" >
<TextBox Text="" x:Name="text"/>
<local:CollHolder.Coll>
<local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
</local:CollHolder.Coll>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When changes happen the Text property, they propagate to the attached property, but not to the dependency property.
CollHolder:
public class CollHolder : DependencyObject
{
public static readonly DependencyProperty BasicPropertyProperty =
DependencyProperty.RegisterAttached("BasicProperty", typeof(string), typeof(CollHolder), new PropertyMetadata("", DPC));
public static readonly DependencyProperty CollProperty =
DependencyProperty.RegisterAttached("Coll", typeof(Coll), typeof(CollHolder), new PropertyMetadata(null));
public static Coll GetColl(DependencyObject obj)
{
var coll = (Coll)obj.GetValue(CollProperty);
if (coll == null)
{
obj.SetValue(CollProperty, coll = new Coll());
}
return coll;
}
public static void SetColl(DependencyObject obj, Coll value)
{
obj.SetValue(CollProperty, value);
}
public static string GetBasicProperty(DependencyObject obj)
{
return (string)obj.GetValue(BasicPropertyProperty);
}
public static void SetBasicProperty(DependencyObject obj, string value)
{
obj.SetValue(BasicPropertyProperty, value);
}
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Basic Property changed");
}
}
MyItem:
public class MyItem : DependencyObject
{
public string MyData
{
get { return (string)GetValue(MyDataProperty); }
set { SetValue(MyDataProperty, value); }
}
// Using a DependencyProperty as the backing store for MyData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDataProperty =
DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("CHANGED!!");
}
}
And the collection is pretty simple:
public class Coll : List<MyItem>
{
}
When changes happen the Text property, they propagate to the attached property, but not to the dependency property.
<TextBox Text="" x:Name="text"/>
<local:CollHolder.Coll>
<local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
</local:CollHolder.Coll>
The TextBox and local:MyItem in the same DataContext, you could use {x:Bind } to get the text value directly.
<TextBox Text="{x:Bind }" x:Name="text" />
<local:CollHolder.Coll>
<local:MyItem MyData="{x:Bind }" />
</local:CollHolder.Coll>
Update
I tried to replace MyItem with Control to test whether DependencyProperty works in Binding mode. It works as expected. So, you could use Control as MyItem's base class.
public class MyItem : Control
{
public string MyData
{
get { return (string)GetValue(MyDataProperty); }
set { SetValue(MyDataProperty, value); }
}
// Using a DependencyProperty as the backing store for MyData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDataProperty =
DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("CHANGED!!");
}
}

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.

XamlWriter.Save loses ItemsSource binding from a ListBox

I have a custom ContentControl
public class DataControl : ContentControl
{
public List<DataItem> Options
{
get { return (List<DataItem>)GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
public static readonly DependencyProperty OptionsProperty =
DependencyProperty.Register("Options", typeof(List<DataItem>), typeof(DataControl));
public DataControl()
{
Options = new List<DataItem>();
}
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
// Using a DependencyProperty as the backing store for Label. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(DataControl));
}
public class DataItem
{
public DataItem(string key, string value)
{
Key = key;
Value = value;
}
public string Key { get; set; }
public string Value { get; set; }
}
whose template is applied by the following Style:
<Style TargetType="{x:Type local:DataControl}" x:Key="DefaultStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DataControl}">
<StackPanel>
<ListBox ItemsSource="{TemplateBinding Options}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Key}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Label Content="{TemplateBinding Label}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If I use a XamlWriter to Save this style and then read it back again, the ItemsSource binding is lost, but the Content binding on the Label isn't.
Style style = Application.Current.TryFindResource("DefaultStyle") as Style;
string s = XamlWriter.Save(style);
Style secondStyle = XamlReader.Parse(s) as Style;
Is there a way to ensure the ItemsSource binding is serialized correctly or to add it back in easily?
This also occurs when trying to get the Style from a ResourceDictionary from another project, e.g.
ResourceDictionary styles = new ResourceDictionary();
styles.Source = new Uri(String.Format("pack://application:,,,/StyleCopyTest;component/Styles/{0}Styles.xaml", type));
return styles;
In the WPF source code the ItemsSource is defined as
[Bindable(true), CustomCategory("Content"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IEnumerable ItemsSource { get; set; }
So this cannot be serialized by XamlWriter.
So you will have to write your own serializer or use approach mentioned here
I found this class here in code project that helps you Serialize the ItemsControl Property binding:
using System;
using System.Linq;
using System.ComponentModel;
namespace GUIKonfigurator
{
using System.Windows.Controls;
public class ItemsControlTypeDescriptionProvider:TypeDescriptionProvider
{
private static readonly TypeDescriptionProvider defaultTypeProvider = TypeDescriptor.GetProvider(typeof(ItemsControl));
public ItemsControlTypeDescriptionProvider(): base(defaultTypeProvider)
{
}
public static void Register()
{
TypeDescriptor.AddProvider(new ItemsControlTypeDescriptionProvider(), typeof(ItemsControl));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,object instance)
{
ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
return instance == null ? defaultDescriptor: new ItemsControlCustomTypeDescriptor(defaultDescriptor);
}
}
internal class ItemsControlCustomTypeDescriptor: CustomTypeDescriptor
{
public ItemsControlCustomTypeDescriptor(ICustomTypeDescriptor parent): base(parent)
{
}
public override PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(base.GetProperties().Cast<PropertyDescriptor>().ToArray());
return ConvertPropertys(pdc);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>().ToArray());
return ConvertPropertys(pdc);
}
private PropertyDescriptorCollection ConvertPropertys(PropertyDescriptorCollection pdc)
{
PropertyDescriptor pd = pdc.Find("ItemsSource", false);
if (pd != null)
{
PropertyDescriptor pdNew = TypeDescriptor.CreateProperty(typeof(ItemsControl), pd, new Attribute[]
{
new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible),
new DefaultValueAttribute("")
});
pdc.Add(pdNew);
pdc.Remove(pd);
}
return pdc;
}
}
}
You just need to register it like this after registering the BindingConvertor:
EditorHelper.Register<BindingExpression, BindingConvertor>();
ItemsControlTypeDescriptionProvider.Register();
Here a quick test I did, creating a ComboBox and Serializing it:
ComboBox cb = new ComboBox();
cb.Width = 100;
cb.Height = 20;
Binding b = new Binding("Model.Activity");
b.Source = this.DataContext;
cb.SetBinding(ComboBox.ItemsSourceProperty, b);
string xaml = _Serializer.SerializeControlToXaml(cb);
And here the resulting Xaml Including the ItemsSource binding:
<ComboBox Width="100" Height="20" ItemsSource="{Binding Path=Model.Activity}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
Hope this helps, I still need to take some time to understand it but so far seems to be working...

WPF Control's Nested property's data binding

I'm trying to develop user control with some nested properties that allows to use databinding to set it. For example, I have something like this:
// Top level control
public class MyControl : Control
{
public string TopLevelTestProperty
{
get { return (string)GetValue(TopLevelTestPropertyProperty); }
set { SetValue(TopLevelTestPropertyProperty, value); }
}
public static readonly DependencyProperty TopLevelTestPropertyProperty =
DependencyProperty.Register("TopLevelTestProperty", typeof(string), typeof
(MyControl), new UIPropertyMetadata(""));
// This property contains nested object
public MyNestedType NestedObject
{
get { return (MyNestedType)GetValue(NestedObjectProperty); }
set { SetValue(NestedObjectProperty, value); }
}
public static readonly DependencyProperty NestedObjectProperty =
DependencyProperty.Register("NestedObject", typeof(MyNestedType), typeof
(MyControl), new UIPropertyMetadata(null));
}
// Nested object's type
public class MyNestedType : DependencyObject
{
public string NestedTestProperty
{
get { return (string)GetValue(NestedTestPropertyProperty); }
set { SetValue(NestedTestPropertyProperty, value); }
}
public static readonly DependencyProperty NestedTestPropertyProperty =
DependencyProperty.Register("NestedTestProperty", typeof(string), typeof
(MyNestedType), new UIPropertyMetadata(""));
}
// Sample data context
public class TestDataContext
{
public string Value
{
get
{
return "TEST VALUE!!!";
}
}
}
...
this.DataContext = new TestDataContext();
...
XAML:
<local:mycontrol x:name="myControl" topleveltestproperty="{Binding Value}" >
<local:mycontrol.nestedobject>
<local:mynestedtype x:name="myNestedControl" nestedtestproperty="{Binding Value}" />
</local:mycontrol.nestedobject>
</local:mycontrol>
It works well for property TopLevelTestProperty, but it doesn't work for NestedTestProperty.
It seems that nested bindings do not work. Can anybody help me please? Is there any way to make such binding?
I think that it happens because of my nested object has no any reference to the top level object, so it cannot be resolved using MyControl's DataContext.
H.B. right, nested control does not inherit DataContext from mycontrol.
Tyr out setting it explicitly:
<local:mycontrol x:name="myControl"
topleveltestproperty="{Binding Value}" >
<local:mycontrol.nestedobject>
<local:mynestedtype x:name="myNestedControl"
DataContext="{Binding ElementName=myControl,
Path=DataContext}"
nestedtestproperty="{Binding Value}" />
</local:mycontrol.nestedobject>
</local:mycontrol>

Categories

Resources