WPX/XAML: How can I use TemplateBinding to define a converter in a UserControl? - wpf

I have two controls that contain identical markup, except that they require different converters.
LeftHeader.xaml snippet:
<Path.RenderTransform>
<RotateTransform Angle="{Binding IsToggled, Converter={x:Static local:LeftHeader.GlyphAngleConverter}}"/>
</Path.RenderTransform>
RightHeader.xaml snippet:
<Path.RenderTransform>
<RotateTransform Angle="{Binding IsToggled, Converter={x:Static local:RightHeader.GlyphAngleConverter}}"/>
</Path.RenderTransform>
I thought I could create a UserControl with a DependencyProperty of type IValueConverter called "GlyphAngleConverter" and this markup:
<UserControl x:Class="GlyphControl"
<UserControl.Template>
<ControlTemplate>
...
<Path.RenderTransform>
<RotateTransform Angle="{Binding IsToggled, Converter={TemplateBinding GlyphAngleConverter}}"/>
</Path.RenderTransform>
...
</ControlTemplate>
</UserControl.Template>
</UserControl>
The header controls would then use the new control and set GlyphAngleConverter basically like they used before:
New LeftHeader.xaml snippet:
<GlyphControl GlyphAngleConverter="{Binding Source={x:Static local:LeftHeader.GlyphAngleConverter}}"/>
New RightHeader.xaml snippet:
<GlyphControl GlyphAngleConverter="{Binding Source={x:Static local:RightHeader.GlyphAngleConverter}}"/>
But I get the exception "Unable to cast object of type 'System.Windows.TemplateBindingExpression' to type 'System.Windows.Data.IValueConverter'."
What am I doing wrong and/or is there a better approach?

As pointed out in the comments, "You can not bind the Converter of a Binding.".
My solution: create a UserControl with a DependencyProperty of type double called "GlyphAngle" and use that in the header controls, e.g.:
<GlyphControl GlyphAngle="{Binding IsToggled, Converter={x:Static local:LeftHeader.GlyphAngleConverter}}"/>
So, instead of passing the converter into the control, I'm passing the converted value.

You could try defining your converter as a resource.
Do so in a resource key merged in app.xaml. Then define your "other" converter in the the resources of one of the usercontrols, with the same key that is in a resource key merged in app.xaml.
If you have two resources with the same key then whichever is the most local will be found.
The "local" converter will be found in one usercontrol and the global in the other.

Related

How to directy use RenderTransform property of TextBlock

I understand I can rotate text like this:
<TextBlock Text="4:00">
<TextBlock.RenderTransform>
<RotateTransform Angle="-90"/>
</TextBlock.RenderTransform>
</TextBlock>
But how can I use the RenderTransform property of TextBlock directly like:
<TextBlock Text="4:00" RenderTransform="<How does this work?>"/>
to avoid the inner code? Maybe a general tutorial for how this works would be good as well.
RenderTransform property is of type Transform which can mean many possible transform types. So you can`t assign it to some type that has some properties using just a string by default.
If you want to to conserve space though, you can define your RotateTransform in resources, and give it some key:
<Window.Resources>
<RotateTransform x:Key="myRotateTransform" Angle="-90" />
</Window.Resources>
Then just use it like that:
<TextBlock Text="4:00" RenderTransform="{StaticResource myRotateTransform}" />
This will work even better if you need to apply same transform to several controls too, because you can edit it in just one place.

MouseDragElementBehavior Binding Properties

By default MouseDragElementBehavior does not implement the methods of Binding (SetBinding, GetBindingExpression, etc) how can I make a binding object to this behavior? Is this possible?
Not sure if i understand your question, but something like this may work:
<Rectangle x:Name="MyVisualElement" Fill="#FFF4F4F5" Stroke="Black" Height="90" Canvas.Left="188" Canvas.Top="113" Width="90">
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior x:Name="mouse" X="{Binding Height, ElementName=MyVisualElement}" Y="{Binding Width, ElementName=MyVisualElement}"/>
</i:Interaction.Behaviors>
</Rectangle>
The MouseDragElementBehavior inerits from DependencyObject and you can set bindings to the properties X and Y, note in the previous code how the element name is specified.
Hope this helps.

How to bind to parent position changes in WPF using XAML and no code behind

I have a visio-like interfact but have actual model data behind some of the elements. The elements can be moved by the user.
I use a contentcontrol on a canvas whereby the viewmodels of the elements are places in the content which can then be displayed differently depending on their type but using the same contentcontrol. It is simple to bind the view to the different properties in the viewmodel. However, I have to save the position in the model, and I cannot find a binding solution.
1) The Application.Save Command is handled in the main view model, so I do not have access to the view there. That means I must save the postion data when the elements are moved, or is there a better approach?
2) Assuming that I am right with 1), I am looking to avoid code behind, i.e. I do not want the contentcontrol to deal with the elements that they have in their content. However, so far the code behind version is all I could come up with:
My code behind solution so far:
All model elements implement an interface:
public interface IViewElement
{
String Position { get; set; }
}
And in the contentcontrol:
void ContentControl_LayoutUpdated(object sender, EventArgs e)
{
IViewElement content = this.Content as IViewElement;
content.Position = new Point(Diagram.GetLeft(this), Diagram.GetTop(this)).ToString();
}
The XAML:
<Style TargetType="{x:Type diagram:Item}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type diagram:Item}">
<Grid Canvas.Top="{Binding ElementName=PART_ContentPresenter, Path=Content.Position, Mode=TwoWay}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{x:Null}">
<!-- PART_ContentPresenter -->
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<DataTemplate DataType="{x:Type local:ViewModel}">
<StackPanel>
...
</StackPanel>
Just encapsulate the codebehind you've used in a Behavior
Why are you using a string to store the position? Use either a Point or two decimal values, and then bind your ContentControl's Canvas.Top and Canvas.Left position to these values using two-way binding.
It will automatically update the model when the Top and Left positions change.
Edit:
Here's an example:
<ContentControl Canvas.Top="{Binding ContentModel.Top, Mode=TwoWay}"
Canvas.Left="{Binding ContentModel.Left, Mode=TwoWay}"
Content="{Binding ContentModel}" />

WPF Bind scale of one element to the scale of another

I have a button that I want to bind the scaleX and scaleY to the scaleX and scaleY of another element, how can I do that?
The tricky part is that I want to bind it to an element that, at least on initialisation, may not have a ScaleTransform set on it...
Ideally, set it up so that the bindee element does always have a ScaleTransform, but it's the identity transform. Then name that ScaleTransform, and bind to it using the usual element-to-element syntax:
<TextBlock Text="One">
<TextBlock.LayoutTransform>
<ScaleTransform ScaleX="1"
ScaleY="1"
x:Name="s" /> <!-- Note the x:Name -->
</TextBlock.LayoutTransform>
</TextBlock>
<TextBlock Text="Two">
<TextBlock.LayoutTransform>
<ScaleTransform ScaleX="{Binding ScaleX, ElementName=s}"
ScaleY="{Binding ScaleY, ElementName=s}" />
</TextBlock.LayoutTransform>
</TextBlock>
If you need to bind to the element specifically, you can traverse down through the properties e.g. {Binding LayoutTransform.ScaleX, ElementName=someElement}. This will no-op if the source element has no LayoutTransform or it isn't a ScaleTransform, but it will report runtime binding errors.

Accessing a RenderTransform in a DataTemplate in Silverlight

I've got a bunch of ContentControls with a DataTemplate like so:
<DataTemplate>
<Canvas>
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"/>
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
</DataTemplate>
...and I want to change their scales dynamically. I'm new to .NET, so please forgive. I tried to use this technique:
http://msdn.microsoft.com/en-us/library/bb613579.aspx
...but DataTemplates don't appear to have FindName in Silverlight. I then tried binding the Scales like so:
<ScaleTransform ScaleX="{Binding Scale}" ScaleY="{Binding Scale}"/>
...but got a XAML error when I ran.
Am I barking up the wrong tree? I figure this must be possible somehow.
Thank you.
Assuming you don't want to animate the scale, simply include a Scale property in your view model. You cannot access an ancestors DataContext from inside a DataTemplate (WPF supports this, however).
Instead of setting the DataContext of your DataTemplate to your entity, create a wrapper class (ViewModel) that also includes a (INotifyPropertyChanged-firing) Scale property. Now your ContentControl can bind to the Scale property of your view model.

Resources