Populating custom array property in XAML - wpf

I am trying to populate a custom string array in XAML, but am receiving an error. I subclassed a ComboBox and added a string [] that I want to fill with custom values:
public class MyComboBox : ComboBox
{
public string[] MyProperty { get { return (string[])GetValue(MyPropertyProperty); } set { SetValue(MyPropertyProperty, value); } }
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(string[]), typeof(MyComboBox));
}
My XAML is as follows:
<Window x:Class="Samples.CustomArrayProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Samples"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="CustomArrayProperty" Height="300" Width="300">
<Grid>
<local:MyComboBox Height="20">
<local:MyComboBox.MyProperty>
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:MyComboBox.MyProperty>
</local:MyComboBox>
</Grid>
</Window>
When I run this, I get the error: "'Monday' is not a valid value for property 'MyProperty'.".
What am I doing wrong?

You can create arrays in XAML using x:Array, you still need to use it as a resource like Liz's answer.
<Window.Resources>
<x:Array Type="sys:String" x:Key="days">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</x:Array>
</Window.Resources>
<local:MyComboBox Height="23" MyProperty="{StaticResource days}" />

Is there any other reason you are subclassing the ComboBox beyond what you are doing here?
Because if the idea is to just provide a list of strings to the combobox then take a look at "Add collection or array to wpf resource dictionary"
As much as possible it would be better to let the combobox take care of itself - by using the ItemsSource property. So what we are doing in the linked example is providing a 'resource' containing your list of strings, and then passing this resource to the ItemsSource as follows:
<ComboBox ItemsSource="{StaticResource stringList}" />
Define a type like this:
public class StringList : List<string> { }
And then create a static resource
<Window.Resources>
<local:StringList x:Key="stringList">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:StringList >
</Window.Resources>
I hope that helps.
Edit: You could also change your DependencyProperty to use StringList instead of String[], then your dependency property will work as well.
<local:MyComboBox MyProperty="{StaticResource stringList}" Height="23" />
And then the dependencyProperty:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty",typeof(StringList),typeof(MyComboBox),new FrameworkPropertyMetadata(null, listChangedCallBack));
static void listChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
ComboBox combo = (ComboBox)property;
combo.ItemsSource= (IEnumerable)args.NewValue;
}
Then this would effectively do the same thing as binding directly to the ItemsSource. But the main thing if I understand you correctly, is just to get the Dependency property to work.

And, an extra example with enum:
namespace MyNamespace
{
public enum OrganizationType
{
Office365,
OnPremisses
}
}
xmlns:local="clr-namespace:MyNamespace"
<x:Array Type="local:OrganizationType" x:Key="OrganizationTypeArray">
<local:OrganizationType>Office365</local:OrganizationType>
<local:OrganizationType>OnPremisses</local:OrganizationType>
</x:Array>

Related

How do dependency properties work in WPF?

I'm trying to understand dependency property and learn how to use it. I'm going through articles and in this article https://www.c-sharpcorner.com/UploadFile/6d590d/wpf-dependency-property/ there's this example:
MainWindow.xaml:
<Window x:Class="WpfApplication1.DependencyPropertyDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="DependencyPropertyDemo" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<local:CarDependencyClass x:Key="carDependencyClass"></local:CarDependencyClass>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Enter Car:" Grid.Row="0" VerticalAlignment="Center" />
<TextBox Text="{Binding Path=MyCar, Source={StaticResource carDependencyClass }}" Name="MyTextCar" Height="25" Width="150" />
<Button Name="MyButton" Content="Click Me!" Height="25" Click="MyButton_Click" Width="150" Grid.Row="1" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for DependencyPropertyDemo.xaml
/// </summary>
public partial class DependencyPropertyDemo : Window {
public DependencyPropertyDemo() {
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e) {
CarDependencyClass dpSample = TryFindResource("carDependencyClass") as CarDependencyClass;
MessageBox.Show(dpSample.MyCar);
}
}
public class CarDependencyClass : DependencyObject {
//Register Dependency Property
public static readonly DependencyProperty CarDependencyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(CarDependencyClass));
public string MyCar {
get {
return (string)GetValue(CarDependencyProperty);
}
set {
SetValue(CarDependencyProperty, value);
}
}
}
}
It works. I noticed that they registered dependency property with the name "MyProperty" and that it isn't used anywhere in the program. Only normal CLR property MyCar is used in xaml.
But then there's another article https://www.c-sharpcorner.com/article/simplest-wpf-dependency-property-for-beginners-on-background-color/. And they provide other example:
MainWindow.xaml:
<Window x:Class="DependencyPropertyTutorial.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:views="clr-namespace:DependencyPropertyTutorial" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DependencyPropertyTutorial" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="BG" Color="Green" />
</Window.Resources>
<Grid>
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
</Grid>
</Window>
CustomButtonControl.xaml:
<UserControl x:Class="DependencyPropertyTutorial.CustomButtonControl"
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:DependencyPropertyTutorial"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button x:Name="btnCustom" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Height="52" Click="btnCustom_Click" />
</Grid>
</UserControl>
CustomButtonControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DependencyPropertyTutorial {
/// <summary>
/// Interaction logic for CustomButtonControl.xaml
/// </summary>
public partial class CustomButtonControl : UserControl {
public CustomButtonControl() {
InitializeComponent();
}
public static readonly DependencyProperty btnDependencyProperty = DependencyProperty.Register("SetBackground", typeof(SolidColorBrush), typeof(CustomButtonControl), new PropertyMetadata(new SolidColorBrush(Colors.HotPink), new PropertyChangedCallback(OnSetColorChanged)));
public SolidColorBrush SetBackground {
set {
SetValue(btnDependencyProperty, value);
}
get {
return (SolidColorBrush)GetValue(btnDependencyProperty);
}
}
private void btnCustom_Click(object sender, RoutedEventArgs e) {
this.SetBackground = new SolidColorBrush(Colors.IndianRed);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
CustomButtonControl mycontrol = d as CustomButtonControl;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e) {
btnCustom.Background = (SolidColorBrush)e.NewValue;
}
}
}
And here they register dependency property with the name "SetBackground" which is exactly the same as the name of CLR property - SetBackground. And if I change dependency property(the one I registered with Register method) "SetBackground" to something else, like "SetBackgroundDependencyProperty", then I get a XAML exception when trying to run the application. But "SetBackground" dependency property isn't even referenced in xaml anywhere. Only CLR property SetBackground is referenced in XAML at the line
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
I also get an error in Visual Studio with this example:
But when I try to build and run the application, it works.
So my questions are: why in the first example they didn't have to name registered dependency property the same as CLR property, but in the second example, I have to name registered dependency property the same as CLR property. Is there a way to name registered dependency property differently to the CLR property in the second example? Why and how xaml even uses dependency properties, considering xaml references only CLR properties anyway? I checked it and in both projects only CLR properties are referenced from XAML, according to VS IntelliSense. Why do I have to register dependency property with the same name as CLR property - "SetBackground" when in xaml only CLR property is referenced and it returns SolidColorBrush from the dependency property, anyway:
return (SolidColorBrush)GetValue(btnDependencyProperty);
Here's the solution with both examples:
https://github.com/KulaGGin/DependencyProperty
First example is a bit dirty, I wouldn't code it such way. There is a good convention to avoid a confusion - to name the DP as the CLR property + 'Property'(but it's not mandatory!) and register it as name of CLR property(if you want to use it as DP in XAML).
First to your questions:
First example does work, because of everywhere, where the property MyCar being used, it is used as CLR property. If you will try to bind to the MyCar, it will fail, because of there is no such a dependency property. To implement the functionality in this example would be enough just to declare a CLR property:
public string MyCar { get; set; }
instead of all this confusion with dependency property.
In second example CLR property as well as dependency property SetBackground are defined (the name btnDependencyProperty for the field is not convenient, but OK). Missunderstanding on your side, is what being used in XAML.
If you use in XAML Binding or DynamicResource the dependency property as well as CLR property are necessary! Therefore they need to have the same name. If there aren't, then you will get an error.
If you set the property to the StaticResource or directly to the value or even do not use it in XAML, then you will be able to run the application.
DependencyProperty implementation is slightly weird.
The XAML compiler depends on the CLR property wrapper in order to compile, but at runtime bindings ignore it completely and just call GetValue/SetValue on the DP. Therefore the names should match.
There was a typo in the first example. The registered dependency property name needs to be the same as the CLR-backing property.
Declaring a DependencyProperty is a two-stage process:
Register the property so that the WPF DependencyProperty system can track it and notify when properties change, etc.
Setup a CLR property that gives developers an API to get and set values.
I hope this helps.

WPF Custom attached property that can be empty

I need to create a custom attached property that will be able to be empty as shown below:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<l:SimpleAttach.MyProperty></l:SimpleAttach.MyProperty>
</Grid>
I tried doing it this way:
public static class SimpleAttach
{
public static object GetMyProperty(DependencyObject obj)
{
return obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, object value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(object), typeof(SimpleAttach), new PropertyMetadata(null));
}
But that just gives an error:
The property "MyProperty" cannot be empty.
By contrast, MS doesn't seem to have any problem creating empty attached properties. Here is an example that does not give an error about being empty:
<TextBlock Name="VatAmount" Text="hello world" TextAlignment="Right" Margin="0,0,20,111" HorizontalAlignment="Right" Width="120" Height="16" VerticalAlignment="Bottom">
<i:Interaction.Behaviors>
</i:Interaction.Behaviors>
</TextBlock>
So what do I have to do to tell XAML that this property is allowed to be empty?
Okay, so I finally figured out my issues. Sometimes it helps to read the official documentation: XAML Syntax In Detail
In particular I seemed to be referring to "Content properties".
XAML content syntax is a syntax that is only enabled on classes that specify the ContentPropertyAttribute as part of their class declaration. The ContentPropertyAttribute references the property name that is the content property for that type of element (including derived classes). When processed by a XAML processor, any child elements or inner text that are found between the opening and closing tags of the object element will be assigned to be the value of the XAML content property for that object.
Second of all, in the example of Microsofts Interactivity.Behaviors, that is a collection and collections are allowed to have 0 items in them.
Furthermore, if you go down the road of implementing an attached behavior that is a collection, then this post will guide you through the quirks: Attached property of type list

WPF binding set before application starts does not notify?

In theory this code should provide me with a 300x300 window with a blue background from having the window's content bound to an object of type AstRootViewModel, however this doesn't seem to be the case. I'm wondering it this is happening because I don't call astApplication.Run() until after I set the mainWindow.ViewModel property. Using snoop to check the binding I have a blank content binding and it's flagged as an error with no error information.
If it is the case that property notification does not occurr until the application Run method is called, then what would be the best way to resolve this in an MVVM friendly way?
I have the following Entry point to a WPF application:
[STAThread]
public static void Main()
{
settingsSource = LoadSettingsFile(".\\applicationSettings.xml");
astApplication = new Application();
mainWindow = new AstWindowView();
mainWindowModel = new AstRootViewModel();
dataModel = new AstDataModel(settingsSource);
mainWindow.ViewModel = mainWindowModel;
astApplication.MainWindow = mainWindow;
astApplication.Run();
}
The AstWindowView class implements the following significant code behind:
public partial class AstWindowView : Window
{
public AstRootViewModel ViewModel
{
get { return (AstRootViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
// Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(AstRootViewModel), typeof(Window), new UIPropertyMetadata(null));
public AstWindowView()
{
InitializeComponent();
}
}
and the following significant XAML
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="AstViewResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Content>
<Binding Path="ViewModel" Mode="Default" UpdateSourceTrigger="PropertyChanged"/>
</Window.Content>
The AstViewResources.xaml file defines the following DataTemplate
<DataTemplate DataType="{x:Type vm:AstRootViewModel}">
<vw:AstRootView/>
</DataTemplate>
And lastly, the AstRootView XAML contains the following significant XAML:
<UserControl x:Class="SEL.MfgTestDev.AutomatedSettingsTransfer.View.AstRootView"
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 Background="#FF000CFF"/>
</UserControl>
It looks like you're not setting the DataContext for AstWindowView anywhere and your Binding has no explicit Source set. Are you seeing a binding error saying something to that effect in your debug output? Try adding after the InitializeComponent call in AstWindowView ctor (could also do it by changing the Binding in XAML):
DataContext = this;

Error when binding a Dependency Property on a custom Behavior

I am exploring the Silverlight attached behaviors mechanism in order to use the Model-View-ViewModel pattern within my Silverlight applications. To start with, I am trying to get a simple Hello World working, but I am completely stuck in an error for which I'm not able to find a solution.
What I have right now is a page that just contains a button which should display a message when clicked. The click event is handled by using a class derived from Behavior, and the message is specified as a dependency property of the behavior itself. The problem comes when trying to bind the message property to a property on a viewmodel class used as the data context: I get an exeption in the call to InitializeComponent in the view.
Here is all the code I'm using, as you can see it is rather simple. First the markup of the main page and the view it contains:
MyPage
<UserControl x:Class="MyExample.MyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyExample"
>
<Grid x:Name="LayoutRoot">
<local:MyView/>
</Grid>
</UserControl>
MyView (the TextBlock is there just to check that the binding syntax is correct)
<UserControl x:Class="MyExample.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyExample"
Width="400" Height="300">
<StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="White">
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
</StackPanel.Resources>
<TextBlock Text="This button will display the following message:"/>
<TextBlock Text="{Binding MyMessage, Source={StaticResource MyResource}}" FontStyle="Italic"/>
<Button x:Name="MyButton" Content="Click me!">
<i:Interaction.Behaviors>
<local:MyBehavior Message="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</i:Interaction.Behaviors>
</Button>
</StackPanel>
</UserControl>
Now the code, there are two classes: one for the behavior and another one for the viewmodel:
MyViewmodel
public class MyViewmodel
{
public string MyMessage
{
get { return "Hello, world!"; }
}
}
MyBehavior
public class MyBehavior : Behavior<Button>
{
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message",
typeof(string), typeof(MyBehavior),
new PropertyMetadata("(no message)"));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += new RoutedEventHandler(AssociatedObject_Click);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Click -= new RoutedEventHandler(AssociatedObject_Click);
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Message);
}
}
Simple enough, but this code throws an AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43] (right at the start of the value being set for the Message property) exception when ran. I'm sure that I'm missing something, but what?
Additional information: if I remove the binding from the Message property on MyBehavior (that is, if I set its value to any static string), it works fine. Also, I'm targeting silverlight 3 RTW.
Thanks a lot!
UPDATE
It seems that unlike WPF, Silverlight does not support data binding on any object deriving from DependencyObject, but only on objects deriving from FrameworkElement. This is no the case for Behavior, hence binding does not work.
I have found a workaround here, in the form of something named surrogate binders. Basically you specify the element and property to be binded, as well as the value, as attributes of the FrameworkElement containing the non-FrameworkElement object.
UPDATE 2
The surrogate binder does not work when the FrameworkElement contains an Interaction.Behaviors sub-element.
I have found another solution here, and this one seems to work. This time, the trick used is a DeepSetter. You define one of such setters as a static resource on the containing StackPanel, and then reference the resource from the behavior. So in my example, we should expand the StackPanel resources section as follows:
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
<local:DeepSetter
x:Key="MyBehaviorSetter"
TargetProperty="Message"
BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>
...and modify the button's behavior declaration as follows:
<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>
UPDATE 3
Good news: data binding for any DependecyObject will be available on Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind
To get the DataBinding support the class should inherit from FrameworkElement.Hoping MSFT will give support in Silverlight 4

Declaratively set property of MyUserControl in MyUserControl.xaml

Assuming we have such control:
public partial class MyUserControl : UserControl
{
public MyUserControl() {
InitializeComponent();
}
public string Foo { get; set; }
}
How can I set "Foo" property value declaratively in MyUserControl.xaml?
<UserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Looking for some thing like this -->
<Foo>Hola</Foo>
</UserControl>
To be more clear: How can I set in XAML a value for the property defined in code-behind.
I've found in the past that you can use a style to set properties on a UserControl from xaml without inheritance. Try something like this:
<UserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Test.MyUserControl" >
<UserControl.Style>
<Style>
<Setter Property="c:MyUserControl.Foo" Value="Hola" />
</Style>
</UserControl.Style>
</UserControl>
This can only be achieved in xaml by inheritance:
You are providing an implementation for your control. So the only way to achieve a value in xaml for your control is through inheritance.
Your second UserControl will look like this:
<Test:MyUserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Test="clr-namespace:Test"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Test:MyUserControl.Foo>Hola</Test:MyUserControl.Foo>
</Test:MyUserControl>
or:
<Test:MyUserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Test="clr-namespace:Test"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Foo="Hola">
</Test:MyUserControl>
In your control set it in the constructor
public MyUserControl{ this.Foo = "Hola";}
If you're using the control somewhere else:
<Window xmlns:mycontrol="Test" ....
Intellisense will help you here with proper syntax of xmlns
<mycontrol:MyUserControl Foo="Hola"/>
I'm not sure, but Foo may probably need to be a DependencyProperty.
It seems like what you really want is a default value for properties on your UserControl. Unfortunately you can't set it in a Style unless it's a DependencyProperty. It also can't be the target of a Binding.
If Foo were a DependencyProperty, you could set it in the PropertyMetadata when you declare your DependencyProperty:
public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(String), typeof(MyUserControl), new PropertyMetadata("Hola"));
Otherwise you're probably better off setting the default value in code-behind.
What about a non string property. e.g. MyCustomObject=

Resources