How to limit a custom property to available values in XAML - wpf

I have an enum declared as follows:
public enum DirectionTypes
{
IN = 2,
OUT = 1
}
This enum is used on user controls where I need to specify in XAML which direction the control needs to work. I created a dependency property on each user control as follows:
public static readonly DependencyProperty DirectionTypeProperty =
DependencyProperty.Register(
"DirectionType",
typeof(DirectionTypes),
typeof(TransactionGrid), new PropertyMetadata(DirectionTypes.IN));
public DirectionTypes DirectionType
{
get
{
return (DirectionTypes)GetValue(DirectionTypeProperty);
}
set
{
SetValue(DirectionTypeProperty, value);
}
}
I can then use the user control as follows:
<local:TransactionGrid x:Name="theGrid" DirectionType="OUT" />
I can run the program just fine. The problem is that DirectionType="OUT" causes an intellisense error in Visual Studio 2015. I get blue squiglies under the XAML property and my designer won't show the preview, saying instead "Invalid Markup". The error says The Type converter for DirectionTypes does not support converting from a string.
What am I missing that will allow the XAML parse properly.

Specify the enumeration value explicitly as follows (assuming DirectionTypes is in the same name space as local):
<local:TransactionGrid x:Name="theGrid" DirectionType="{x:Static local:DirectionTypes.OUT}" />

Related

WPF two-way binding with internal setter

I'm using WPF's two-way binding on a CLR property, which implements INotifyPropertyChanged.
The set for the property is internal, while the get is public.
Unfortunately, I get the following error:
System.Windows.Markup.XamlParseException was unhandled
Message: An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: A TwoWay or OneWayToSource binding cannot work on the read-only property 'Name' of type 'MyType'.
Is this the expected behavior? I would have thought that internal setters should work just fine...
Note that the CLR-type is defined in another assembly, and are visible in the current assembly, with the [assembly: InternalsVisibleTo("MyAssembly")] attribute.
Does anyone have workarounds/suggestions? The declaring assembly is a class library, so it's not an option for me to change the set to public.
You can create your own NEW public wraper property and use getter and setter of it to interact with your internal property
internal string _SideTabHeader;
public string SideTabHeader
{
get { return _SideTabHeader; }
set
{
if( value<0)
{
do nothing
}
else
{
_SideTabHeader=value;
};
}
}
Oh my... I just found out, WPF bindings don't work with internal properties. Oh, Microsoft... Whatever were you thinking?
Update:
Here's what I've understood so far (Thank you, #Grx70):
WPF is not a native part of the .NET framework, it's just a "plug-in" framework that happens to be also written by Microsoft. That is why it can't access the internal members of your assembly.
Microsoft could have allowed WPF to respect the [assembly: InternalsVisibleTo("XXX")] attribute, but as of right now, WPF ignores it - which unfortunately does not leave one with any easy workarounds.
Note: I tested using InternalVisibleTo - both Signed and Unsigned, with PresentationFramework, PresentationCore, and a whole bunch of other DLLs with no luck.
The only workaround I can think of right now is to create a "Proxy" class which can expose all required members as public. This is quite a PITA (I have a LOT of classes, and I hate the maintenance nightmare that comes with creating an equal number of "Proxy" classes) - so I might look into using PostSharp, or Fody or some kind of weaver to auto-create these "Proxy" classes if I can.
All the best to anyone else facing this issue.
This is very late and not solving the initial question, but as very related it may help someone else which very similar problem...
If your internal property is of type Enum else skip
In my case I was trying to do a WPF xaml binding to a property of type inherited from a WCF service. The easy way to solve that simple case was to use int.
public Dictionary<int, string> ProductsList => EnumExtensions.ProductsList;
public int ProductType
{
get { return (int)_DeliveryProduct.ProductType; }
set
{
if (value.Equals(ProductType)) return;
_DeliveryProduct.ProductType = (ProductEnum)value;
RaisePropertyChanged(() => ProductType);
}
}
_DeliveryProduct is my reference to my domain object for which the property ProductType is an enum but in my viewmodel that property is an int.
... Note that ProductEnum is autogenerated from the API and can't be changed to public.
internal static Dictionary<int, string> ProductsList => new Dictionary<int, string>
{
{(int)ProductEnum.Regular, ProductEnum.Regular.GetDisplayName()},
{(int)ProductEnum.Intermediate, ProductEnum.Intermediate.GetDisplayName()},
{(int)ProductEnum.Super, ProductEnum.Super.GetDisplayName()},
{(int)ProductEnum.Diesel, ProductEnum.Diesel.GetDisplayName()}
};

Dependency Property register names

public string City
{
get { return GetValue(CityProperty).ToString(); }
set { SetValue(CityProperty, value); }
}
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register("City", typeof(string), typeof(Object1));
If I make a mistake, here:
...Register("City"), typeof...
like for instance I forget to capitalize "city", or i.e "vity"
Is the dependency property subsystem just totally blown away? The string City property is still there right? I can set it in various ways and the static CityProperty thingamabob is still there, I can do something somewhere with that. I can do like Object1.CityProperty and such, But where is exactly is the breakdown/link? If that "City" literal in the register method and the City property don't match, then the string City property is just not a dependency property?
I guess also I mean, if the string City property is calling GetValue, then what is the difference? Will the subsystem 'find' everything and be able to support using string City property as the: a) target of binding b) animation c) styling
EDIT
In a somewhat disturbing development, the following 'works' . When you set the City property from XAML, it apparently stores it somewhere. What part(s) of a) using as a target for binding, b) styling, c) animation don't is a mystery to me.
namespace util
{
public class foo : FrameworkElement
{
public String City
{
get { return (String)GetValue(CityProperty); }
set { SetValue(CityProperty, value); }
}
// Using a DependencyProperty as the backing store for City. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register("city", typeof(String), typeof(foo), null);
}
}
namespace screwing_up_dependencies
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var aFoo = this.FindName("bar");
if (aFoo is util.foo)
{
util.foo theFoo = (util.foo)aFoo;
((Button)sender).Content = theFoo.City;
}
}
}
}
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"
xmlns:util="clr-namespace:util"
>
<util:foo x:Name="bar" City="blah"></util:foo>
<Button Content="Button" Grid.Row="1" Click="Button_Click" Height="105"/>
</Grid>
EDIT 2
Furthermore, you can also do this. It apparently doesn't make any difference what the "Name" parameter to the register command is. And also it doesn't seem to matter what the OwnerType parameter is either.
<util:foo x:Name="bar2" City="{Binding ElementName=ContentPanel, Path=Width}"></util:foo>
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register(Guid.NewGuid().ToString(), typeof(String), typeof(object), null);
var aFoo = this.FindName("bar2");
if (aFoo is util.foo)
{
util.foo theFoo = (util.foo)aFoo;
((Button)sender).Content = theFoo.City;
}
Will the subsystem 'find' everything and be able to support using string City property as the: a) target of binding b) animation c) styling
No.
Reflection is used by other systems to divine information about the instance. When the property city does not exist no operations can occur. For in C# one can have overloaded property names (i.e. City and city) and those are two separate entities in the eyes of the compiler and the runtime process reflecting off of an unknown instance.
is there any Lint tool available that will detect this kind of thing automagically
To avoid errors when creating dependency properties I use Jeff Wilcox's (Helpful Silverlight Snippets - Jeff Wilcox). Via using snippets which are geared towards the 6 types of dependency properties, it avoids the errors. Note that even though it is Silverlight, I use them without change in WPF.

How to display application title in XAML text field

I have a Windows Phone page code that is shared by multiple applications.
At the top of the page, I show the title of the application, like so:
Is it possible to make the text be bound to the application title as defined in the application's assembly?
I realise that I could do this by code by reading the title in the assembly and then doing something like:
this.ApplicationTitle.Text = title;
I was hoping that the title as defined in the assembly could be accessed with some magic like:
Text={assembly title}" directly from within the xaml.
Thanks
Create a property called ApplicationTitle that returns that name of the application like the following, and then bind to it in XAML:
public string ApplicationTitle
{
get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; }
}
(You can use a relative binding source if you can't or don't want to use the data context.)
edit:
I just realized that my method involved security considerations since GetName is a method that is [Security Critical]. And I got a MethodAccessException stating: Attempt to access the method failed: System.Reflection.Assembly.GetName()
So here's another way to get the assembly name and return it in a property by using the AssemblyTitle attribute.
public string ApplicationTitle
{
get
{
System.Reflection.AssemblyTitleAttribute ata =
System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(
typeof(System.Reflection.AssemblyTitleAttribute), false)[0] as System.Reflection.AssemblyTitleAttribute;
return ata.Title;
}
}
To bind in XAML, you can use this:
Text="{Binding ElementName=LayoutRoot, Path=Parent.ApplicationTitle}"

Argument validation in custom activity designer

I am having problems getting validation to work properly in the designer for my custom activity. The simplest sample to reproduce the behavior is as follows:
I have a custom WF4 activity with a dynamic collection of arguments stored in a dictionary:
[Designer(typeof(DictionaryActivityDesigner))]
public class DictionaryActivity : NativeActivity
{
[Browsable(false)]
public Dictionary<string, InArgument> Arguments { get; set; }
public InArgument<string> StringArg { get; set; }
public DictionaryActivity()
{
Arguments = new Dictionary<string, InArgument>();
}
protected override void Execute(NativeActivityContext context)
{ }
}
In the designer I dinamically create expression text boxes for editing these arguments. The user has the possibility to define the arguments and their types in a separate modal window, but for the sake of simplicity I have fixed the arguments in this sample:
public partial class DictionaryActivityDesigner
{
private Dictionary<string, Type> definition;
public DictionaryActivityDesigner()
{
definition = new Dictionary<string, Type>
{
{ "String Arg", typeof(string) },
{ "Int Arg", typeof(int) }
};
InitializeComponent();
}
public void InitializeGrid(Dictionary<string, Type> arguments)
{
ArgumentsGrid.RowDefinitions.Clear();
ArgumentsGrid.Children.Clear();
int gridRow = 0;
foreach (var arg in arguments)
{
ArgumentsGrid.RowDefinitions.Add(new RowDefinition());
var label = new Label()
{
Content = arg.Key + ":"
};
Grid.SetRow(label, gridRow);
Grid.SetColumn(label, 0);
ArgumentsGrid.Children.Add(label);
var textbox = new ExpressionTextBox()
{
ExpressionType = arg.Value,
OwnerActivity = ModelItem,
UseLocationExpression = false
};
var binding = new Binding()
{
Mode = BindingMode.TwoWay,
Converter = new ArgumentToExpressionConverter(),
ConverterParameter = "In",
Path = new PropertyPath("ModelItem.Arguments[(0)]", arg.Key)
};
textbox.SetBinding(ExpressionTextBox.ExpressionProperty, binding);
Grid.SetRow(textbox, gridRow);
Grid.SetColumn(textbox, 1);
ArgumentsGrid.Children.Add(textbox);
gridRow++;
}
}
private void ActivityDesigner_Loaded(object sender, RoutedEventArgs e)
{
InitializeGrid(definition);
}
}
Below is the XAML for the designer:
<sap:ActivityDesigner x:Class="ActivityValidation.DictionaryActivityDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
Loaded="ActivityDesigner_Loaded">
<sap:ActivityDesigner.Resources>
<ResourceDictionary>
<sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
</ResourceDictionary>
</sap:ActivityDesigner.Resources>
<StackPanel Orientation="Vertical">
<Grid Name="ArgumentsGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition MinWidth="250" />
</Grid.ColumnDefinitions>
</Grid>
<sapv:ExpressionTextBox ExpressionType="s:String"
OwnerActivity="{Binding ModelItem}"
Expression="{Binding ModelItem.StringArg, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}" />
</StackPanel>
</sap:ActivityDesigner>
The InitializeGrid method adds the expression text boxes for the arguments to the ArgumentGrid. Under it I have a separate statically defined expression text box for a fixed argument in the activity to demonstrate the (almost) desired behavior.
Now for the problems:
Invalid expressions for the dynamic arguments only cause the error icon to appear beside the text box but it doesn't propagate to the top bar of the designer as it does if there is an error in the statically defined text box.
If I close the designer in such invalid state (and save the definition), the eror icon correctly propagates to the top bar even if the error is only in the dynamic text box. Though the behavior gets even more strange afterwards. After changing the values for the arguments, now even the error icon beside the text box doesn't work consistently any more.
If I delete the contents of a dynamic text box completely, the value in the dictionary gets set to null which manifests in the workflow definition as <x:Null x:Key="String Arg" /> instead of <InArgument x:TypeArguments="x:String" x:Key="String Arg">["a"]</InArgument> or just ommiting the entry as is the case before editing the expression for the first time. If I reopen such a workflow even the statically created text box doesn't work properly any more (the error icon is only visible when text box is focused and it doesn't propagate to the top any more).
It seems obvious that I am doing something wrong when creating the dynamic text boxes. What would be the correct way of doing it? Is there any example available for creating a designer for a custom activity with dynamic number of arguments?
EDIT:
For those interested:
There was some more discussion on MSDN Forums where I have also posted the issue.
As a result of that discussion, I've also filed a report on Microsoft Connect.
I encountered the problem I described here while trying to create a designer for a dynamic collection of arguments in an activity. I managed to work around the problem by using the built-in DynamicArgumentDialog window. I had to restructure my activity to accept a single collection of both input and output arguments:
public Dictionary<string, Argument> Arguments { get; set; }
instead of two separate collections I was using before:
public Dictionary<string, InArgument> InArguments { get; set; }
public Dictionary<string, OutArgument> OutArguments { get; set; }
I found the Custom Activity to Invoke XAML Based Child Workflows very helpful when making this work.

WPF: instances of usercontrol share dependency properties

I made an usercontrol and it works great, but when I put two instances of this control to one window, only the last of them works. I tried to find solution and I realized, that dependency properties are shared, but I dont know how to get it work.
Here is my dependency property:
public double AnimatingVerticalOffset
{
get { return (double)GetValue(AnimatingVerticalOffsetProperty); }
set { SetValue(AnimatingVerticalOffsetProperty, value); }
}
public static readonly DependencyProperty AnimatingVerticalOffsetProperty;
static ListChooser()
{
ListChooser.AnimatingVerticalOffsetProperty =
DependencyProperty.Register("AnimatingVerticalOffset", typeof(double), typeof(ListChooser), new UIPropertyMetadata(OnAnimationVerticalOffsetChanged));
}
The dependency property itself must be static with no ties to one single instance. And that applies for its callbacks too (OnAnimationVerticalOffsetChanged in your case) - these must be static methods (don't worry, the object instance is passed via its parameter, you just have to do some type casting to ensure the object is the type you are working with).
You should use static initializer to initialize DP, the method you used (initializing in constructor) works, but the DP will overwrite for each instance.
See this question for deeper explanation.
EDIT:
Corrected code:
public double AnimatingVerticalOffset
{
get { return (double)GetValue(AnimatingVerticalOffsetProperty); }
set { SetValue(AnimatingVerticalOffsetProperty, value); }
}
public static readonly DependencyProperty AnimatingVerticalOffsetProperty =
DependencyProperty.Register("AnimatingVerticalOffset", typeof(double), typeof(ListChooser), new UIPropertyMetadata(OnAnimationVerticalOffsetChanged));
static ListChooser()
{
}
If the callback is not static, you will get compile error (=> you have to make it static).
EDIT:
Remember, the DP definition is static, not the property's value itself! DPs work exactly just like any other property, it just has some extra features: value inhertiance, bidnings, animation...

Resources