Custom markupextension on custom DataTemplate property - wpf

I'm wondering if there is a way to use a custom markup extension on a custom property on a type derived from datatemplate?
I need some additional information inside my DataTemplates:
public class MyDataTemplate : DataTemplate
{
public string GroupName { get; set; }
public string TemplateKey { get; set; }
public object Geometry { get; set; }
}
<MyDataTemplate x:Key="Foo" DataType="{x:Type system:String}" GroupName="Group1"
Geometry="{telerik:CommonShape ShapeType=RectangleShape}">
...
</MyDataTemplate>
When I put the CommonShape markupextension on the DataType property, everything works.
When I put the x:Type markupextension on the Geometry property, everything works, too.
But if I put the custom markupextension on the custom property, I get the error
The property 'Geometry' cannot be set as a property element on template.
Only Triggers and Storyboards are allowed as property elements.
Is there any workaround for this?
Alex
Edit:
One possible workaround could be to put the values of the markupextension into the resource dictionary and use a StaticResource on the Geometry property - however, I'm not sure if this is possible using xaml?

Related

Binding a ViewModel property to a property of a static resource

I have an interface IFooBar and some concrete implementations of it, FooBarOne and FooBarToo.
public interface IFooBar
{
int Value { get; set; }
}
public class FooBarOne : IFooBar { ... }
public class FooBarTwo : IFooBar { ... }
I've added a DependencyProperty (called FooBar) of type IFooBar to a custom control MyControl.
public static readonly DependencyProperty FooBarProperty = ...
public IFooBar FooBar
{
get { return (IFooBar)GetValue(FooBarProperty ); }
set { SetValue(FooBarProperty, value); }
}
Whenever this control is used, I can create instances of FooBarOne or FooBarTwo as static resources, and then use these to set the FooBar DependencyProperty on instances of MyControl and this all works as expected.
<UserControl.Resources>
<ResourceDictionary>
<ns:FooBarOne x:Key="MyFooBarOne" Value="1"/>
<ns:FooBarTwo x:Key="MyFooBarTwo" Value="2"/>
</ResourceDictionary>
</UserControl.Resources>
...
<controls:MyControl FooBar="{StaticResource MyFooBarOne}"/>
<controls:MyControl FooBar="{StaticResource MyFooBarTwo}"/>
What I'm struggling with is that I now need to bind a value from a ViewModel to the the IFooBar.Value property.
I tried adding the following to my resouces:
<UserControl.Resources>
<ResourceDictionary>
<ns:FooBarOne x:Key="MyFooBarOne" Value="{Binding SomeViewModelProperty}"/>
<ns:FooBarTwo x:Key="MyFooBarTwo" Value="{Binding SomeViewModelProperty}"/>
</ResourceDictionary>
</UserControl.Resources>
But this doesn't work because IFooBar.Value isn't a DependencyProperty.
I realise I could probably add a new DependencyProperty for IFooBar.Value to my MyControl, but in reality IFooBar actually contains numerous properties and I wanted to avoid having to create a new DependencyProperty for each of the properties on IFooBar.
Is there a way for me to bind a ViewModel property onto the the properties of my IFooBar instances?
Is there a way for me to bind a ViewModel property onto the the properties of my IFooBar instances?
No, for you to be able to bind something to a property in XAML, the target property must be a dependency property.
In the below sample markup, Value is the target property and SomeViewModelProperty is the source property:
<ns:FooBarOne x:Key="MyFooBarOne" Value="{Binding SomeViewModelProperty}"/>
Again, the target property must be a dependency property for you to be able to bind a value to it.
I presume that you're using MVVM, if so, then you should have properties on your ViewModel that return an IFooBar rather than declaring static resources. Then you can just return those values. If you need to specifically create an IFooBar for a given value, then you can create a ValueConverter that takes your value and spits out a FooBarOne or FooBarTwo.

Declaring list and binding property values via XAML for a custom silverlight control

I feel like I've missed something obvious, but when creating a custom control in Silverlight I can declare properties as public and they will be exposed when making XAML declarations, so I can easily do something like this:
public class MyControl : UserControl
{
public string Title {get; set;}
}
And this:
<local:MyControl Title="Hello World" />
However properties are not always simple types, I may have a complex property, like a list that defines 1 or more columns and identifies a field to be bound to it. With existing controls the syntax for defining these values in XAML is straight forward:
<local:MyControl Title="People List">
<local:MyControl.Columns>
<local:MyControlColumn Heading="Column 1" Binding="{Binding Name}" />
<local:MyControlColumn Heading="Column 2" Binding="{Binding Age}" />
</local:MyControl.Columns>
</local:MyControl>
However I'm at a loss of how to make this work in the class definition:
public class MyControl : UserControl
{
public string Title {get; set;}
public IEnumerable ItemSource {get; set;}
public ObservableCollection<MyControlColumn> Columns {get; set;} // ?
}
public class MyControlColumn
{
public string Heading {get; set;}
public ??? Binding {get; set;} // ?
}
Can anyone point me in the right direction for making exposed list and binding properties?
For the Columns collection create a backing private variable to hold an instance collection and remove the set accessor:-
private ObservableCollection<MyControlColumn> _columns = new ObservableCollection<MyControlColumn>();
public ObservableCollection<MyControlColumn> Columns {get { return _columns; } }
For the Binding property it looks like what you might want is the Binding type itself System.Windows.Data.Binding. However I think I'd need to know a little more about what you intend to do with the object once you've got it.
You should consider making the MyControl Title property a DependencyProperty:-
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MyControl), null);
Similarly for the ItemSource although you should also rename to ItemsSource to keep convention with existing controls.
I can't help feel there is some re-invention of the wheel here, does DataGrid or ListBox or some other similar control not cut it for you?

How to use a custom class in XAML?

I have the following class,
internal class PageInformation
{
public string Name
{
get;
set;
}
public Uri PageUri
{
get;
set;
}
}
How can I use it in XAML (a page) and assign values to its properties?
Update-1:
I added xmlns attribute in page tag
<Page xmlns:local="clr-namespace:Demo.Pages">
and inherited PageInformation from DependencyObject (to have dependency properties).
Still XAML does not recognize the PageInformation class
I was trying to use PageInformation element in a Canvas. Since it is not a UIElement, I think, It can't be added there.
I can use it as a resource element instead (in Page.Resources) :)

TextBox binding in WPF

I have a textbox in XAML file, On Changing the text in the textbox the prop setter is not getting called. I am able to get the value in ProjectOfficer textbox and not able to update it. I am using MVVM pattern
Below is my code XAML
TextBox Text="{Binding Path=Officer,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="ProjectOfficer"/>
ViewModel.cs
public Staff Officer
{
get
{
return __con.PrimaryOfficer ;
}
set
{
_con.PrimaryOfficer = value;
_con.PrimaryOfficer.Update(true);
}
}
Staff.cs
public class Staff : EntityBase
{
public Staff();
public string Address { get; }
public string Code { get; set; }
public override void Update();
}
Thanks
You're binding a property of type string on the TextBox to a property of type Officer on your ViewModel. I expect the setter isn't being called because WPF can't do the conversion.
If you check the output window in visual studio, you'll probably see a binding error for this property.
Try something like:
TextBox text ="{Binding Path=Address,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="ProjectOfficer"/>
Make sure the holder of the TextBox is linked to a Staff object. The textbox cannot bind directly to an object without telling the property to display (like Address in my example above).

Can I use XAML to set a nested property (property of the value of a property) of a control?

I've got a WPF Control that exposes one of it's children (from it's ControlTemplate) through a read-only property. At the moment it's just a CLR property, but I don't think that makes any difference.
I want to be able to set one of the properties on the child control from the XAML where I'm instantiating the main control. (Actually, I would like to bind to it, but I think setting it would be a good first step.)
Here's some code:
public class ChartControl : Control
{
public IAxis XAxis { get; private set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.XAxis = GetTemplateChild("PART_XAxis") as IAxis;
}
}
public interface IAxis
{
// This is the property I want to set
double Maximum { get; set; }
}
public class Axis : FrameworkElement, IAxis
{
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(Axis), new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender, OnAxisPropertyChanged));
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
}
Here's the two ways I can think of setting the nested property in XAML (neither compile):
<!--
This doesn't work:
"The property 'XAxis.Maximum' does not exist in XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'."
"The attachable property 'Maximum' was not found in type 'XAxis'."
-->
<local:ChartControl XAxis.Maximum="{Binding Maximum}"/>
<!--
This doesn't work:
"Cannot set properties on property elements."
-->
<local:ChartControl>
<local:ChartControl.XAxis Maximum="{Binding Maximum}"/>
</local:ChartControl>
Is this even possible?
Without it I guess I'll just need to expose DP's on the main control that get bound through to the children (in the template). Not so bad, I guess, but I was just trying to avoid an explosion of properties on the main control.
Cheers.
You can't do it like this... you can access nested properties through its path in a binding, but not when you define the value of the property.
You have to do something like that :
<local:ChartControl>
<local:ChartControl.XAxis>
<local:Axis Maximum="{Binding Maximum}"/>
</local:ChartControl.XAxis>
</local:ChartControl>

Resources