How to dynamically add buttons and Silverlight player in a Grid? - silverlight

I'm basically trying to create the following set of code dynamically/programmatically but I'm unsure of how to do it.
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<smf:SMFPlayer x:Name="player" Grid.Row="0" AutoPlay="False">
<smf:SMFPlayer.Playlist>
<media:PlaylistItem
DeliveryMethod="AdaptiveStreaming"
MediaSource="http://video3.smoothhd.com.edgesuite.net/ondemand/Big%20Buck%20Bunny%20Adaptive.ism/Manifest"/>
<media:PlaylistItem
DeliveryMethod="AdaptiveStreaming"
SelectedCaptionStreamName="textstream_eng"
MediaSource="http://streams.smooth.vertigo.com/elephantsdream/Elephants_Dream_1024-h264-st-aac.ism/manifest"/>
</smf:SMFPlayer.Playlist>
</smf:SMFPlayer>
<StackPanel Grid.Row="1" Orientation="Horizontal" Background="Transparent">
<Button x:Name="test1" Height="30" Width="70" Content="Test 1"/>
<Button x:Name="test2" Height="30" Width="70" Content="Test 2"/>
</StackPanel>
</Grid>
Here's how it looks statically:

First of all, you must give a name to your StackPanel like this;
<StackPanel x:Name="spBottom" Grid.Row="1" Orientation="Horizontal" Background="Transparent">
<Button x:Name="test1" Height="30" Width="70" Content="Test 1"/>
<Button x:Name="test2" Height="30" Width="70" Content="Test 2"/>
</StackPanel>
And then, you must add the following lines in code-behind;
For iLoop As Integer = 0 to 4
Dim btn As New Button With {.Content = "Button" & iLoop}
spBottom.Children.Add(btn)
Next iLoop
I hope this will be help to you!

The controls without an xmlns (XML namespace) prefix can be created in your code-behind without adding any usings. For example, in C# you can recreate the StackPanel from your XAML using the following code:
StackPanel panel = new StackPanel() { Orientation = Orientation.Horizontal, Background = null };
panel.SetValue(Grid.RowProperty, 2);
LayoutRoot.Children.Add(panel);
The elements with an xmlns prefix, anything with a colon, such as <smf: require knowledge of the namespace in the codebehind. The associated namespaces are defined in the first element and look like xmlns:smf="PathToTheNamespace". This namespace is often refrenced in a codebehind file in C# by adding a using PathToTheNamespace statement at the top.

Related

Appium can't locate collapsed element

I'm trying to access the Type property of the custom prompt dialog from an automation test. So the element for the Type (text box or text block) is collapsed because no one needs to see it, I just need it for logical processing on automation side.
I don't understand why it can't be located despite being available in the tree. or is there another why to get such access?
XAML:
<controls:PromptDialog ...
AutomationProperties.AutomationId="PromptView"
d:DataContext="{d:DesignInstance Type=viewModels:PromptViewModel}">
<Grid Margin="{StaticResource MarginThickness}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="{StaticResource Gutter}" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Prompt-->
<Grid Grid.Row="0"
Visibility="{Binding IsShowingPrompt, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="{StaticResource Gutter}" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!--Image-->
<ContentControl Grid.Row="0"
Grid.Column="0"
Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewModels:PromptViewModel}">
<Image Name="Image" />
//displaying image per type
</ContentControl.Resources>
</ContentControl>
<ScrollViewer Grid.Row="0"
Grid.Column="2"
Focusable="False"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
MaxHeight="400">
<StackPanel>
<TextBlock behaviors:TextBoxHyperlinkBehavior.Text="{Binding Text}"
TextWrapping="Wrap"
Focusable="False"
VerticalAlignment="Center"
HorizontalAlignment="Left"
FontFamily="{Binding Font.Name}"
Foreground="{Binding FontColor}"
AutomationProperties.AutomationId="PrompView_Text" />
<TextBox Visibility="Collapsed"
Text="{Binding Type}"
AutomationProperties.AutomationId="PromptView_Type" />
</StackPanel>
</ScrollViewer>
<!--Commands-->
<UniformGrid Grid.Row="2" Rows="1" HorizontalAlignment="Right">
// buttons
</UniformGrid>
</Grid>
</controls:PromptDialog>
Appium:
public IVisualElement Type => _appiumSession.CreateVisualAppiumElement("PromptView_Type");
Or
public AppiumWebElement Type => _appiumSession.FindElementByAccessibilityId("PromptView_Type");
WPF Snoop:
Error:
OpenQA.Selenium.WebDriverTimeoutException : Timed out after 10 seconds
---- OpenQA.Selenium.WebDriverException : An element could not be located on the page using the given search parameters.
Any pointers are highly appreciated,
thanks
There were several times when I used Snoop and it found more elements/attributes that Appium can access.
That is why now I am using Accessibility Insights for Windows. Of course you can use inspect.exe, but Accessibility Insights for Windows is the recommended from Microsoft. You can also try WinAppDriver UI Recorder that is generating XPaths.
If your element is not present in Accessibility Insights for Windows Appium will not be able to locate it.
According to documentation:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.visibility?view=net-5.0
Collapsed elements are not rendered and NOT in layout.
Why not to use Visibility.Hidden instead, or Visibility.Visible with height of 0px.
I don't know anything about Appium, but the problem could be solve with .net automation api very easily.
// Must find top window of app first or could cause overflow easily.
var topWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.AutomationIdProperty, "Id of top window"));
// Find out your target element if it's in visual tree, whatever it's collapsed or not.
// You can also Find out another element in the middle first to reduce the search work.
var target = topWindow.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, "PromptView_Type"));
// retrieve the value.
var val = ((ValuePattern)target.GetCurrentPattern(ValuePattern.Pattern)).Current.Value;
You have to add reference to UIAutomationClient.dll and UIAutomationType.dll to make this code work.
Update for .net core app
For .net core(3.0, 3.1, 5.0), there's a huge difference about how an automation peer answer inquiry than .net framework app. Automation peer will not answer inquiry if it's offscreen(control's visibility had been set to collapsed or hidden) unless inquiry was targeting raw view. So we have to force inquiry target to make it works.
var topWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Id of top window"));
AutomationElement target = null;
var cr = new CacheRequest()
{
TreeFilter = Automation.RawViewCondition,
};
using (cr.Activate())
{
target = topWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "PromptView_Type"));
}

TextBlock1 is not declared. It may be inaccessible due to its protection level

As silly as this sounds I'm a little stumped at this one. Here's my XAML in a Win Phone 8 App:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Page" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
I've searched around but I don't know why I can't write code against the TextBlock1 control in code behind. When I type TextBlock1.Text= .... I get the error TextBlock1 is not declared. It may be inaccessible due to its protection level. But I can't see how it is private?
All I'm trying to do is add a textblock, assign some content to it, and then that selected value is passed across another page to perform relevant action.
In addition as soon as I remove it outside of the PhoneListSelector I can access it.
TextBlock1 is defined inside an ItemTemplate, anything defined a Template cannot be access directly as it will be created on runtime by the control.
You probably need to do binding on the TextBlock if you want to manipulate anything that the LongListSelector's DataContext has.
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" Text="{Binding Content"} HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
MainLongListSelector.DataContext = new List<TestViewModel>();
public class TestViewModel : INotifyPropertyChanged
{
//Assuming you've implemented the interface
private string _content;
public string Content { get { return _content; } { set { _content = value; NotifyOfPropertyChanged("Content"); } }
}
From here, you can try to access the selected value content and pass that to the next page.
var selectedItem = MainLongListSelector.SelectedItem as TestViewModel;
GoToNextPage(selectedItem.Content);
I strongly suggest to read MVVM design pattern and everything should be easy for you to implement, always remember UI is not DATA it's responsibility is only to show something that is passed through the ViewModel.

scrollviewer the property 'content' is set more than once

Here my code:
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer
Grid.RowSpan="2" Style="{StaticResource HorizontalScrollViewerStyle}"
HorizontalScrollBarVisibility="Visible" >
<!-- The elements you want to be horizontally scrollable goes here -->
<!-- Horizontal scrolling grid used in most view states -->
<GridView
Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Padding="100,136,116,46"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"/>
<Button Hello /> <!-- From here come the Error -->
</ScrollViewer>
</Grid>
When I add any thing after my GridView, it give me that error (the property content is set more than once).
The answer is right there in the error message. A ScrollViewer can only have one child (you have two, a GridView and a Button). If you want to add multiple things, you will have to wrap them in a panel that allows multiple children (e.g. Grid).

Srange result about wpf grid row removal

I have a question about grid removal:
whenever I delete the last row or all rows in the grid, there is still something(actually the last row) displayed on the grid. I did actually checked the grid.rowdefinitions.count is 0. the method I used is removeAt and removeRange(0, grid.rowdefinitions.count). what's wrong with that? Can anybody tell why? Many thanks!
//------------
<Grid DockPanel.Dock="Top" HorizontalAlignment="Left" Name="grid1" ShowGridLines="true" Width="200" Height="200">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="100" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="200" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="300" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="400" />
<TextBlock Grid.Row="4" Grid.Column="0" Text="500" />
</Grid>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Width="200" DockPanel.Dock="Top">
<Button Width="80" Click="rem5Row">Remove 5 Rows</Button>
</StackPanel>
//--------------
private void rem5Row(object sender, RoutedEventArgs e)
{
if (grid1.RowDefinitions.Count < 5)
{
Console.WriteLine("less than 5!");
}
else
{
grid1.RowDefinitions.RemoveRange(0,grid1.RowDefinitions.Count);
}
}
I'm not sure I understand your question correctly (and I'm not sure the code reflects what you say you're doing). But: how do you know the "final row" is displaying?
What I suspect is happening is this: you are removing rows and expecting the controls in those rows to be removed also. However, this is not how a WPF Grid functions: the controls are not the child of their row, and they are not being removed when you remove the RowDefinition.
So, all of the child controls will still be rendering but, because their Grid.Row is invalid, they will render in the "Row 0" position. They are rendered in markup-order, so the final result will appear as though the "last row" is being displayed because this is the control that's drawn last. In fact, all of your controls are still present; they're just all in the same place on screen.
If you want to confirm this, you could use Snoop to examine your runtime UI. Alternatively, set widths for your textboxes so that the row 0 box is wider - you will see the overlap clearly then.

Text trimming in the beginning of the string

I have an application in WPF and I want see in my textboxes only the end of the string.
XAML:
<Grid Height="109" Width="126">
<Grid.RowDefinitions>
<RowDefinition Height="166*" />
<RowDefinition Height="145*" />
</Grid.RowDefinitions>
<TextBlock Text="10000004" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" Width="40" Background="LightBlue"/>
<TextBlock Text="10000005" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" Width="40" Grid.Row="1" Background="LightGreen"/>
</Grid>
As you probably have seen on MSDN, you will need to create/extend with your own TextTrimmingProperty dependency property.
Look at this to extend your TextBlock so that you can create your own TextTrimmingProperty to work differently.
int Len = 4; //put this equcal to how many digits you need from last...
//path is the actual string
string endText = path.Substring(path.Length - Len, path.Length)

Resources