Accessing a RenderTransform in a DataTemplate in Silverlight - 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.

Related

Difficulties binding colors for WPF ComboBoxItem ItemTemplate with TemplateBinding

I'm trying to implement a simple combo box (bound to a list of strings) that shows each item as the string label on the right and icon (which is in the form of a GeometryDrawing) on the left. I want the items in the box to inherit the colors I'm using because I've got a custom scheme (with a dark background and white text).
I am able to use TemplateBinding (binding to to ComboBoxItem.Foreground) to make the colors of the text label appear properly but am not able to do the same for the Geometry drawing and I can't figure out why.
Here an image of what I am trying to achieve.
Note that the icon shows up nicely to the left of the name. This only works if I explicitly specify "White" as the brush of the GeometryDrawing.
Below is the XAML I'm using to do make this work.
<tk:RadComboBox ItemsSource="{Binding PostAnalysisRoutines}"
Grid.Row="8" Grid.Column="2"
Text="{Binding Settings.PostCaptureAnalysisRoutine, Mode=TwoWay}">
<!--
Each item in the box is a horizontal stackpanel with an icon for
the routine on the left and the routine name on the right
-->
<tk:RadComboBox.ItemTemplate>
<DataTemplate DataType="sdk:AnalysisRoutine">
<StackPanel Orientation="Horizontal">
<Image Width="30" Height="30">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<!-- ***FORCE TO USE "White" here WHY???*** -->
<GeometryDrawing Brush="White"
Geometry="{Binding Converter={StaticResource PathGeometryConverter}}"
/>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<!-- *** BUT ITS OK TO USE TemplateBinding HERE. WHY??? *** -->
<Label Foreground="{TemplateBinding tk:RadComboBoxItem.Foreground}"
Background="{TemplateBinding tk:RadComboBoxItem.Background}"
Content="{Binding}"/>
</StackPanel>
</DataTemplate>
</tk:RadComboBox.ItemTemplate>
</tk:RadComboBox>
A couple of notes:
I am using Telerik's RadComboBox here but I see a similar problem if I switch this over to regular WPF ComboBox.
I am able to use TemplateBindings for the Label's foreground and background colors to bind to the ComboBoxItem. Those work fine.
The PathGeometryConverter just takes the string name and finds a predefined PathGeometry resource for it.
Unfortunately I am NOT a able to use that same TemplateBinding for the GeometryDrawing's "Brush" property. When I try...
<GeometryDrawing Brush="{TemplateBinding tk:RadComboBoxItem.Foreground}"
Geometry="{Binding Converter={StaticResource PathGeometryConverter}}"
/>
I end up with this:
Therefore I am forced to explicitly specify "White" as the brush color for the
Geometry drawing to make this work.
What is the proper way to make my GeometryDrawing in my template "inherit" the surrounding color scheme...? Is there some sort of TemplateBinding I am missing?
I think your problem is because you're using templatebinding combined with something that is coming out of a resource and or because it's several levels down from the thing being templated. If the geometry was directly in the template then I think it'd work.
What I suggest is you instead try a binding with relativesource and ancestortype tk:RadComboBoxItem, path=foreground.
Or relativesource templatedparent.
I would also use a path rather than an image. It might make very little difference in this case but paths can be sharper. You can bind the Data of a Path to a Geometry resource. Or at least I think you can, I usually set them to a dynamic or staticresource directly.

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.

Silverlight RelativeSource of TemplatedParent Binding within a DataTemplate, Is it possible?

I'm trying to make a bar graph usercontrol. I'm creating each bar using a DataTemplate.
The problem is in order to compute the height of each bar, I first need to know the height of its container (the TemplatedParent). Unfortunately what I have:
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height, Converter={StaticResource HeightConverter}, Mode=OneWay}"
does not work. Each time a value of NaN is returned to my Converter. Does RelativeSource={RelativeSource TemplatedParent} not work in this context? What else can I do to allow my DataTemplate to "talk" to the element it is being applied to?
Incase it helps here is the barebones DataTemplate:
<DataTemplate x:Key="BarGraphTemplate">
<Grid Width="30">
<Rectangle HorizontalAlignment="Center" Stroke="Black" Width="20" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height, Converter={StaticResource HeightConverter}, Mode=OneWay}" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>
To answer your question RelativeSource only works in a ControlTemplate it doesn't work in a DataTemplate.
Is there a reason why the Silverlight Toolkit Chart controls don't work for you in creating a bar graph (or a Column Chart as the Tookit refers to vertical set of bars).
Have you tried the ActualHeight property? It should return you a value. RelativeSource with the TemplatedParent mode will work in a data template, but it will return the content presenter of the templated control/item, not the control/item itself (which it does when used in a control template). To experiment, put a button in the data template, and assign that binding expression (without the path) to its Tag property. Handle its Click event, and put a breakpoint in the event handler. Now when you run the project and click on the button, the breakpoint will be hit in your code, and you can see the object that it is binding to from the Tag property of the button (which you can see from the sender parameter). Hope this helps...

How to bind a Canvas to a collection of items in Silverlight

Background
I've got a collection of objects which I want to draw on a canvas. Each of these object has a DateTime property, which determines the position of that object along the x-axis on the canvas. Each object also has some other properties which determine the image that need to be drawn on the canvas. The most important feature that I want to implement is that as time passes by the second, these images representing the objects would move along the x-axis. In other words, the right vertical boundary of the canvas would always represent the current time (e.g. DateTime.Now), and objects in the collection would need to update their position on the canvas relative to that boundary. I am very new to Silverlight and hence I have quite a few questions including the following. In addition, I also have the requirement to follow the MVVM framework.
Questions
What should I use in the XAML to achive the above? I thought about using ItemsControl with Canvas as the Panel, but I am not sure how to do it or even whether it is the best way. Any actual XAML code would be great.
How should I bind the collection of objects to the canvas? And if so, how do I move them along the x-axis as time passes? That is, I would like the canvas to update whenever:
there are objects added to the
collection; or
objects removed from the collection;
or
existing object changing (e.g. some
property changed and hence need to
change the image that get shown on
the canvas) in the collection; or
even if there are no changes to the
collection as mentioned above, these
objects will need to move every
second.
Sorry if I have use the wrong terms for anything as I am still new to Silverlight.
Thanks.
I know this question is a little old, but you can just use a render transform - I'm doing something similar;
<ItemsControl ItemsSource="{Binding Notes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="{Binding W}" Height="{Binding H}" BorderBrush="Navy" BorderThickness="5" CornerRadius="10">
<TextBlock Text="{Binding Text}"/>
<Border.RenderTransform>
<TransformGroup>
<... elided ...>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</TransformGroup>
</Border.RenderTransform>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you really want to use MVVM and data-binding, then an ItemsControl with the ItemsPanel defined as a Canvas might just work. Bind the ItemsControl.ItemsSource to an ObservableCollection in your VM. In your ItemTemplate for the ItemsControl, bind the UI item element's Canvas.X and Canvas.Y to your data items, using an IValueConverter in between to do the mapping of DateTime to X coord, etc...
Something like this:
<ItemsControl ItemsSource="{Binding Path=MyItemsInVM, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="50" Height="50" Canvas.Left="{Binding Path=MyDateTimeProperty, Converter={StaticResource DateTimeToLeftOffsetConverter}}" Canvas.Top="100" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Another approach is using the Model-View-Presenter pattern. MVVM is not the only show in town. When you have the need to do a lot of UI manipulation, or work with the VSM, then a Presenter can be a better fit (although Behaviors can also play an important role).
You can set up a timer in your presenter that operates at your refresh interval, and in the Presenter just handle the timer event to iterate over the collection and map objects to (X,Y) positions, updating the UI elements directly.

How to bind ScaleTransformation.X to slider in Silverlight 3

I need to zoom Canvas. In WPF it is possible to bind ScaleTransformation.X to slider.Value.
I'm not able to do the same in Silverlight - some errors.
Is it supported in SL3?
Thank you.
The reason this doesn't work is that in SL3 the binding target needs to be a FrameworkElement. (This restriction is lifted in SL4 but that doesn't help right now).
However the solution just takes a little lateral thinking (or in this case backward thinking). The source object does not need to be a Framework element. So the answer is reverse the binding, that is put the binding on the Slider Value property and put it in to TwoWay mode.
<Border Width="200" Height="200">
<Border.RenderTransform>
<ScaleTransform x:Name="TargetTransform" />
</Border.RenderTransform>
<!-- Some Content Here -->
</Border>
<Slider Value="{Binding ScaleX, ElementName=TargetTransform, Mode=TwoWay}"
Width="200" Canvas.Top="250"
Minimum="0.1" Maximum="2.0" />

Resources