Select ImageBrush using VisualTreeHelper - c#

I am trying to select ImageBrush items from a LongListSelector. As the ImageBrushes are inside a DataTemplate, I am selecting them using VisualTreeHelper.
My sample xaml code:
<phone:LongListSelector Grid.Row="0" Name="AllPhotosListBox"
SelectionChanged="AllPhotosListBox_SelectionChanged"
ItemsSource="{Binding PhotoItems}"
LayoutMode="Grid" GridCellSize="112, 112">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Border Margin="24,12,-12,0" Grid.Row="2" BorderThickness="1" BorderBrush="Gray" >
<Grid toolkit:TiltEffect.IsTiltEnabled="True">
<Grid.Background>
<ImageBrush ImageSource="{Binding ImageUrlLow}" Stretch="UniformToFill" />
</Grid.Background>
</Grid>
</Border>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
My sample c# code for selecting ImageBrushes is:
for (int i = 0; i < AllPhotosListBox.ItemsSource.Count; i++)
{
AllPhotosListBox.UpdateLayout();
AllPhotosListBox.ScrollTo(AllPhotosListBox.ItemsSource[i]);
LongListSelector longListBoxItem = AllPhotosListBox as LongListSelector;
longListBoxItem.UpdateLayout();
var grid = VisualTreeHelper.GetChild(longListBoxItem, 0);
var grid1 = VisualTreeHelper.GetChild(grid, 0);
var viewportControl = VisualTreeHelper.GetChild(grid1, 0) as ViewportControl;
var contentPresenter1 = VisualTreeHelper.GetChild(viewportControl, 0) as ContentPresenter;
var canvas1 = VisualTreeHelper.GetChild(contentPresenter1, 0);
var canvas2 = VisualTreeHelper.GetChild(canvas1, 0);
var ContentPresenter2 = VisualTreeHelper.GetChild(canvas2, 0);
var border = VisualTreeHelper.GetChild(ContentPresenter2, 0);
var grid2 = VisualTreeHelper.GetChild(border, 0) as Grid;
ImageSource imgSource = (grid2.Background as ImageBrush).ImageSource;
// doing other stuffs here...
}
But here I am not getting all the imagebrushes. Let, I have ten imagebrushes. But I am getting only two(most of the time the first and the last) imagebrushes which have sources and rest others' sources are empty.
Is there any wrong in my selection. Need help...

Related

Add Button to ForEach loop

I have a foreach loop that lists a bunch of websites. I would like the ability to add a button to next to each of these websites.
e.g.
google.com X //X represents button
facebook.com X
I don't think adding a stackpanel is the way to go as I want to add it next to the textblock being created in this for loop.
public void WebsiteList(string[] blocked_sites)
{
Button removewebsite = new Button();
numofsites = blocked_sites.Length;
website.Margin = new Thickness(57, 75, 10, 20);
website.Width = 300;
removewebsite.Width = 20;
removewebsite.Height = 20;
removewebsite.Foreground = Brushes.Red;
removewebsite.Content = "X";
removewebsite.Background = Brushes.Transparent;
website.Foreground = Brushes.White;
website.TextWrapping = TextWrapping.Wrap;
website.FontSize = 13;
foreach (string Site in blocked_sites)
{
website.Inlines.Add(new Run("• "));
string editedSite = Site.Replace("*://*.", "").Replace("*://*", "").Replace("*://", "").Replace("/*", "");
website.Inlines.Add(new Run(editedSite));
website.Inlines.Add(new LineBreak());
removewebsite.Name = "test";
//HERE IS WHERE I WANT TO ADD THE BUTTON ON THE END
}
}
I've tried adding a stackpanel using stackpanel.children.Add(removewebsite) but it's not lining up with the textblocks. I think I'm just lacking sufficient knowledge in the most suitable way to go about it, would love to be pointed in the right direction.
Use an ItemsControl and set or bind its ItemsSource property to the modified string[]:
public void WebsiteList(string[] blocked_sites)
{
numofsites = blocked_sites.Length;
string[] s = new string[numofsites];
for (int i = 0; i < numofsites; ++i)
{
s[i] = string.Format("• {0}{1}", blocked_sites[i].Replace("*://*.", "").Replace("*://*", "").Replace("*://", "").Replace("/*", ""),
Environment.NewLine);
}
ic.ItemsSource = s;
}
XAML:
<ItemsControl x:Name="ic">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" Foreground="White" TextWrapping="Wrap" FontSize="13" Margin="57, 75, 10, 20" Width="300" />
<Button Content="X" Foreground="Red" Width="20" Height="30" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Wpf Text marquee behind other element

I built an messagebar, with an animated text,
see the .gif from the animation.
Like you can see the text "fly's" in the foreground and hides the placeholder. But I need the placeholder in the foreground.
My first idea was to change the region of the animation from
doubleAnimation.To = tbInfo.ActualWidth *-1;
to
doubleAnimation.To = boLogo.ActualWidth;
but the result looks like this: version with other animation area.
How can I set the placeholder in the foreground, so that the animation "fly's" behind it?
My XAML-Code
<Canvas x:Name="canMain" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Border x:Name="boLogo" Height="40" Background="Gray" Canvas.Left="0" Canvas.Top="-20">
<Button Content="Placeholder" Width="90" />
</Border>
<TextBlock x:Name="tbInfo" Visibility="Hidden" FontSize="32" FontWeight="Bold" Padding="5" HorizontalAlignment="Stretch" VerticalAlignment="Center"></TextBlock>
</Canvas>
and the code to show the window
public void ShowWindow(string str)
{
tbInfo.Text = str;
this.Height = 39;
this.Width = SystemParameters.WorkArea.Width;
this.Left = SystemParameters.PrimaryScreenWidth - this.Width;
this.Show();
TextMarquee(20);
}
private void TextMarquee(int duration)
{
double height = canMain.ActualHeight - tbInfo.ActualHeight;
tbInfo.Margin = new Thickness(0, height / 2, 0, 0);
DoubleAnimation doubleAnimation = new DoubleAnimation();
doubleAnimation.From = canMain.ActualWidth;
doubleAnimation.To = tbInfo.ActualWidth * -1;
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(duration));
tbInfo.BeginAnimation(Canvas.LeftProperty, doubleAnimation);
tbInfo.Visibility = Visibility.Visible;
}
Use the Panel.ZIndex:
<Canvas x:Name="canMain" >
<Border x:Name="boLogo" Panel.ZIndex="2">
<Button Content="Placeholder" Width="90" />
</Border>
<TextBlock x:Name="tbInfo" Panel.ZIndex="1"></TextBlock>
</Canvas>
https://msdn.microsoft.com/de-de/library/system.windows.controls.panel.zindex%28v=vs.110%29.aspx
Try the Grid.ZIndex:
<Grid x:Name="canMain" >
<Border x:Name="boLogo" Grid.ZIndex="2">
<Button Content="Placeholder" />
</Border>
<TextBlock x:Name="tbInfo" Grid.ZIndex="1"/>
</Grid>
Being ZIndex = "2" the most visible layer.

get every textblock from listview

I'm building a UWP app and I'm trying to get every textblock from my listview.
This is my listview:
<ListView Grid.Row="1" BorderBrush="#0062AD" BorderThickness="1" ItemsSource="{Binding BusRoutes}" x:Name="Routes1" SelectionMode="None" IsItemClickEnabled="False" Padding="0 10 0 0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="400">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="startingPoint" Grid.Row="0" Grid.Column="0" Padding="0 10 0 10" TextAlignment="Center" HorizontalAlignment="Center" Text="{Binding hours}"/>
<TextBlock TextAlignment="Center" Grid.Row="0" Grid.Column="1" Padding="0 10 0 10" x:Name="endingPoint" HorizontalAlignment="Center" Text="{Binding hours2}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And this is my attempt of doing this:
public T FindElementByName<T>(DependencyObject element, string sChildName) where T : FrameworkElement
{
T childElement = null;
var nChildCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < nChildCount; i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
if (child == null)
continue;
if (child is T && child.Name.Equals(sChildName))
{
childElement = (T)child;
break;
}
childElement = FindElementByName<T>(child, sChildName);
if (childElement != null)
break;
}
return childElement;
}
And in my ItemClick event I did this:
this.UpdateLayout();
for (int i = 0; i < Routes1.Items.Count; i++)
{
var container = this.Routes1.ContainerFromIndex(i);
TextBlock endingPoint = FindElementByName<TextBlock>(container, "endingPoint");
endingPoint.Visibility = Visibility.Collapsed;
}
for (int i = 0; i < Routes1.Items.Count; i++)
{
var container = this.Routes1.ContainerFromIndex(i);
TextBlock startingPoint = FindElementByName<TextBlock>(container, "startingPoint");
startingPoint.SetValue(Grid.ColumnSpanProperty, 2);
}
and I'm getting System.ArgumentException. The weird thing is that when I'm trying to take one textblock eg var container = this.Routes1.ContainerFromIndex(0);
I can get the first textblock normally. Why is this happening?
I forgot to mention that the exception blows up at this line:
var nChildCount = VisualTreeHelper.GetChildrenCount(element);

How to improve speed of adding controls to UI

I have the following code which creates WPF controls and then adds them to a window in a fashion I need. It works decently, but when trying to create 256 (x4 - two textblocks, combo box, textbox) controls it takes a while to display the tab. Window loads fine but I have many tabs and when I click on point setup tab it lags a little before displaying the tab. It only lags the first time I click on the tab, every time after the first it responds immediately.
At first I thought it was a rendering issue, but after much other research I am of the impression C#/WPF doesn't do well with creating a bunch of objects on the fly and adding them to forms.
If I drop the number of items to 50 it responds immediately, 100 is a slight lag and 200 (256) is a little more of a lag and too much to be acceptable to users.
Any experiences with issues like this before and advice for how to fix it or other tips/tricks.
Thanks in advance!
Wesley
public static void pointSetup(VirtualizingStackPanel desc, VirtualizingStackPanel map) //Draws point description and point map table in point setup tab
{
StackPanel row;
TextBlock text;
TextBox textBox;
ComboBox comboBox;
Thickness rowSpacing = new Thickness(0, 0, 0, 5);
Thickness textSpacing = new Thickness(0, 3, 5, 3);
List<string> list = new List<string>();
list.Add("xx");
for (byte i = 0; i < Global.currentZonesToMap; i++)
{
list.Add("Zone " + (i + 1));
}
for (short i = 0; i < 256; i++)
{
//desc
row = new StackPanel();
row.Margin = rowSpacing;
row.Orientation = Orientation.Horizontal;
text = new TextBlock();
text.Text = "Point " + (i + 1);
text.Margin = textSpacing;
text.Width = 50;
textBox = new TextBox();
textBox.MaxLength = 28;
textBox.Text = "";
textBox.Width = 270;
row.Children.Add(text);
row.Children.Add(textBox);
desc.Children.Add(row);
//map
row = new StackPanel();
row.Margin = rowSpacing;
row.Orientation = Orientation.Horizontal;
text = new TextBlock();
text.Text = "Point " + (i + 1);
text.Margin = textSpacing;
text.Width = 50;
comboBox = new ComboBox();
comboBox.ItemsSource = list;
comboBox.Width = 270;
row.Children.Add(text);
row.Children.Add(comboBox);
map.Children.Add(row);
}
}
New Code (using DataTemplate and ItemsControl)
public class DevicePoint
{
public string desc { get; set; }
public int zone { get; set; }
public List<string> zones { get; set; }
}
//Initialized all variables and displays UI (constructor)
public Dispatcher()
{
InitializeComponent();
List<string> opts = new List<string>();
opts.Add("xx");
for (byte i = 0; i < Global.currentZonesToMap; i++)
{
opts.Add("Zone " + (i + 1));
}
List<DevicePoint> points = new List<DevicePoint>();
for (short i = 0; i < 256; i++)
points.Add(new DevicePoint() { desc = "Point " + (i + 1), zone = 0, zones = opts });
pointDesc.ItemsSource = points;
pointZoneMap.ItemsSource = points;
... other stuff here ...
}
<StackPanel>
<TextBlock Margin="10" FontWeight="Bold" HorizontalAlignment="Center" Text="Point Descriptions" />
<ScrollViewer Width="360" Margin="30,10,30,10" MaxHeight="405">
<ItemsControl Name="pointDesc" Margin="5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel VirtualizingStackPanel.IsVirtualizing="True" Margin="0,0,0,5" Orientation="Horizontal">
<TextBlock Margin="0,3,5,3" Width="50" Text="{Binding desc}" />
<TextBox MaxLength="28" Width="270" Text="{Binding desc}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Margin="10" FontWeight="Bold" HorizontalAlignment="Center" Text="Point - Zone Map" />
<ScrollViewer Width="360" Margin="30,10,30,10" MaxHeight="405">
<ItemsControl Name="pointZoneMap" Margin="5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel VirtualizingStackPanel.IsVirtualizing="True" Margin="0,0,0,5" Orientation="Horizontal">
<TextBlock Margin="0,3,5,3" Width="50" Text="{Binding desc}" />
<ComboBox Width="270" ItemsSource="{Binding zones}" SelectedIndex="{Binding zone}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
If your computer have multiple cores, and I am assuming it have, try to perform the for loop in parallel (parallelfor (from .net 4 or above).
You can set the points List size during creation to 256, this will prevent memory allocations during the items adding operation.
Consider use a StringBuilder if Global.currentZonesToMap is large.
Use StringBuilder to build the value for the DevicePoint.desc string property.
Good luck,
M. Moshe

Issues displaying 2 seperate Canvases at the same time in XAML

I have canvas A and canvas B.
Canvas A contains an Image and a grid with diffrent colour squares.
numSelectCan is a diffrent image.
Once i click on a square a event triggers that should set the visibilty state of numSelectCan to visible and then it should show up overlaping Canvas A.
That is not the case. I have tryed everything but i cant get numSelectCan to show up at all.
During development numSelectCan is displayed and works fine. At runtime numSelectCan is just gone. I tryed disabling A but still no success.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="10,0,10,10">
<Controls:RoundButton x:Name="btnCancel" PressedBrush="Orange" Foreground="White" BorderBrush="White" ImageSource="/Assets/Appgraphics/Buttons/cancel.png" HorizontalAlignment="Left" VerticalAlignment="Bottom" Click="btnCancel_Click"/>
<Controls:RoundButton x:Name="btnQuestion" PressedBrush="Orange" Foreground="White" ImageSource="/Assets/Appgraphics/Buttons/questionmark.png" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" Click="btnQuestion_Click" BorderBrush="White"/>
<Controls:RoundButton x:Name="btnConfirm" PressedBrush="Orange" Foreground="White" BorderBrush="White" ImageSource="/Assets/Appgraphics/Buttons/check.png" HorizontalAlignment="Right" VerticalAlignment="Bottom" Click="confirmSelection"/>
<Canvas x:Name="picCanvas" HorizontalAlignment="Center" Width="582" Height="320">
<Image x:Name="imgBackCC" Height="320" Width="582" Stretch="Fill" HorizontalAlignment="Center" Source="" Margin="0,0,0,0"/>
</Canvas>
<Canvas x:Name="numSelectCan" HorizontalAlignment="Center" Height="155" Visibility="Visible" Width="530" VerticalAlignment="Center">
<Image x:Name="numSelBackground" Source="/Assets/Appgraphics/UserInterface/numFieldBackground.png" Height="155" Width="530" Stretch="Fill"/>
<Image x:Name="numFieldButton1" Source="/Assets/Appgraphics/UserInterface/numField0.png" Width="70" Height="70" Canvas.Left="5" Canvas.Top="5"/>
<Image x:Name="numFieldButton2" Source="/Assets/Appgraphics/UserInterface/numField1.png" Width="70" Height="70" Canvas.Left="80" Canvas.Top="5"/>
<Image x:Name="numFieldButton3" Source="/Assets/Appgraphics/UserInterface/numField2.png" Width="70" Height="70" Canvas.Left="155" Canvas.Top="5"/>
<Image x:Name="numFieldButton4" Source="/Assets/Appgraphics/UserInterface/numField3.png" Width="70" Height="70" Canvas.Left="230" Canvas.Top="5"/>
<Image x:Name="numFieldButton5" Source="/Assets/Appgraphics/UserInterface/numField4.png" Width="70" Height="70" Canvas.Left="305" Canvas.Top="5"/>
<Image x:Name="numFieldButton6" Source="/Assets/Appgraphics/UserInterface/numField5.png" Width="70" Height="70" Canvas.Top="80" Canvas.Left="5"/>
<Image x:Name="numFieldButton7" Source="/Assets/Appgraphics/UserInterface/numField6.png" Width="70" Height="70" Canvas.Left="80" Canvas.Top="80"/>
<Image x:Name="numFieldButton8" Source="/Assets/Appgraphics/UserInterface/numField7.png" Width="70" Height="70" Canvas.Left="155" Canvas.Top="80"/>
<Image x:Name="numFieldButton9" Source="/Assets/Appgraphics/UserInterface/numField8.png" Width="70" Height="70" Canvas.Left="230" Canvas.Top="80"/>
<Image x:Name="numFieldButton10" Source="/Assets/Appgraphics/UserInterface/numField9.png" Width="70" Height="70" Canvas.Left="305" Canvas.Top="80"/>
<Image x:Name="numFieldDelButton" Source="/Assets/appgraphics/UserInterface/numFieldDelete.png" Width="145" Height="145" Canvas.Top="5" Canvas.Left="380" />
</Canvas>
</Grid>
</Grid>
Not sure what is going wrong here, or if im missing a simple thing like setting the display order for the diffrent canvases
Any Ideas/Advice is appreciated.
Update
I added the entire content panel from my xaml.
heres the function that creates the grid for me when the page is initialized
public void createGrid()
{
//create the grid
Grid backGrid = new Grid();
backGrid.Width = 530;
backGrid.Height = 285;
backGrid.HorizontalAlignment = HorizontalAlignment.Center;
backGrid.VerticalAlignment = VerticalAlignment.Center;
backGrid.ShowGridLines = false;
backGrid.Margin = new Thickness(25, 15, 0, 0);
//define columns
for (int c = 0; c < 10; c++)
{
ColumnDefinition colDef = new ColumnDefinition();
backGrid.ColumnDefinitions.Add(colDef);
}
//define rows
for (int r = 0; r < 6; r++)
{
RowDefinition rowDef = new RowDefinition();
backGrid.RowDefinitions.Add(rowDef);
}
//colour counter
int counter = 0;
//create textboxes
for (int r = 0; r < 6; r++)
{
for (int c = 0; c < 10; c++)
{
//set the coulour of the canvases based on the counter
if (counter == 4)
{
counter = 0;
}
SolidColorBrush tempBrush = new SolidColorBrush();
switch (counter)
{
case 0:
tempBrush = new SolidColorBrush(Colors.Red);
break;
case 1:
tempBrush = new SolidColorBrush(Colors.Orange);
break;
case 2:
tempBrush = new SolidColorBrush(Colors.Blue);
break;
case 3:
tempBrush = new SolidColorBrush(Colors.Green);
break;
}
string canName = c.ToString() + r.ToString();
string txtName = "text" + c.ToString() + r.ToString();
string tempCanName = "canvas" + c.ToString() + r.ToString();
//creating the canvas
Canvas tempCanvas = new Canvas();
tempCanvas.Name = tempCanName;
tempCanvas.Background = tempBrush;
tempCanvas.HorizontalAlignment = HorizontalAlignment.Stretch;
tempCanvas.VerticalAlignment = VerticalAlignment.Stretch;
tempCanvas.Margin = new Thickness(2);
//creating the textblock
TextBlock tempName = new TextBlock();
tempName.Width = 32;
tempName.Height = 32;
tempName.Name = txtName;
tempName.Foreground = new SolidColorBrush(Colors.White);
tempName.TextAlignment = TextAlignment.Center;
tempName.Margin = new Thickness(13, 0, 0, 0);
tempName.FontWeight = FontWeights.Bold;
tempName.HorizontalAlignment = HorizontalAlignment.Center;
tempName.VerticalAlignment = VerticalAlignment.Center;
tempName.Visibility = Visibility.Visible;
tempName.FontSize = 30;
tempName.Tap += tempName_Tap;
//adding the canvas to the grid
Grid.SetRow(tempCanvas, r);
Grid.SetColumn(tempCanvas, c);
//adding all items into the canvas and into the grid
tempCanvas.Children.Add(tempName);
backGrid.Children.Add(tempCanvas);
//increment counter
counter++;
}
}
//add the grid into the mainpage
picCanvas.Children.Add(backGrid);
}
All of this works. I get the grid with all the diffrent colour squares, but when I press textblock, I want my 2nd canvas to pop up and that does not happen. I added breakpoints through out and it goes through all of them, i just dont see the second canvas
heres the code for capturing the tap on the textblock
//function that handels the event when a textblock is tapped
private void tempName_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//create a copy of the object that triggerd this event
TextBlock tempBlock = sender as TextBlock;
//create a string from the name of this textblock and then cut of the textpart from the name
string tempName = tempBlock.Name;
tempName = tempName.Substring(4);
//move the canvas top or bottom based on which row the current selection is in
string currentRow = tempName.Substring(1);
if ((currentRow == "0") || (currentRow == "1") || (currentRow == "2"))
{
numSelectCan.VerticalAlignment = VerticalAlignment.Top;
}
else
{
numSelectCan.VerticalAlignment = VerticalAlignment.Bottom;
}
//show the number selector control
numSelectCan.Visibility = Visibility.Visible;
}
I am still stuck on this. Cant get numSelectCan to show up...
hello I have tried your code and what i found is that your question is little bit confusing so i assume that you want your second canvas to be visible on tap on the textblock. but you have not register any event-handler on tap on textblock so no event fired that's why first make your textblock visible and add a tap event handler on which you can set the visibility of second canvas.
also tempName_Tap eventHandler is you color gridbox eventhandler not a textblock tap eventhandler so it never register you tap on text block.
Also in the beginning you write that you want your second canvas to be visible on tap on the grid box for that your code is working fine.
I took your xaml/code and tested it out with my own images. Without the images (or entire project) to see how they are drawing on the canvas, I have to say there is a chance that you don't have the correct Z-Index for your 2 canvases. They normally are layered one on top of the other not shifted off to one side. You can set which Canvas is on top of the other by setting the SetZIndex. Try calling:
//show the number selector control
numSelectCan.Visibility = Visibility.Visible;
Canvas.SetZIndex(numSelectCan, 5);
and to hide it again:
//Hide the number selector control
numSelectCan.Visibility = Visibility.Hidden;
Canvas.SetZIndex(numSelectCan, -5);
Of course the Hidden and changing the ZIndex order might be a little redundant. But I have had trouble with one of the canvases taking the events instead of the one that is displayed so I do both.

Categories

Resources