How to get the position of a control in XAML? - wpf

I would like to get the position of a control relative to its parent, or just any kind of position, doesn't really matter. But I want to do this only in XAML without any code behind. I have a grid 5x3, and the button is located in 3rd row and 2nd column. So, basically I would like to get the width of first two rows of the grid, and the width of the first column of the gird.
I want to know the position of the button in pixels, because I want to do translation animation like this:
<DoubleAnimation Storyboard.TargetName="Studio" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" From="500" To="0" Duration="0:0:2">
but instead of using hardcoded value of 500, I want it to be anything that corresponds to the button's horisontal position in pixels

Something like the XAML below might work for you. It is inspired by your idea of a transparent dummy Rectangle that fills the columns left of the Button.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="dummy" Grid.ColumnSpan="2"/>
<Button Grid.Column="2" VerticalAlignment="Center" Content="Click">
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-1"/>
<TranslateTransform/>
<ScaleTransform ScaleX="-1"/>
</TransformGroup>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Children[1].X"
To="{Binding ActualWidth, ElementName=dummy}"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
Instead of declaring the RenderTransform as shown above you may perhaps better use a binding converter to invert the value of the dummy Rectangle's ActualWidth property.

Related

Adding content/text in rectangle WPF

This is my code for rectangle :
<Rectangle x:Name="rect1" Grid.Column="1" Fill="#FF5C626C" HorizontalAlignment="Left" Height="50" Margin="36,171,0,0" StrokeThickness="2" VerticalAlignment="Top" Width="358" RadiusX="30" RadiusY="50">
<Rectangle.Triggers>
</Rectangle.Triggers>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF767C84" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Rectangle.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF5C626C" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
My question is how do i add a text/content in the rectangle ?
One might suggest using a code block, but if u go through my code, youu'll notice that the rectangle changes it's color on mouseover.So if i put a textblock over the rectangle,the mouseover doesn't work properly(as the textblock covers the entire rectangle).
Another suggestion would be to use a border.But i am not sure about this as i need to find the code to apply mouse over effect on a border.
The next suggestion might be to use a button instead.I would've but my rectangle has corner radius and is a bit round-shaped which, if i use a button, would be hard to achieve.
So how do i add content/text inside the rectangle?
If you feel you definitely have to use a rectangle, put it in a grid and add a TextBlock element above it.
By setting the TextBlock's IsHitTestVisible property to False all hit-testing (mouse events) will be ignored on it and fall through to your rectangle.
<Grid>
<Rectangle (...your attributes here...)>
(...your rectangle code here...)
</Rectangle>
<TextBlock Text="Hello World!" IsHitTestVisible="False" />
</Grid>

Access ItemsControl Items and Animate One by One

Today is a good day since I started with WPF, this for a launcher I'm creating.
Using the following code, I managed to get the result to be seen in the screenshot:
<Grid>
<ItemsControl ItemsSource="{Binding Programs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" Background="Transparent" Foreground="White" Width="128" Height="150" >
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Button.RenderTransform>
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Image}" Height="128" />
<ContentPresenter Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
<Rectangle Grid.Row="0" Fill="{TemplateBinding Background}" />
<Rectangle Grid.Row="1" Fill="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Button.Template>
<Button.Resources>
<Storyboard SpeedRatio="4" x:Key="MouseEnterStoryboard" x:Name="MouseEnterStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#22FFFFFF"></ColorAnimation>
</Storyboard>
<Storyboard SpeedRatio="4" x:Key="MouseLeaveStoryboard" x:Name="MouseLeaveStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="Transparent"></ColorAnimation>
</Storyboard>
<Storyboard Duration="00:00:00.05" x:Key="MouseClickStoryboard" AutoReverse="True">
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
</Storyboard>
<Storyboard x:Key="WindowLoadedStoryboard">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:01" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource MouseEnterStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource MouseLeaveStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard Storyboard="{StaticResource WindowLoadedStoryboard}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Screenshot:
Now, for each item in the list bound to this control, it will create a button.
How would I access this button programmatically, better yet, how would I access one of the Storyboards programatically since assigning a name (x:to them simply won't do the trick it seems...
Also, how can I animate the buttons one by one? Currently they each fade in at exact the same time (# WindowLoadedStoryboard), but I would like to let each button fade in one by one with a short delay, to create a nice effect. How would I achieve this?
Hope someone can answer these 2 questions for me!
Greetings!
Your problem with accessing the elements defined in the DataTemplate is caused because you defined those elements in a DataTemplate... those elements could be rendered in many different types of UI container controls. You can find the solution in the How to: Find DataTemplate-Generated Elements page from MSDN.
You first need to get hold of the relevant container control that contains the item that has had that DataTemplate applied to it. Next, you need to get the ContentPresenter from that container control and then you can get the DataTemplate from ContentPresenter. Finally, you can access the named elements from the DataTemplate. From the linked page:
// Getting the currently selected ListBoxItem
// Note that the ListBox must have
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
ContainerFromItem(myListBox.Items.CurrentItem));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock =
(TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);
// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: " +
myTextBlock.Text);

Smooth scroll text in grid column wpf

I have a Grid with 3 columns. I want to scroll a long text, which I update using a ticker in my second column. I tried textblock, but if my text don't fit in, it will cut my string down. Can you recommend me something to do that.
My code looks like this now:
Here are the three column.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
The textblock which I update
<TextBlock Name="SongTitle" Text="Now onair:" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1" TextWrapping="NoWrap">
<TextBlock.RenderTransform>
<TranslateTransform x:Name="translate" />
</TextBlock.RenderTransform>
<TextBlock.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
Here is some problem, it starts to scroll not from the column edge...
<DoubleAnimation
From="300" To="0"
Storyboard.TargetName="translate"
Storyboard.TargetProperty="X"
Duration="0:0:8" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
To start with, instead of using a Grid, you should be using a ScrollViewer. To hide the scrollbars, set HorizontalScrollBarVisibility and VerticalScrollBarVisibility to Hidden. You can then handle mouse wheel events through code-behind to prevent the user from manually scrolling the text.
Of course, it is also possible to use a Grid, but it will give you a bunch of headaches.
To answer your original question, you should be using LayoutTransform instead of RenderTransform.

Quick One regarding Margins and Storyboards

Just a quick question, I've been searching for ages on Google. I have a storyboard:
<Storyboard x:Key="ViewLeftToRight" AccelerationRatio=".5" DecelerationRatio=".5">
<DoubleAnimation Storyboard.TargetName="ReferenceInfo" Storyboard.TargetProperty="Margin" Duration="0:0:0.15" To="{Binding, Width},0,0,0"/>
</Storyboard>
It doesn't work. I was wondering if there is a way for me to bind the Width of the control to the "left" margin. If I need to use a converter, could you possibly show how it would be written in XAML in the above example?
Thanks!
No, you can't bind an animation to Margin, because it does not define a corresponding dependency property. There are several alternatives, here are two:
Place your object in a Canvas and animate Canvas.Left and Canvas.Top
Define a RenderTransform on your object, and animate its X and Y properties.
1)
<Canvas>
<TextBlock Text="test" x:Name="ReferenceInfo" Canvas.Left="0" Canvas.Top="0" />
</Canvas>
Here your storyboard short target the same element ReferenceInfo, but target the attached properties, which you denote using brackets like "(Canvas.Left)":
<DoubleAnimation
Storyboard.TargetName="ReferenceInfo"
Storyboard.TargetProperty="(Canvas.Left)" Duration="0:0:0.15"
To="{Binding Width}"/>
2)
<TextBlock Text="test" x:Name="ReferenceInfo">
<TextBlock.RenderTransform>
<TranslateTransform x:Name="TranslateReferenceInfo" X="0" Y="0" />
</TextBlock.RenderTransform>
</TextBlock>
The animation would then reference the TranslateTransform itself by name:
<DoubleAnimation
Storyboard.TargetName="TranslateReferenceInfo"
Storyboard.TargetProperty="X" Duration="0:0:0.15"
To="{Binding Width}"/>

Bind to animated property (WPF)

Does anybody knows, if there is a way to use a property that is animated as source for a binding?
As far as I found out, is that an animation doesn't "really" set the value on the property, and therefore doesn't fire the changed events, which is needed to trigger the binding.
tia
Martin
I don't know if what you are saying is exactly true. In the XAML below, the TextBlock will display the Width of the Rectangle. When you click on the Rectangle, the Width property is animated from 50 to 300. Along every increment, the TextBlock changes in value. Am I not understanding your question?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding ElementName=Rect,Path=Width}" Grid.Row="0" />
<Rectangle Grid.Row="1"
Name="Rect"
Height="30"
Width="50"
Fill="Blue"
HorizontalAlignment="Left">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
From="50"
To="300"
Duration="0:0:10"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Grid>
Animation does actually really change the value, it does call the property changed callback you pass to DependecyProperty.Register and it does cause a layout/render pass id needed.

Resources