Unable to find controls inside DataTemplate in SilverLight5 - c#

I am using DataTemplate where I've declared two Buttons and they are invisible by default. Now I want to make them Visible in code behind based on some condition but I am unable to find these controls.
Following is my code:
XAML:
<ItemsControl x:Name="SynonymsItemsControl" ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="SynonymsStackPanel">
<CheckBox x:Name="SynonymsChkBx" Content="{Binding Display}" Margin="10,0,0,0" />
WANT TO FIND THESE TWO BUTTONS
<Button x:Name="AddSynonymsBtn" Margin="525, 0, 0, 0" ToolTipService.ToolTip="Add Synonyms" Visibility="Collapsed">
<Image Source="/Images/AddSynonyms.png" Height="24"/>
</Button>
<Button x:Name="CancelSynonymsBtn" Margin="600, 0, 0, 0" ToolTipService.ToolTip="Cancel Synonyms" Visibility="Collapsed">
<Image Source="/Images/CancelSynonyms.png" Height="24"/>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
CODE-BEHIND:
I'm able to find ItemControl by doing this,
System.Windows.Controls.ItemsControl itemControl = (from sp in selectedTreeViewItem.GetVisualDescendants().OfType<System.Windows.Controls.ItemsControl>()
where sp.Name == "SynonymsItemsControl"
select sp).FirstOrDefault();
Now When I want to find buttons which are declared inside this ItemsControl->DataTemplate, it comes as Null
Button AddSynonymsBtn = (from btn in itemControl.GetVisualDescendants().OfType<Button>()
where btn.Name == "AddSynonymsBtn"
select btn).FirstOrDefault();
So I am wondering that do I need to find ItemsControl at all? if so, then why am I unable to find these buttons?
I've also tried
var findControl = temControl.ItemContainerGenerator.ContainerFromIndex(index); but same result(Null).

You can easly get controls from behind by using Template.FindName(string Name);
private Button _partSynonyms = null;
public MyControlTemplate{
Loaded += (sender, e) => OnLoaded();
}
private void OnLoaded(){
_partSynonyms = (Button)Template.FindName("PART_AddSynonymsBtn"); //You "should" use PART_ as a prefix in a Template
if(_partSynonyms == null)
//Button not found -> Log some error but you should not throw an exception
}

Related

How can I remove selected row from a grid control

I have created a Grid and adding rows dynamically. And I have a remove button in every row. On click of that remove button I want to remove that particular row.
I am quite new to WPF, can some one help me out to achieve this.
Thanks.
Xaml code:
<Grid Grid.Column="2" Visibility="Collapsed" Name="operationalGrid">
<Button Height="25" Name="btnRemove" Width="24" Style="{DynamicResource ButtonStyle}" Grid.Column="4" Click="btnRemove_Click">
<materialDesign:PackIcon Background="Transparent" Height="25" Width="30" Cursor="Hand" ToolTip="Remove" Kind="Minus" HorizontalAlignment="Center" Foreground="White" VerticalAlignment="Top" Margin="0,0,0,0">
</materialDesign:PackIcon>
</Button>
</Grid>
c# code:
Adding rows:
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
if (ucDynamicControls != null)
ucDynamicControls.btnToggle.Visibility = ucDynamicControls.btnToggle.Visibility = Visibility.Visible;
ucDynamicControls = new ucControls();
grdContols.RowDefinitions.Add(row);
Grid.SetRow(ucDynamicControls, controlCount++);
grdContols.Children.Add(ucDynamicControls);
Instead of using row definitions, you may use ItemsControl.
As example:
First define your ItemTemplate
<ItemsControl Name="icTodoList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Define your ItemsPanel
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Full tutorial:
https://www.wpf-tutorial.com/list-controls/itemscontrol/
You have come up with a very complex, one might say, perverse way of accomplishing the task.
A typical implementation is to create a container element with data. Then the collection with these elements is passed to ItemsControl. And he already visual each element as it should.
In this implementation: deleting a row is removing an item from the collection.
In your implementation, you need to parse the Visual Element Tree.
It is possible, but difficult.
The button, as I understand it, is inside the Grid row.
This Grid is inside ucDynamicControls.
And the outer Grid row already contains this ucDynamicControls.
If I understood correctly, then in the button event it is necessary to go up from the button to the Grid containing ucDynamicControls.
In this Grid, remove the ucDynamicControls element from the Children collection.
And the line (in which it) is to collapse: Heigh = 0.
And it's not clear why you chose Grid, and StackPanel for the ucDynamicControls collection?
If you use a StackPanel, then it will be possible not to set the line number and for it will be enough just to remove the desired ucDynamicControls from the collection.
An example implementation (if I understand your code correctly):
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button))
return;
DependencyObject child = button;
DependencyObject parent;
while (!((parent = LogicalTreeHelper.GetParent(child)) is UcDynamicControls) && parent != null)
child = parent;
if (parent == null)
return;
UcDynamicControls ucDynamicControls = (UcDynamicControls)parent;
int row = Grid.GetRow(ucDynamicControls);
Grid grid = LogicalTreeHelper.GetParent(ucDynamicControls) as Grid;
if (grid == null || grid.RowDefinitions.Count <= row)
return;
grid.RowDefinitions[row].Height = new GridLength(0);;
grid.Children.Remove(ucDynamicControls);
}

How to get the destination ListView item on drop?

In UWP C#, I have one ListView in upper row & another in lower row. When I drag a listitem from upper ListView & drop it on lower ListView, I am getting the source. But, I am unable to get the destination. ie) the listview item/(Folder object in my case) where I dropped.
<ListView Name="ListviewCars"
CanDragItems="True" DragItemsStarting="ListviewCars_DragItemsStarting"
SelectionMode="Single" IsItemClickEnabled="True"
DataContext="Cars" ItemsSource="{Binding CarsCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Transparent" Height="80" Orientation="Horizontal"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<Grid Name="GrdCars" >
<Grid Height="80" Width="90" Padding="5">
<Grid.Background>
<ImageBrush Stretch="Uniform" ImageSource="Assets/car.png"/>
</Grid.Background>
<TextBlock Text="{Binding Name}" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Name="GrdViewImg" ScrollViewer.VerticalScrollBarVisibility="Disabled"
AllowDrop="True" DragOver="Image_DragOver"
Drop="Image_OnDrop"
DataContext="Folders" ItemsSource="{Binding FolderCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Transparent" Height="80" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<Grid Name="GrdForFolderMenu" RightTapped="GrdForFolderMenu_RightTapped">
<Grid Height="80" Width="90" Padding="5">
<Grid.Background>
<ImageBrush Stretch="UniformToFill" ImageSource="Assets/Folderimage.png"/>
</Grid.Background>
<TextBlock Text="{Binding Name}" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have found a simple solution myself. I get the source from DragItemsStarting event of the SoureListView. and target item (as mentioned by ashchuk) from a Grid placed inside Datatemplate of Target ListView. as shown below. Everything works fine now!
(Cars is my Source custom list item. Folders is my Target custom list item)
private void SoureListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
Cars x = e.Items[0] as Cars;
string DraggedSourceCar = x.Name;
e.Data.Properties.Add("myArgs", DraggedSourceCar);
}
private void GridInsideDatatemplateOfTargetListview_Drop(object sender, DragEventArgs e)
{
var x = sender as Grid;
var y = x.DataContext as Folders;
string toMoveFolderName = y.Name;
string DraggedSourceCar = e.DataView.Properties["myArgs"].ToString();
Debug.WriteLine(DraggedSourceCar + Environment.NewLine + toMoveFolderName );
}
private void TargetListview_DragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
}
You have to find out by yourself = DragEventArgs.GetPosition() in the destination drop, then the underlying item with smoe helper functions
public static object GetObjectAtPoint<ItemContainer>(this ItemsControl control, Point p)
where ItemContainer : DependencyObject
{
// ItemContainer - can be ListViewItem, or TreeViewItem and so on(depends on control)
ItemContainer obj = GetContainerAtPoint<ItemContainer>(control, p);
if (obj == null)
return null;
return control.ItemContainerGenerator.ItemFromContainer(obj);
}
public static ItemContainer GetContainerAtPoint<ItemContainer>(this ItemsControl control, Point p)
where ItemContainer : DependencyObject
{
HitTestResult result = VisualTreeHelper.HitTest(control, p);
if (result != null)
{
DependencyObject obj = result.VisualHit;
while (VisualTreeHelper.GetParent(obj) != null && !(obj is ItemContainer))
{
obj = VisualTreeHelper.GetParent(obj);
}
// Will return null if not found
return obj as ItemContainer;
}
else return null;
}
Did you checked this sample?
After some research I found what DragEventArgs contains an OriginalSource property with Name matches the name of target list when Drop event invoked.
I'm not checked it with folders and subfolders, but maybe OriginalSource will contain folder where item dropped.
<TextBlock Grid.Row="1" Margin="8,4"
VerticalAlignment="Bottom"
Text="All Items"/>
<ListView x:Name="SourceListView"
Grid.Row="2" Margin="8,4"
SelectionMode="Extended"
CanDragItems="True"
DragItemsStarting="SourceListView_DragItemsStarting"/>
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,4"
VerticalAlignment="Bottom"
Text="Selection"/>
<ListView x:Name="TargetListView"
Grid.Row="2" Grid.Column="1" Margin="8,4"
AllowDrop="True" CanReorderItems="True" CanDragItems="True"
DragOver="TargetListView_DragOver"
Drop="TargetListView_Drop"
DragItemsStarting="TargetListView_DragItemsStarting"
DragItemsCompleted="TargetListView_DragItemsCompleted"/>
And here is printscreen with fired breakpoint:
EDIT:
To get an item inside of TargetList you can do a trick.
I think you use DataTemplate to display custom list items ("folders"). You can see a sample below. As you see I add Grid_DragOver trigger.
<Page.Resources>
<DataTemplate x:Key="ListViewDataTemplate">
<Grid Margin="20,5" DragOver="Grid_DragOver"
BorderBrush="White" BorderThickness="5" AllowDrop="True">
<TextBlock Margin="10" LineHeight="40" FontSize="32" FontWeight="Bold"/>
</Grid>
</DataTemplate>
</Page.Resources>
This way Grid_DragOver will be invoked when mouse pointer enter inside the Grid in DataTemplate.
And if you use binding List<YourFolderClass> as data source, you'll get folder in DataContext. For example I used this:
var SampleData = new ObservableCollection<string>
{
"My Research Paper",
"Electricity Bill",
"My To-do list",
"TV sales receipt",
"Water Bill",
"Grocery List",
"Superbowl schedule",
"World Cup E-ticket"
};
You can see all code in gist.

WPF C# Get array position of grid button from Click Event

I have a window that I'm basically building ghetto minesweeper in. I have a grid that I feed a jagged array into, set up so that it will adapt to any change in the size of the array (no hard set values or rows/columns). Over top of that, I have a grid of blank buttons that simply adapts in size to the grid below.
When you click a button, it hides revealing the value under it, and I need some way to return the position of the button clicked, so I can compare against the original jagged array to find out whether not the item was a bomb. (this would also help me for doing a fill action on empty tiles). But given how I have it set up, the Grid.GetRow or Grid.GetColumn just return 0's.
Does anyone know how I can get the array position (preferably row and column) from the setup that I have?
XAML Below, the C# click events follow it.
<Window x:Class="MinesweeperWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Minesweeper" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto">
<Window.Resources>
<DataTemplate x:Key="Buttons_Template">
<Button Content=""
Height="20"
Width="20"
Margin="0,0,0,0"
Visibility="Visible"
Click="ButtonClick"
MouseRightButtonUp="RightClick"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_2">
<TextBlock Text="{Binding}"
Height="20"
Width="20"
Margin="0,0,0,0"
FontFamily="Rockwell"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="5"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
<DataTemplate x:Key="Buttons_Template2">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource Buttons_Template}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<TextBlock x:Name="RemainingMines" HorizontalAlignment="Left" />
<TextBlock x:Name="Difficulty" HorizontalAlignment="Center" />
<TextBlock x:Name="Timer" HorizontalAlignment="Right" />
</Grid>
<Grid Name="ResetButton" Grid.Row="1">
<Button Name="Reset" Content="Reset"/>
</Grid>
<Grid Name="GridBoard" ShowGridLines="True" Grid.Row="2">
<ItemsControl x:Name="GridItems" ItemTemplate="{DynamicResource DataTemplate_1}"/>
</Grid>
<Grid Name="ButtonsBoard" ShowGridLines="True" Grid.Row="2">
<ItemsControl x:Name="ButtonItems" ItemTemplate="{DynamicResource Buttons_Template2}"/>
</Grid>
</Grid>
</Window>
Click Methods in C#
private void ButtonClick(object sender, RoutedEventArgs e)
{
int col = Grid.GetColumn((Button)sender); //this just returns 0
int row = Grid.GetRow((Button)sender); //this just returns 0
Button source = e.Source as Button;
source.Visibility = Visibility.Hidden;
Console.WriteLine("L: {0} x {1}", col, row); //prints L: 0 x 0
}
private void RightClick(object sender, RoutedEventArgs e)
{
int col = Grid.GetColumn((Button)sender); //this just returns 0
int row = Grid.GetRow((Button)sender); //this just returns 0
Button source = e.Source as Button;
if(source.Content.Equals(""))
{
source.Content = "\u2691";
}
else
{
source.Content = "";
}
Console.WriteLine("R: {0} x {1}", col, row); //prints R: 0 x 0
}
Any help would be appreciated on this.
You need to use appropriate control for buttons to host. In your xaml you are using items control inside Grid. But Items control do not have row and column index. Thats why you are not able to get the index.
Use UniformGrid or some relevant control.
You can refer this article
https://stackoverflow.com/a/13588066/5273015
Will help you a lot in other assignments as well

Hide GridViewItem and reposition items in GridView

As you can see above, I need it to reshuffle itself into alignment. Obviously the items still exist, is there a way to completely ignore them?
I have an ObservableCollection:
public static volatile ObservableCollection<MyVideo> MyVideoModels = new ObservableCollection<MyVideo>();
This gets filled with the MyVideo objects.
Binding it to my GridView like so:
public VideosFoundView()
{
this.InitializeComponent();
this.AddVideoFolderGridView.ItemsSource = VideosFoundView.MyVideoModels;
}
The DataTemplate I am using for the GridView.
<GridView Grid.Row="2" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="AddVideoFolderGridView">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="5">
<Image Source="{Binding FullImageLocationOnDisk}" MaxHeight="300" MaxWidth="200" DoubleTapped="gridViewItem_DoubleTapped" Loaded="gridViewItem_Loaded" Loading="gridViewItem_Loading">
</Image>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
And I have a static ToggleSwitch above this GridView like so:
<Grid Grid.Row="1">
<Grid>
<TextBlock Text="Ingore Image Not Found"/>
<ToggleSwitch x:Name="ToggleSwitchIgnoreImages" Toggled="ignoreImages_Toggled"/>
</Grid>
</Grid>
Which does:
private void ignoreImages_Toggled(object sender, RoutedEventArgs e)
{
ToggleSwitch tSwitch = (ToggleSwitch)(sender as ToggleSwitch);
if (tSwitch.IsOn)
{
for(int i = 0; i < VideosFoundView.MyVideoModels.Count; i++)
{
if(VideosFoundView.MyVideoModels[i].FullImageLocationOnDisk == "ms-appx:///Assets/image-not-found.gif")
{
var gridViewItem = (GridViewItem)this.AddVideoFolderGridView.ContainerFromIndex(i);
gridViewItem.Visibility = Visibility.Collapsed;
}
}
}
else
{
for (int i = 0; i < VideosFoundView.MyVideoModels.Count; i++)
{
//VideosFoundView.MyVideoModels[i].Visibility = "Auto";
var gridViewItem = (GridViewItem)this.AddVideoFolderGridView.ContainerFromIndex(i);
gridViewItem.Visibility = Visibility.Visible;
}
}
}
However the problem is that the items are still taking up space on my GridView, and the other items do not re-position themselves accordingly.
The items are still taking up space on the GridView, when the GridViewItem is collapsed. It might because the ItemTemplate has not be refreshed, the space is still reserved.
As a workaround, there is a port of the Toolkit's WrapPanel for UWP in the project WinRTXamlToolkit.
You can get it from NuGet.
Then in your Page add this prefix:
xmlns:toolkit="using:WinRTXamlToolkit.Controls"
Now you can use <toolkit:WrapPanel Orientation="Horizontal" ></toolkit:WrapPanel> as it was before.
For example:
<GridView x:Name="AddVideoFolderGridView">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" >
</toolkit:WrapPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="5">
<Image Source="{Binding FullImageLocationOnDisk}" MaxHeight="300" MaxWidth="200" DoubleTapped="gridViewItem_DoubleTapped" Loaded="gridViewItem_Loaded" Loading="gridViewItem_Loading">
</Image>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Solution from here: Not showing items with Visibility=Collapsed in Windows 8.1 GridView
tldr:
Edit the template of your GridView and replace the ItemsWrapGrid in the ItemsPanelTemplate with the WrapPanel you can find here http://codepaste.net/8gr5go

how to get listbox selected items tag in mainpage xaml

I am developing windows phone 8 app.I need a user response in some part of my application
i create windows phone user control page .User will select a value from a listbox that is in usercontrol page
here is code of my user control page
<Grid x:Name="columngrid" Background="#FF1FCB4E" Width="480" >
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
<RowDefinition Height="70"/>
<RowDefinition Height="70"/>
</Grid.RowDefinitions>
<ListBox Name="URLListBox" Grid.Row="0" Background="#FF1FCB4E" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#FF0B232F" BorderThickness="2">
<TextBlock x:Name="surename" Width="460" Tag="{Binding b1Tag}" Height="80" FontSize="25" Text="{Binding text}" Foreground="#FFBF9595" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0" />
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" x:Name="btnOK" Content="OK" Background="#FF892121" />
<Button Grid.Row="2" x:Name="btnCancel" Content="Cancel" Background="#FF892121"/>
</Grid>
i want to get selected textblock's text in mainpage.xaml.But When i create class object for user control page i cant reach textblock.I can only reach listbox.How can i get selected textblock's text
I try this but i realized that s will bring listbox object
surah popupsurah = new surah();//usercontrol page
Popup popup2 = new Popup();
private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
collapsedgrid.Visibility = Visibility.Collapsed;
popupsurah.URLListBox.Tap += (s, args) =>
{
string transferID = ((TextBlock)s).Text as string;
Best way is to do binding with the ListBox SelectedItem property and so you can easily retrieve it from the object itself.
Something like below,(Algorithm)
You can have a class:
class MyClass
{
List<string> Items{get;set;}
string SelectedItem{get;set;}
}
You can pass an instance of this class to the UserControl:
MyUserControl(MyClass);
In the Constructor of MyUserControl:
set the DataContext: this.DataContext=MyClass;
Bind the Property Names in the xaml. Thats it.
Another approach is:
Save the selected Item string into the state dictionary:
PhoneApplicationService.Current.State["SelectedItem"]=YourSelectedItemString;
You can retrieve in the MainPage.xaml like this:
var selectedItem=PhoneApplicationService.Current.State["SelectedItem"] as string;
Better you can go for Phone:LongListSelector control which is more optimised than ListBox,Which has almost all events and properties of ListBox.

Categories