Incorrect UI element rendering while using viewbox and canvas - windows-8

I am creating an app which uses both the orientation portrait & landscape. The app contains root as viewbox and the children of it is scrollviewer. Scrollviewer contains the canvas.
Now the current visible (bound) part of canvas is called two page (horizontally consecutive). User can put other UI elements on it and create a form. Now when the orientation is changed to portrait the bound will have two pages as vertically consecutive. Apart from it the UI elements should also move. See the below screenshot which shows what I want to achieve.
I tried this to achieve by using first determining the page number (from current X and page size) and relative X Y position (relative from single page edge). I am getting values mathematically correct but rending happens wrong. Can any one tell me why this is happening ?
UPDATE 1
Here's my code.
XAML
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Button Content="Click" Click="Button_Click_1" />
<Viewbox x:Name="vBox" Stretch="Fill" StretchDirection="Both" Grid.Row="1" Grid.Column="1">
<ScrollViewer x:Name="sv" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<Canvas x:Name="MyCanvas" Background="Aqua" Height="768" Width="1366">
<Rectangle x:Name="sepertor1" Fill="Red" Width="20" Height="768" Canvas.Left="546.4" />
<Rectangle x:Name="sepertor2" Fill="Red" Width="20" Height="768" Canvas.Left="1092.8" />
<Rectangle x:Name="sepertor3" Fill="Red" Width="20" Height="768" Canvas.Left="1639.2" />
<Rectangle x:Name="sepertor4" Fill="Red" Width="20" Height="768" Canvas.Left="2165.6" />
<TextBox x:Name="txtFirst" Height="30" Width="200" Text="First" Canvas.Left="326.4" Canvas.Top="518" />
<TextBox x:Name="txtSecond" Height="30" Width="200" Text="Second" Canvas.Left="566.4" Canvas.Top="0"/>
<TextBox x:Name="txtThird" Height="30" Width="200" Text="Third" Canvas.Left="1439.2" Canvas.Top="0"/>
<TextBox x:Name="txtForth" Height="30" Width="200" Text="Forth" Canvas.Left="1985.6" Canvas.Top="518"/>
</Canvas>
</ScrollViewer>
</Viewbox>
</Grid>
C#
//This is page's SizeChanged event
bool IsFirst = true;
private void Page_SizeChanged_1(object sender, SizeChangedEventArgs e)
{
int PageNumber;
if (ApplicationView.Value == ApplicationViewState.FullScreenLandscape)
{
sv.Height = Window.Current.Bounds.Height - 220;
sv.Width = vBox.Width = Window.Current.Bounds.Width * 0.8;
//Canvas contains red seperator (width = 20) which shows two page in current window bound.
//canvas width is of Window.Current.Bounds.Width * 0.8, so page size is Window.Current.Bounds.Width * 0.4
//txtFirst is in first page & txtSecond is in second page.
var PageWidth = Window.Current.Bounds.Width * 0.4; //1366 * 0.4
var PageHeight = Window.Current.Bounds.Height - 220; //768 - 220
vBox.Stretch = Stretch.Fill;
MyCanvas.Width = PageWidth * 4;
MyCanvas.Height = PageHeight;
if (!IsFirst)
{
IsFirst = false;
foreach (FrameworkElement control in MyCanvas.Children.Where(x => x.GetType() != typeof(Rectangle)).ToList())
{
var CurrentHeight = control.Height;
var CurrentLeft = Canvas.GetLeft(control);
var CurrentTop = Canvas.GetTop(control);
var RelativeX = CurrentLeft % PageWidth;
PageNumber = (int)Math.Ceiling(CurrentLeft / PageWidth);
if (PageNumber > 1)
{
if (PageNumber % 2 == 0) //even 2, 4, 6, 8
{
control.SetValue(Canvas.LeftProperty, CurrentLeft + (PageNumber / 2) * PageWidth);
control.SetValue(Canvas.TopProperty, CurrentTop - PageHeight + CurrentHeight);
}
else //odd 1, 3, 7, 5
{
//control.SetValue(Canvas.LeftProperty, (CurrentLeft - PageWidth) * 2);
control.SetValue(Canvas.LeftProperty, ((CurrentLeft - RelativeX) * 2) + RelativeX);
}
}
}
}
}
else if (ApplicationView.Value == ApplicationViewState.FullScreenPortrait)
{
IsFirst = false;
sv.Height = Window.Current.Bounds.Height - 220;
sv.Width = vBox.Width = Window.Current.Bounds.Width * 0.8;
vBox.Stretch = Stretch.None;
var PageWidth = Window.Current.Bounds.Height * 0.4; //1366 * 0.4
var PageHeight = Window.Current.Bounds.Width - 220; //768 - 220
MyCanvas.Width = PageWidth * 2;
MyCanvas.Height = PageHeight * 2;
foreach (FrameworkElement control in MyCanvas.Children.Where(x => x.GetType() != typeof(Rectangle)).ToList())
{
var CurrentHeight = control.Height;
var CurrentLeft = Canvas.GetLeft(control);
var CurrentTop = Canvas.GetTop(control);
var RelativeX = CurrentLeft % PageWidth;
PageNumber = (int)Math.Ceiling(CurrentLeft / PageWidth);
if (PageNumber > 1)
{
if (PageNumber % 2 == 0) //even 2, 4, 6, 8, 10
{
//control.SetValue(Canvas.LeftProperty, CurrentLeft - (PageNumber / 2) * PageWidth); this is also working
control.SetValue(Canvas.LeftProperty, ((CurrentLeft - RelativeX) / 2));
control.SetValue(Canvas.TopProperty, CurrentTop + PageHeight - CurrentHeight);
}
else //odd 1, 3, 5, 7, 9
{
//control.SetValue(Canvas.LeftProperty, (CurrentLeft + PageWidth) / 2);
control.SetValue(Canvas.LeftProperty, ((CurrentLeft - RelativeX) / 2) + RelativeX);
}
}
}
}
}

Related

UWP: how to reflow controls based on grid width

I have two buttons placed horizontally. They are inside a grid. This grid containing two buttons has width 370. When the text on the button becomes large, it needs width more than 370. So what I want to do is, instead of placing then horizontally, I want to place them vertically dynamically when text will start cropping. Basically, I want auto-reflow behavior inside this grid for these two buttons based on width of the grid (not based on width of main window). So I want them to fit in 370 width and if they cannot, I want them to place themselves vertically. How can I achieve this?
I explored GridView but it will show buttons inside the box that comes with GridView so I do not want extra UI that comes with GridView unless we have an option to hide it?
I checked AdaptiveTrigger but that is based on width of window not of the control (grid in this case)
<Grid
Grid.Row="2"
Margin="0,36,0,28"
Width="370"
HorizontalAlignment="Left"
VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
Grid.Column="0"
CornerRadius="3">
<Button
MinWidth="118"
MinHeight="30"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
AutomationProperties.Name="{x:Bind ViewModel.PrimaryActionAutomationName, Mode=OneWay}"
BorderThickness="1"
Click="{x:Bind ViewModel.InvokePrimaryAction}"
Content="{x:Bind ViewModel.PrimaryAction, Mode=OneWay}"
CornerRadius="3"
Style="{StaticResource AccentButtonStyle}" />
</Grid>
<Grid
Grid.Column="1"
CornerRadius="3"
Margin="32,0,0,0">
<HyperlinkButton
AutomationProperties.Name="{x:Bind ViewModel.SecondaryLinkAutomationName, Mode=OneWay}"
AutomationProperties.AutomationId="{Binding AutomationId, ConverterParameter=HyperlinkButton, Converter={StaticResource AutomationIdConverter}}"
Content="{x:Bind ViewModel.SecondaryText, Mode=OneWay}"
FontSize="14"
Margin="0,0,0,0"
Style="{StaticResource HyperlinkButtonStyle}"
NavigateUri="{x:Bind ViewModel.SecondaryLink, Mode=OneWay}" />
</Grid>
</Grid>
For your scenario, I'd suggest you custom a StateTrigger based on the StateTriggerBase Class to monitor the width of the Grid and apply visual states based on the width.
I've made a simple sample that you could refer to.
ControlSizeTrigger:
public class ControlSizeTrigger : StateTriggerBase
{
//private variables
private double _minHeight, _minWidth = -1;
private FrameworkElement _targetElement;
private double _currentHeight, _currentWidth;
//public properties to set from XAML
public double MinHeight
{
get
{
return _minHeight;
}
set
{
_minHeight = value;
}
}
public double MinWidth
{
get
{
return _minWidth;
}
set
{
_minWidth = value;
}
}
public FrameworkElement TargetElement
{
get
{
return _targetElement;
}
set
{
if (_targetElement != null)
{
_targetElement.SizeChanged -= _targetElement_SizeChanged;
}
_targetElement = value;
_targetElement.SizeChanged += _targetElement_SizeChanged;
}
}
//Handle event to get current values
private void _targetElement_SizeChanged(object sender, SizeChangedEventArgs e)
{
_currentHeight = e.NewSize.Height;
_currentWidth = e.NewSize.Width;
UpdateTrigger();
}
//Logic to evaluate and apply trigger value
private void UpdateTrigger()
{
//if target is set and either minHeight or minWidth is set, proceed
if (_targetElement != null && (_minWidth > 0 || _minHeight > 0))
{
//if both minHeight and minWidth are set, then both conditions must be satisfied
if (_minHeight > 0 && _minWidth > 0)
{
SetActive((_currentHeight >= _minHeight) && (_currentWidth >= _minWidth));
}
//if only one of them is set, then only that condition needs to be satisfied
else if (_minHeight > 0)
{
SetActive(_currentHeight >= _minHeight);
}
else
{
SetActive(_currentWidth >= _minWidth);
bool bbb = _currentWidth >= _minWidth;
Debug.WriteLine("Widthtrigger :" + bbb);
}
}
else
{
SetActive(false);
}
}
}
MainPage.xaml:
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ControlSizeStates">
<VisualState x:Name="SizeChange">
<VisualState.StateTriggers>
<local:ControlSizeTrigger MinWidth="800" TargetElement="{x:Bind Path=MyStackPanel}" />
<!--<AdaptiveTrigger MinWindowHeight="500" />-->
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MyStackPanel.Background" Value="Red" />
<Setter Target="MyStackPanel.Orientation" Value="Horizontal" />
<Setter Target="MyButton.Foreground" Value="Black" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel x:Name="MyStackPanel" Width="100" Height="200" Background="AliceBlue" Orientation="Vertical">
<Button x:Name="MyButton" Foreground="Red" Content="Click" Click="Button_Click"/>
<Button x:Name="MyButton2" Foreground="Red" Content="Click" />
</StackPanel>
</Grid>
MainPage.cs:
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MyStackPanel.Width = 1100;
}

Render/draw child of Scrollview outside Scrollview area

Im implementing a UI in Windows 10 (UWP) with elements that can be moved using drag and drop from a menu area to a ScrollView area, it should also be possible to move the elements back to the menu area from the ScrollView. When a element is moved from the menu to the ScrollView the element is removed as child of the menu and added as a child to a child of the ScrollView.
But when I try to move the elements back they render behind the menu. I have played around with z index and the order the elements are in the XAML and tested to remove and re add the ScrollView at run-time to put it at top, but with no luck.
Seams like ScrollView children does not draw outside the view? Any suggestions on how to solve this?
Below is some sample code that illustrates the issue:
<Page x:Class="ScrollViewTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ScrollViewTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent"
Height="200"
Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1"
Canvas.ZIndex="1"
Background="Transparent">
<Canvas Background="Transparent"
Height="0"
Width="0"
VerticalAlignment="Top"
HorizontalAlignment="Left">
<Border Canvas.Left="-25"
Canvas.Top="100"
BorderThickness="2"
BorderBrush="Red"
Width="50"
Height="50"></Border>
</Canvas>
</Grid>
<ScrollViewer Grid.Column="0"
VerticalScrollMode="Enabled"
Canvas.ZIndex="2"
Background="Transparent">
<Canvas Background="Transparent"
Height="0"
Width="0"
VerticalAlignment="Top"
HorizontalAlignment="Left">
<Border Canvas.Left="75"
BorderThickness="2"
BorderBrush="Blue"
Width="50"
Height="50"></Border>
</Canvas>
</ScrollViewer>
</Grid>
And the result, I want the blue border to be shown on top of the grid
Seems like ScrollView children does not draw outside the view?
This is because the Content of Grid has a limited size which is exactly the size of itself, but the content of ScrollViewer has no limited size since it is scroll-able, not like a Grid, the child element of a ScrollViewer will always be rendered inside of it.
..., I want the blue border to be shown on top of the grid
To be honestly, I think it is not the right direction to use Canvas.ZIndex to implement the drag and drop gesture between different parent. A Canvas.ZIndex value is interpreted by the most immediate parent Canvas element from where the value is set. The value is used to explicitly define the draw order in cases where child elements overlap. In this case, your Grid and ScrollViewer have the same parent, but the Canvass have different parent.
Here I can give you a method to implement this function:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="rootGrid"
PointerPressed="rootGrid_PointerPressed"
PointerReleased="rootGrid_PointerReleased">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Canvas x:Name="canvas" Grid.Column="1" Background="Wheat">
<Border BorderThickness="2" BorderBrush="Red" Width="50" Height="50" />
<Ellipse Width="50" Height="50" VerticalAlignment="Top" Fill="Red" />
</Canvas>
<ScrollViewer x:Name="scrollViewer" Grid.Column="0"
VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Hidden"
Background="LightBlue">
<Canvas Width="100" x:Name="canvasInsideScrollViewer">
<Border BorderThickness="2" BorderBrush="Blue" Width="50" Height="50" VerticalAlignment="Bottom" />
</Canvas>
</ScrollViewer>
</Grid>
Code behind:
public Page20()
{
this.InitializeComponent();
this.Loaded += Page20_Loaded;
}
private void Page20_Loaded(object sender, RoutedEventArgs e)
{
gridRect = canvas.TransformToVisual(rootGrid).TransformBounds(new Rect(0.0, 0.0, canvas.ActualWidth, canvas.ActualHeight));
scrollViewerRect = scrollViewer.TransformToVisual(rootGrid).TransformBounds(new Rect(0.0, 0.0, scrollViewer.ActualWidth, scrollViewer.ActualHeight));
}
private Rect gridRect;
private Rect scrollViewerRect;
private FrameworkElement MoveElement;
private void rootGrid_PointerPressed(object sender, PointerRoutedEventArgs e)
{
var pointer = e.GetCurrentPoint(rootGrid);
if (gridRect.Left <= pointer.Position.X && pointer.Position.X <= gridRect.Right &&
gridRect.Top <= pointer.Position.Y && pointer.Position.Y <= gridRect.Bottom)
{
foreach (var childElement in canvas.Children)
{
var element = childElement as FrameworkElement;
Rect childBounds = element.TransformToVisual(rootGrid).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
if (childBounds.Left <= pointer.Position.X && pointer.Position.X <= childBounds.Right &&
childBounds.Top <= pointer.Position.Y && pointer.Position.Y <= childBounds.Bottom)
{
MoveElement = element;
canvas.Children.Remove(element);
}
}
}
else if (scrollViewerRect.Left <= pointer.Position.X && pointer.Position.X <= scrollViewerRect.Right &&
scrollViewerRect.Top <= pointer.Position.Y && pointer.Position.Y <= scrollViewerRect.Bottom)
{
foreach (var childElement in canvasInsideScrollViewer.Children)
{
var element = childElement as FrameworkElement;
Rect childBounds = element.TransformToVisual(rootGrid).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
if (childBounds.Left <= pointer.Position.X && pointer.Position.X <= childBounds.Right &&
childBounds.Top <= pointer.Position.Y && pointer.Position.Y <= childBounds.Bottom)
{
MoveElement = element;
canvasInsideScrollViewer.Children.Remove(element);
}
}
}
}
private void rootGrid_PointerReleased(object sender, PointerRoutedEventArgs e)
{
var pointer = e.GetCurrentPoint(rootGrid);
if (MoveElement != null)
{
if (gridRect.Left <= pointer.Position.X && pointer.Position.X <= gridRect.Right &&
gridRect.Top <= pointer.Position.Y && pointer.Position.Y <= gridRect.Bottom)
{
var canvasPointer = e.GetCurrentPoint(canvas);
canvas.Children.Add(MoveElement);
Canvas.SetLeft(MoveElement, canvasPointer.Position.X);
Canvas.SetTop(MoveElement, canvasPointer.Position.Y);
}
else if (scrollViewerRect.Left <= pointer.Position.X && pointer.Position.X <= scrollViewerRect.Right &&
scrollViewerRect.Top <= pointer.Position.Y && pointer.Position.Y <= scrollViewerRect.Bottom)
{
var scrollviewPointer = e.GetCurrentPoint(canvasInsideScrollViewer);
canvasInsideScrollViewer.Children.Add(MoveElement);
Canvas.SetLeft(MoveElement, scrollviewPointer.Position.X);
Canvas.SetTop(MoveElement, scrollviewPointer.Position.Y);
}
}
MoveElement = null;
}
As you can see, I changed the Grid in the second column to Canvas, so the element can be rendered in the absolute position as your mouse point. Here is the rendering image of my demo:
Here the most confusing part is that you want to translate UIElement between different parent controls, but if you put your Borders (which you want to drag and drop) also in the rootGrid, your Grid, ScrollViewer, Borders will have the same parent, then you can follow CustomBehaviorControl of XAMLBehaviorsSample to complete the drag-and-drop work.

Horizontally stretch items with horizontal orientation

I'm looking to show a dynamic number of buttons in a horizontal way, so that they always fill up the space horizontally no matter how many items.
For example, when there are two button
__________________________________
| other controls |
| |
|________________________________|
| Button 1 | Button 2 | Button 3 |
----------------------------------
and button 2 gets hidden(collapsed), this should become
__________________________________
| other controls |
| |
|________________________________|
| Button 1 | Button 3 |
----------------------------------
Is this possible with Winrt/WP 8.1 xaml?
I would work with a Grid and set the column width to zero in order to hide it. Since all other columns have star values they should stretch accordingly. If everything fails you can recalculate the star values manually since you know the number of visible buttons.
Sample:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Foo" Margin="0,0,0,0" HorizontalAlignment="Stretch" />
<Button Grid.Column="1" Content="Foo" Margin="0,0,0,0" HorizontalAlignment="Stretch" />
<Button Grid.Column="2" Content="Foo" Margin="0,0,0,0" HorizontalAlignment="Stretch" />
</Grid>
Set the ColumnDefinition of one column to zero and the button is hidden and the other columns adjust accordingly.
You can always provide your own custom layout logic by deriving from Panel. Here's what I came up with:
StretchPanel.cs
class StretchPanel : Panel
{
#region Properties
public bool EqualWidths
{
get { return (bool)GetValue(EqualWidthsProperty); }
set { SetValue(EqualWidthsProperty, value); }
}
public static readonly DependencyProperty EqualWidthsProperty =
DependencyProperty.Register("EqualWidths", typeof(bool), typeof(StretchPanel), new PropertyMetadata(false, onEqualWidthsChanged));
#endregion
static void onEqualWidthsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var panel = (StretchPanel)d;
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
protected override Size MeasureOverride(Size availableSize)
{
var renderedChildren = Children.Where(c => c.Visibility == Visibility.Visible);
var count = renderedChildren.Count();
Size childAvailableSize = availableSize;
if (EqualWidths)
childAvailableSize = new Size(availableSize.Width / count, availableSize.Height);
foreach (var child in renderedChildren)
child.Measure(childAvailableSize);
var totalHeight = renderedChildren.Max(c => c.DesiredSize.Height);
return new Size(availableSize.Width, totalHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
var renderedChildren = Children.Where(c => c.Visibility == Visibility.Visible);
var count = renderedChildren.Count();
var equalWidth = finalSize.Width / count;
var totalWidth = renderedChildren.Sum(c => c.DesiredSize.Width);
var totalHeight = renderedChildren.Max(c => c.DesiredSize.Height);
var x = 0.0;
foreach (var child in renderedChildren)
{
var r = new Rect();
r.X = x;
r.Y = 0;
r.Width = EqualWidths ? equalWidth : child.DesiredSize.Width / totalWidth * finalSize.Width;
r.Height = child.DesiredSize.Height;
child.Arrange(r);
x += r.Width;
}
return new Size(finalSize.Width, totalHeight);
}
}
You can use it like so:
<Page
x:Class="App19.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App19"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="MinWidth" Value="0" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="Margin" Value="0,10,0,0" />
</Style>
</Page.Resources>
<StackPanel>
<TextBlock>Different widths</TextBlock>
<local:StretchPanel EqualWidths="False">
<Button>a</Button>
<Button>big</Button>
<Button>purple</Button>
<Button>dishwasher</Button>
</local:StretchPanel>
<TextBlock>Equal widths</TextBlock>
<local:StretchPanel EqualWidths="True">
<Button>a</Button>
<Button>big</Button>
<Button>purple</Button>
<Button>dishwasher</Button>
</local:StretchPanel>
<TextBlock>Different widths, one child hidden</TextBlock>
<local:StretchPanel EqualWidths="False">
<Button>a</Button>
<Button>big</Button>
<Button Visibility="Collapsed">purple</Button>
<Button>dishwasher</Button>
</local:StretchPanel>
<TextBlock>Equal widths, one child hidden</TextBlock>
<local:StretchPanel EqualWidths="True">
<Button>a</Button>
<Button>big</Button>
<Button Visibility="Collapsed">purple</Button>
<Button>dishwasher</Button>
</local:StretchPanel>
</StackPanel>
</Page>
And a screenshot:

Get all controls id of silverlight page

I want to get all control's id of silverlight page.
So I want to iterate through all controls inside xaml page.
For this I have used following code:
private List<UIElement> GetElement(UIElement parent, Type targetType)
{
List<UIElement> items = new List<UIElement>();
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child.GetType() == targetType)
{
items.Add(child);
}
}
}
return items;
}
and I call above function on my button click event as below.
List<UIElement> lsttexts = GetElement(LayoutRoot, typeof(System.Windows.Controls.SLFramework.UniTextBox));
List<UIElement> lstlabels = GetElement(LayoutRoot, typeof(TextBlock));
List<UIElement> lstbtn = GetElement(LayoutRoot, typeof(Button));
List<UIElement> lstcombo = GetElement(LayoutRoot, typeof(ComboBox));
List<UIElement> lstStackpanel = GetElement(LayoutRoot, typeof(StackPanel));
Demo Example of my page:
<TextBlock Text="Name : " Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" />
<StackPanel x:Name="stkpnl" Grid.Row="0" Grid.Column="1" Orientation="Vertical">
<StackPanel x:Name="childpanel1" Orientation="Horizontal">
<TextBlock x:Name="lbl1" Text="Name : " HorizontalAlignment="Left" VerticalAlignment="Center" />
<TextBox x:Name="txt1" Text="=test1"></TextBox>
</StackPanel>
<StackPanel x:Name="childpanel11" Orientation="Horizontal">
<TextBlock x:Name="lbl11" Text="Name : " HorizontalAlignment="Left" VerticalAlignment="Center" />
<TextBox x:Name="txt11" Text="=test11"></TextBox>
</StackPanel>
</StackPanel>
It get all textbol and textbox outside the satckpanel, listitem and tab control.
But I want to get all control of page includiing which are inside TabControl, statckpanel and Listitem.
Thanks

Bind a Path Stroke color to Foreground

Using the TabControl element for Silverlight in Blend I created the following markup:
<controls:TabControl>
<controls:TabItem Header="TabItem" Style="{StaticResource TabItemStyle1}" />
<controls:TabItem Style="{StaticResource TabItemStyle1}">
<controls:TabItem.Header>
<StackPanel Orientation="Horizontal">
<Path Data="M0,14L0,6 5,0 10,6 10,14 0,6 10,6 0,14 10,14"
StrokeLineJoin="Round" Margin="0 0 6 0"
Stroke="Black"/>
<TextBlock Text="TabItem"/>
</StackPanel>
</controls:TabItem.Header>
</controls:TabItem>
</controls:TabControl>
TabItemStyle1 is a copy of the default style of a TabItem.
I altered TabItemStyle1 by adding a color animation in the MouseOver storyboard so that unselected tab items become red when the mouse hovers them:
<ColorAnimation BeginTime="0" Duration="00:00:00.001"
Storyboard.TargetName="HeaderTopUnselected"
Storyboard.TargetProperty="(UIElement.Foreground).(SolidColorBrush.Color)"
To="Red" />
Now when I hover the second tab, the text becomes red but the Path remains black:
How should I define the Path Stroke color to make it follow the same rule?
The following should work:
<controls:TabControl>
<controls:TabItem Header="TabItem" Style="{StaticResource TabItemStyle1}" />
<controls:TabItem Style="{StaticResource TabItemStyle1}">
<controls:TabItem.Header>
<StackPanel Orientation="Horizontal">
<Path Data="M0,14L0,6 5,0 10,6 10,14 0,6 10,6 0,14 10,14"
StrokeLineJoin="Round" Margin="0 0 6 0"
Stroke="{Binding ElementName=textBlock, Path=Foreground}"/>
<TextBlock x:Name="textBlock" Text="TabItem"/>
</StackPanel>
</controls:TabItem.Header>
</controls:TabItem>
</controls:TabControl>
it's not a perfect solution but you could use this
<sdk:TabControl>
<sdk:TabItem Header="item1"></sdk:TabItem>
<sdk:TabItem Foreground="Red" x:Name="someNameForTheTab">
<sdk:TabItem.Header>
<StackPanel Orientation="Horizontal">
<!--Just set stroke binding to the foreground of the tabItem-->
<Path Stroke="{Binding Foreground, ElementName=someNameForTheTab}" Data="M0,14L0,6 5,0 10,6 10,14 0,6 10,6 0,14 10,14"
StrokeLineJoin="Round" Margin="0 0 6 0"/>
<TextBlock Text="item2"/>
</StackPanel>
</sdk:TabItem.Header>
</sdk:TabItem>
</sdk:TabControl>
Try binding to the TemplatedParent like this:
<Path
Data="M0,14L0,6 5,0 10,6 10,14 0,6 10,6 0,14 10,14"
StrokeLineJoin="Round"
Margin="0 0 6 0"
Stroke="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"/>
I haven't tested this, but give it a whirl and let me know. If it doesn't work, try this:
<Path Data="M0,14L0,6 5,0 10,6 10,14 0,6 10,6 0,14 10,14" StrokeLineJoin="Round" Margin="0 0 6 0">
<Path.Stroke>
<SolidColorBrush Color="{Binding Foreground.Color, RelativeSource={RelativeSource TemplatedParent}}" />
</Path.Stroke>
</Path>
I have a feeling that the Color property needs to be the source of binding, not the actual brush.
I made it work by binding the header content brushes to {TemplateBinding TextElement.Foreground}.
In other cases I used standard property binding with converters, for example if I had to adapt element's brushes to item state.
// animazione periferica
public static void LineAnimation(Line _line,String _colore)
{
Storyboard result = new Storyboard();
Duration duration = new Duration(TimeSpan.FromSeconds(2));
ColorAnimation animation = new ColorAnimation();
animation.RepeatBehavior = RepeatBehavior.Forever;
animation.Duration = duration;
switch (_colore.ToUpper())
{
case "RED":
animation.From = Colors.Red;
break;
case "ORANGE":
animation.From = Colors.Orange;
break;
case "YELLOW":
animation.From = Colors.Yellow;
break;
case "GRAY":
animation.From = Colors.DarkGray;
break;
default:
animation.From = Colors.Green;
break;
}
animation.To = Colors.Gray;
Storyboard.SetTarget(animation, _line);
Storyboard.SetTargetProperty(animation, new PropertyPath("(Line.Stroke).(SolidColorBrush.Color)"));
result.Children.Add(animation);
result.Begin();
}
}
//**********************************************
public partial class MainPage : UserControl
{
public Line _line;
public MainPage()
{
InitializeComponent();
Canvas.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
Canvas.MouseLeftButtonUp += Canvas_MouseLeftButtonUp;
}
void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_line.X2 = e.GetPosition(this.Canvas).X;
_line.Y2 = e.GetPosition(this.Canvas).Y;
_line.Loaded += _line_Loaded;
Canvas.Children.Add(_line);
}
void _line_Loaded(object sender, RoutedEventArgs e)
{
Cls_Barriere.LineAnimation(sender as Line, "RED");
}
void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_line = new Line();
_line.Stroke = new SolidColorBrush(Colors.White);
_line.StrokeThickness = 5;
_line.StrokeStartLineCap = PenLineCap.Round;
_line.StrokeEndLineCap = PenLineCap.Round;
_line.StrokeDashCap = PenLineCap.Round;
_line.X1 = e.GetPosition(this.Canvas).X;
_line.Y1= e.GetPosition(this.Canvas).Y;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
}
}