In the code below, I have a UserControl that contains an ellipse and a textblock. I'd like to create a reusable control that I can bind to that allows me to set the text based on a string, and changes the fill color between Red/Green based on a boolean.
I can do this now by digging deep into the markup and using some complex binding, but I want to reuse this control in a list and it seemed easier to create a control for the purpose. However, I am not sure where to go next and if I should be creating dependency-properties that tie to the values for Fill and Text, or what.
<UserControl
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"
mc:Ignorable="d"
x:Class="Herp.Derp.View.DeliveryStatusIndicator"
x:Name="UserControl"
d:DesignWidth="91" d:DesignHeight="35">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<Ellipse Width="35" Height="35" Fill="Green">
<Ellipse.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_location_circle}"/>
</Ellipse.OpacityMask>
</Ellipse>
<TextBlock Style="{StaticResource Heading2}"
VerticalAlignment="Center" Margin="3,0,0,0">
<Run Text="FRONT"/>
</TextBlock>
</StackPanel>
</Grid>
</UserControl>
here how you can achieve this
UserControl
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty FrontTextProperty = DependencyProperty.Register( "FrontText", typeof(string),typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string FrontText
{
get { return (string)GetValue(FrontTextProperty); }
set {
SetValue(FrontTextProperty, value);
frontBlock.Text = value;
}
}
public static readonly DependencyProperty EllipseStateProperty = DependencyProperty.Register("EllipseState", typeof(bool), typeof(UserControl1), new FrameworkPropertyMetadata(false));
public bool EllipseState
{
get { return (bool)GetValue(EllipseStateProperty); }
set
{
SetValue(EllipseStateProperty, value);
if (value)
{
ellipse.Fill = new SolidColorBrush( Colors.Green);
}
else
{
ellipse.Fill = new SolidColorBrush(Colors.Red);
}
}
}
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 EllipseState="{Binding yourProperty }"/>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="207,94,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
Yes, to create properties that "parent" XAML can assign bindings to, you need to create aDependencyProperty for each field that you want to bind to.
You would then bind your user control xaml to the backing property for the DP.
Here's what I got for a working solution:
public partial class DeliveryStatusIndicator : UserControl
{
public DeliveryStatusIndicator()
{
InitializeComponent();
}
public static readonly DependencyProperty DeliveryDescriptionProperty = DependencyProperty.Register("DeliveryDescription", typeof(string), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata("Default", DescriptionChangedEventHandler));
private static void DescriptionChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Desc.Text = (string)e.NewValue;
}
public string DeliveryDescription
{
get { return (string)GetValue(DeliveryDescriptionProperty); }
set
{
SetValue(DeliveryDescriptionProperty, value);
}
}
public static readonly DependencyProperty DeliveryStatusProperty = DependencyProperty.Register("DeliveryStatus", typeof(bool), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata(false, StatusChangedEventHandler));
private static void StatusChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Indicator.Fill = (bool)e.NewValue ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
}
public bool DeliveryStatus
{
get { return (bool)GetValue(DeliveryStatusProperty); }
set
{
SetValue(DeliveryStatusProperty, value);
}
}
}
Related
I have a user control that that consist of
a. A textblock whose content is bound to UserLabel
b. A textbox whose content is bound to UserValue.
When I add this user control to the Main Window, I want to add a subscript in the UserLabel, but do not know how to do that.
I want to do something like this (THE FOLLOWING CODE DOES NOT WORK. IT IS WHAT I WANT TO DO):
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:MyNamespace"
Title="MyControl Sample"
Height="300"
Width="300">
<StackPanel>
<local:MyControl>
<UserLabel.Text>
Subscript<Run BaselineAlignment="Subscript" FontSize="12pt">This</Run>
<UserLabel.Text>
<local:MyControl>
</StackPanel>
</Window>
How can I achieve something like that?
Here is the XAML:
<UserControl x:Class="TEST.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="parent">
<StackPanel Width="100" Orientation="Horizontal"
DataContext="{Binding ElementName=parent}">
<!-- User Label -->
<TextBlock Width="200" Name="UserLabel"
Text="{Binding Path=UserLabel}" >
</TextBlock>
<!-- User Input -->
<TextBox Width="100" Name="MetricValue"
Text="{Binding Path=UserValue}"/>
</StackPanel>
</UserControl>
and here is the CODE BEHIND:
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl: UserControl
{
public MyControl()
{
InitializeComponent();
}
#region User Label DP
public static readonly DependencyProperty UserLabelProperty =
DependencyProperty.Register("UserLabel",
typeof(string),
typeof(MyControl),
new PropertyMetadata(""));
public string UserLabel
{
get { return GetValue(UserLabelProperty) as String; }
set { SetValue(UserLabelProperty, value); }
}
#endregion // User Label DP
#region UserValue DP
public static readonly DependencyProperty UserValueProperty =
DependencyProperty.Register("UserValue",
typeof(string),
typeof(MyControl),
new PropertyMetadata(""));
public string UserValue
{
get { return GetValue(UserValueProperty) as String; }
set { SetValue(UserValueProperty, value); }
}
#endregion // UserValue DP
}
Make UserLabel a TextBlock instead of a string that has no concept of Run elements:
#region User Label DP
public static readonly DependencyProperty UserLabelProperty =
DependencyProperty.Register("UserLabel",
typeof(TextBlock),
typeof(MyControl));
public TextBlock UserLabel
{
get { return GetValue(UserLabelProperty) as TextBlock; }
set { SetValue(UserLabelProperty, value); }
}
#endregion // User Label DP
UserControl XAML:
<!-- User Label -->
<ContentControl Width="200" Content="{Binding UserLabel, ElementName=parent}" />
Window XAML:
<StackPanel>
<local:MyControl>
<local:MyControl.UserLabel>
<TextBlock>
<Run FontSize="12pt">Subscript</Run>
<Run BaselineAlignment="Subscript" FontSize="12pt">This</Run>
</TextBlock>
</local:MyControl.UserLabel>
</local:MyControl>
</StackPanel>
I created a Dependency Property in the usercontrol, but however changes in the usercontrol was NOT notified to the Viewmodel
Usercontrol
<UserControl x:Class="DPsample.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="txtName"></TextBox>
</Grid>
UserControl.cs
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
#region SampleProperty
public static readonly DependencyProperty SamplePropertyProperty
= DependencyProperty.Register("SampleProperty",
typeof(string),
typeof(UserControl1),
new PropertyMetadata(OnSamplePropertyChanged));
public string SampleProperty
{
get { return (string)GetValue(SamplePropertyProperty); }
set { SetValue(SamplePropertyProperty, value); }
}
static void OnSamplePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
(obj as UserControl1).OnSamplePropertyChanged(e);
}
private void OnSamplePropertyChanged(DependencyPropertyChangedEventArgs e)
{
string SamplePropertyNewValue = (string)e.NewValue;
txtName.Text = SamplePropertyNewValue;
}
#endregion
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPsample" x:Class="DPsample.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 SampleProperty="{Binding SampleText,Mode=TwoWay}" HorizontalAlignment="Left" Margin="76,89,0,0" VerticalAlignment="Top" Width="99"/>
<Button Content="Show" HorizontalAlignment="Left" Margin="76,125,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
MainWindow.cs
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var item = this.DataContext as MainViewModel;
MessageBox.Show(item.SampleText.ToString());
}
MainViewModel.cs
public class MainViewModel : NotifyViewModelBase
{
public MainViewModel()
{
this.SampleText = "test";
}
private string _sampleText;
public string SampleText
{
get { return _sampleText; }
set { _sampleText = value; OnPropertyChanged("SampleText"); }
}
}
Bind the TextBox.Text property in the UserControl to its SampleProperty like this:
<TextBox Text="{Binding SampleProperty,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Now you could simply remove your OnSamplePropertyChanged callback.
You might also register SampleProperty to bind two-way by default like this:
public static readonly DependencyProperty
SamplePropertyProperty = DependencyProperty.Register(
"SampleProperty", typeof(string), typeof(UserControl1),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
An alternative way to do this is an ElementName Binding. First assign the x:Name attribute to the UserControl (for example x:Name="MyUC"), then change the binding to:
<TextBox Text="{Binding ElementName=MyUC, Path=SampleProperty}"/>
I'm trying to bind a user control property "MyUserControl.Names" to a collection property "Names" of the main window. It doesn't work if I do it in ItemsControl template, but it works if I move the control definition out of the ItemsControl template. Here is the xaml:
<Window x:Class="TestItemsControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestItemsControl"
Height="200" Width="200"
Name="MainControl">
<StackPanel>
<ItemsControl ItemsSource="{Binding Groups, ElementName=MainControl}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- This doesn't work -->
<StackPanel Background="WhiteSmoke" Height="40" Width="100" Margin="5" HorizontalAlignment="Left">
<TextBlock Text="{Binding .}"/>
<local:MyUserControl Names="{Binding Names, ElementName=MainControl}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- This works -->
<StackPanel Background="WhiteSmoke" Height="40" Width="100" Margin="5" HorizontalAlignment="Left">
<TextBlock Text="Group3"/>
<local:MyUserControl Names="{Binding Names, ElementName=MainControl}"/>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs contains two dependency properties:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SetValue(GroupsProperty, new ObservableCollection<string>());
SetValue(NamesProperty, new ObservableCollection<string>());
Groups.Add("Group1");
Groups.Add("Group2");
Names.Add("Name1");
Names.Add("Name2");
}
public static readonly DependencyProperty GroupsProperty =
DependencyProperty.Register("Groups", typeof(ObservableCollection<string>), typeof(MainWindow),
new PropertyMetadata(null));
public ObservableCollection<string> Groups
{
get { return (ObservableCollection<string>)GetValue(GroupsProperty); }
set { SetValue(GroupsProperty, value); }
}
public static readonly DependencyProperty NamesProperty =
DependencyProperty.Register("Names", typeof(ObservableCollection<string>), typeof(MainWindow),
new PropertyMetadata(null));
public ObservableCollection<string> Names
{
get { return (ObservableCollection<string>)GetValue(NamesProperty); }
set { SetValue(NamesProperty, value); }
}
}
Here is the result:
The first two rectangles are what ItemsControl generates. The third one is what I have manually added right after the ItemsControl. As you can see, even though the code is exactly the same in both cases, the first two rectangles don't have names, but the third one has. Is there any reason why wouldn't it work with ItemsControl?
Edit:
Here is the code of the MyUserControl.xaml.cs:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
SetValue(NamesProperty, new ObservableCollection<string>());
}
public static readonly DependencyProperty NamesProperty = DependencyProperty.Register(
"Names", typeof(ObservableCollection<string>), typeof(MyUserControl),
new PropertyMetadata(null, NamesPropertyChanged));
public ObservableCollection<string> Names
{
get { return (ObservableCollection<string>)GetValue(NamesProperty); }
set { SetValue(NamesProperty, value); }
}
private static void NamesPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var control = (MyUserControl)obj;
var oldCollection = e.OldValue as INotifyCollectionChanged;
var newCollection = e.NewValue as INotifyCollectionChanged;
if (oldCollection != null)
oldCollection.CollectionChanged -= control.NamesCollectionChanged;
if (newCollection != null)
newCollection.CollectionChanged += control.NamesCollectionChanged;
control.UpdateNames();
}
private void NamesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateNames();
}
private void UpdateNames()
{
NamesPanel.Children.Clear();
if (Names == null)
return;
foreach(var name in Names)
{
var textBlock = new TextBlock();
textBlock.Text = name + ", ";
NamesPanel.Children.Add(textBlock);
}
}
}
MyUserControl.xaml:
<UserControl x:Class="TestItemsControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestItemsControl"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
Name="ParentControl">
<StackPanel Name="NamesPanel" Orientation="Horizontal"/>
</UserControl>
Replace SetValue in the UserControl's constructor by SetCurrentValue. It may even make sense not to assign an initial value at all for the Names property.
public MyUserControl()
{
InitializeComponent();
SetCurrentValue(NamesProperty, new ObservableCollection<string>());
}
SetValue (as opposed to SetCurrentValue) sets a so-called local value to the Names property. When you assign a Binding as in the second case, this is also considered a local value with the same precedence as the one set in the constructor.
However, in the first case, the Binding is set in a DataTemplate, where it doesn't count as a local value. Since it has lower precedence, it does not replace the initial value.
More details here: Dependency Property Value Precedence
How can I create a custom button control which takes the argument of path in the button constructor so i can reuse the control throughout my projects?
This is how i normally create buttons....
<Grid>
<Button Style="{DynamicResource ButtonPathStyle}" Width="24" Height="24">
<Path Fill="Gray" Data="M50,50 L100,100 L150,50" Stretch="Uniform" />
</Button>
</Grid>
However I would like to create a custom button so i could create them like this along with a custom style sheet
<Grid>
<PathButton Width="24" Height="24"
Data="M50,50 L100,100 L150,50"
Stretch="Uniform" />
</Grid>
If anyone knows of any good resources of can even whip together a quick example it'd be greatly appreciated. I would assume creating the button would be pretty simple as a custom control.
And by custom control i mean compile into a DLL i can share.
Code Behind:
public class PathButton : Button
{
public static DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(Geometry), typeof(PathButton), new FrameworkPropertyMetadata(new PropertyChangedCallback(Data_Changed)));
public Geometry Data
{
get { return (Geometry)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
private static void Data_Changed(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
PathButton thisClass = (PathButton)o;
thisClass.SetData();
}
private void SetData()
{
Path path = new Path();
path.Data = Data;
path.Stroke = this.Foreground;
path.StrokeThickness = 1;
this.Content = path;
}
}
XAML:
<Window x:Class="WpfApplication2.MainWindow"
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:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:PathButton Data="M50,50 L100,100 L150,50" />
</Grid>
Here you go
public class LazyButton : Button
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(Geometry), typeof(LazyButton), new PropertyMetadata(new PropertyChangedCallback(OnDataChanged)));
private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LazyButton button = d as LazyButton;
button.Content = new Path() { Data = e.NewValue as Geometry, Fill = Brushes.Gray, Stretch = Stretch.Uniform };
}
public Geometry Data
{
get { return (Geometry)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
}
This is usable in xaml:
<local:LazyButton Data="M50,50 L100,100 L150,50"></local:LazyButton>
I'm sure you can figure out how to make the Fill and Stretch options settable in xaml as well
I am factoring some code into UserControls which parameters are bound when consumed. I am meeting difficulties with the use of ObservableCollection as a DependencyProperty.
The example showing the difficulty is a project consisting in a MainWindow with two DependencyProperty:
one representing a String (named "Data") and
another one representing an ObservableCollection (named "Origin");
and a UserControl (named UserControl1) exposing two similar DependencyProperty (named resp. "Liste" and "Noun").
The MainWindow contains a TextBlock which Text is bound to "Data" and a ComboBox which ItemsSource is bound to "Origin". Both are working fine.
Both controls are factored into UserControl1, with the DependencyProperty "Liste" and "Noun" acting as intermediate, and UserControl1 is consumed in MainWindow.
Each DataContext (of MainWindow and of UserControl1) is set to "this".
The trouble is while the factored TextBlock (within UserControl1) is working and showing the content of "Data", the factored ComboBox is not working and its DropDown is empty.
The code of MainWindow.xaml is:
<Window x:Class="ChainedBindingUserControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350" Width="525"
xmlns:Local="clr-namespace:ChainedBindingUserControl"
>
<StackPanel>
<TextBlock Text="{Binding Data}"
Width="150"
/>
<ComboBox ItemsSource="{Binding Origin}"
Width="150"
/>
<Label Content="--------------------------------------------------"
Width="200"
/>
<Local:UserControl1 Liste="{Binding Origin}"
Noun="{Binding Data}"
Height="50" Width="150"
/>
</StackPanel>
</Window>
Its code behind is :
namespace ChainedBindingUserControl
{
public partial class MainWindow : Window
{
public ObservableCollection<String> Origin
{
get { return (ObservableCollection<String>)GetValue(OriginProperty); }
set { SetValue(OriginProperty, value); }
}
public static readonly DependencyProperty OriginProperty =
DependencyProperty.Register("Origin", typeof(ObservableCollection<String>), typeof(MainWindow),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public String Data
{
get { return (String)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("Blablabla", FrameworkPropertyMetadataOptions.AffectsRender));
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
ObservableCollection<String> zog = new ObservableCollection<String>();
zog.Add("A");
zog.Add("B");
zog.Add("C");
Origin = zog;
}
}
}
The file UserControl1.xaml is :
<UserControl x:Class="ChainedBindingUserControl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Name="root"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<TextBlock Text="{Binding Noun}"
/>
<ComboBox ItemsSource="{Binding Liste}"
/>
</StackPanel>
</UserControl>
Its code behind is :
namespace ChainedBindingUserControl
{
public partial class UserControl1 : UserControl
{
public ObservableCollection<String> Liste
{
get { return (ObservableCollection<String>)GetValue(ListeProperty); }
set { SetValue(ListeProperty, value); }
}
public static readonly DependencyProperty ListeProperty =
DependencyProperty.Register("Liste", typeof(ObservableCollection<String>), typeof(UserControl1),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public String Noun
{
get { return (String)GetValue(NounProperty); }
set { SetValue(NounProperty, value); }
}
public static readonly DependencyProperty NounProperty =
DependencyProperty.Register("Noun", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));
public UserControl1()
{
InitializeComponent();
this.DataContext = this;
}
}
}
`
EDIT
According to the pieces of information and snippets provided on http://sshumakov.com/2012/11/13/how-to-create-dependency-properties-for-collections/ , I changed the code behind of UserControl1 into
public partial class UserControl1 : UserControl
{
public IList Liste
{
get { return (List<String>)GetValue(ListeProperty); }
set { SetValue(ListeProperty, value); }
}
public static readonly DependencyProperty ListeProperty =
DependencyProperty.Register("Liste", typeof(IList), typeof(UserControl1),
new FrameworkPropertyMetadata(new List<String>(), FrameworkPropertyMetadataOptions.AffectsRender));
public String Noun
{
get { return (String)GetValue(NounProperty); }
set { SetValue(NounProperty, value); }
}
public static readonly DependencyProperty NounProperty =
DependencyProperty.Register("Noun", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));
public UserControl1()
{
InitializeComponent();
this.DataContext = this;
SetValue(ListeProperty, new List<String>());
}
}
but it is still not working.
The trouble doesn't come from the DataContext since the TextBlock works as expected.
The trouble here is specific: why a DependecyProperty acting as an intermediate for Binding is working when the property is of type String while it doesn't work when it is of type ObservableCollection (or List, etc).
Thanks in advance for any explanation.
Your problem is in the UserControl's xaml, here:
<TextBlock Text="{Binding Noun}"
/>
<ComboBox ItemsSource="{Binding Liste}"
/>
These binding expressions are attempting to locate Noun and Liste properties on the DataContext of your UserControl, not on the UserControl itself. You need to specify a different target. Since you've already named your UserControl element, you can replace the bindings with this:
<TextBlock Text="{Binding ElementName=root, Path=Noun}"
/>
<ComboBox ItemsSource="{Binding ElementName=root, Path=Liste}"
/>
Imagine that you are creating control that has property that accepts collection:
public class CustomControl : Control
{
public IEnumerable<string> Items { get; set; }
}
If you want property Items to act as binding target you must change it to be dependency property:
public class CustomControl : Control
{
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IEnumerable<string>), typeof (CustomControl), new PropertyMetadata(new List<string>()));
public IEnumerable<string> Items
{
get { return (IEnumerable<string>) GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
}
As you can see, we changed this property to dependency property and supplied new instance of List class as default parameter. As it turned out, this default value will be used on class level (i.e. it will be created only once and each instance of CustomControl will have reference to the same collection). Therefore, we need one modification:
public class CustomControl : Control
{
public CustomControl()
{
Items = new List<string>();
}
}
Now you can use this control and supply value for Items property via binding:
<Grid>
<DependencyPropertiesCollection:CustomControl Items="{Binding ItemsSource}"/>
</Grid>
Currently this control has one limitation – Items property can’t be filled directly in XAML like this code does:
<Grid>
<DependencyPropertiesCollection:CustomControl>
<DependencyPropertiesCollection:CustomControl.Items>
<System:String>Item 1</System:String>
<System:String>Item 2</System:String>
<System:String>Item 3</System:String>
<System:String>Item 4</System:String>
<System:String>Item 5</System:String>
</DependencyPropertiesCollection:CustomControl.Items>
</DependencyPropertiesCollection:CustomControl>
</Grid>
To fix this, you need to change property type from IEnumerable to IList:
public class CustomControl : Control
{
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof (IList), typeof (CustomControl), new PropertyMetadata(new List<string>()));
public IList Items
{
get { return (IList)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public CustomControl()
{
Items = new List<string>();
}
}
Credits:-
http://sshumakov.com/2012/11/13/how-to-create-dependency-properties-for-collections/