<GridView x:Name="MainGridStations" ItemsSource="{x:Bind Stations}" IsItemClickEnabled="True" ItemClick="GridView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:Station">
<Grid x:Name="WantToSelectByCode">
<Grid Background="White" HorizontalAlignment="Center" Width="300" Height="200" VerticalAlignment="Center">
<Grid Background="#e4f0fc" Height="65" VerticalAlignment="Bottom" Opacity="0.8">
<TextBlock x:Name="StationName" Text="{Binding Name}" FontWeight="Bold" Foreground="#2c9a8b" HorizontalAlignment="Center" />
</Grid>
</Grid>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
I'm trying to select a child by index of a dynamically filled gridview but what i've tried always returns null.
Like so for the first child for example:
var container = MainGridStations.ContainerFromIndex(0);
var presenter = VisualTreeHelper.GetChild(container, 0) as GridViewItem;
What am I doing wrong here?
You can get the corresponding GridViewItem from the method ItemsControl.ContainerFromIndex(Int32) directly and don't need to use the VisualTreeHelper to get it again.
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem= container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Red);
The container have been the corresponding GridViewItem got from the index.
Note that: since your inner Grid have a Background="White" property configuration, you can delete the code to see the effect more obviously using my above code to change the gridViewItem.Background.
---Update---
You must get the GridViewItem after the items loaded. You can try the code in your GridView_ItemClick event handler or the page's loaded event hander. Also pay attention to my above note that to get more obvious effect, please delete the Background="White" in your above xaml code.
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem = container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Green);
}
//get the item here
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem= container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Red);
//var presenter = VisualTreeHelper.GetChild(container, 0) as GridViewItem;
}
Related
I need to build the UIElementCollection DetailsElement, after initializing the UserControl.
My problem lies in the FindName. I can't figure out how to use the TemplatedParent. Which element is that? an how can I refer to it in CB?
So DetailsElements would contain all the InputField elements in the DataGrid Details.
public partial class Segments
{
public Segments()
{
InitializeComponent();
// this does not work
var e = DgSections.FindName("MainPanel",...) as StackPanel;
DetailsElements = e.Children;
}
UIElementCollection DetailsElements;
...
<UserControl
x:Class="My.Views.MainUserControls.Segments"
.....
<DataGrid
x:Name="DgSections"
....
<DataGrid.Columns>
...
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border Margin="0" Style="{StaticResource DataGridDetailsBorder}">
<StackPanel
x:Name="MainPanel"
...>
<muc:InputField
x:Name="Length"
.../>
<muc:InputField
x:Name="Diam"
... />
// and more InputFields
</StackPanel>
</Border>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
....
As mentioned in a comment, there is no Border element created by the time Segments is created. It's loaded and rendered on demand.
What you can do is to handle the Loaded event of the Border and programmatically set the properties of the InputFields:
private void SomeButton_Click(object sender, RoutedEventArgs e)
{
Border border = (Border)sender;
StackPanel stackPanel = (StackPanel)border.Child;
foreach (InputField inputField in stackPanel.Children.OfType<InputField>())
{
//...
}
}
XAML:
<Border Margin="0" Style="{StaticResource DataGridDetailsBorder}" Loaded="OnLoaded">
Here is my scenario. My table has fixed number of columns, say 2, and initially, it has only one visible row, but when the focus is on the last column of row 1 and the user press 'tab', row 2 will be made visible.
My problem is that I can't dynamically select the row I want to make visible because I have to specify its x:Name during compilation.
Below is my current work.
.xaml file
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
.cs file
private int lastRowIndex = 1;
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
nextLineName.Visibility = Visibility.Visible; // which causes error because nextLineName is string instead of StackPanel
}
Besides, my current implementation is to create 50 rows and make the last 49 invisible initially, and I am open to any method to group all the TextBox more systematically or flexibly.
Thanks for reading.
You could give the parent StackPanel an x:Name or keep a reference to it if you create it dynamically:
<StackPanel x:Name="root" Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
...and then get a reference to a child StackPanel using the Children property and some basic LINQ:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
StackPanel child = root.Children.OfType<StackPanel>().FirstOrDefault(x => x.Name == nextLineName);
if (child != null)
child.Visibility = Visibility.Visible;
}
How could I create a StackPanel dynamically?
Like this:
var sp = new StackPanel { Name = "SP3", Orientation = Orientation.Horizontal, Visibility = Visibility.Collapsed };
sp.Children.Add(new TextBlock { Text = "3-1" });
var txt = new TextBlock() { Text = "3-2" };
txt.KeyDown += showNextLine;
sp.Children.Add(txt);
root.Children.Add(sp);
I can't think of an easy way to do the first part of this (since you have 50 stack panels), but if you put all of them in a dictionary, then you could update them using just the key.
Here's the dictionary part done manually:
Dictionary<int, StackPanel> myStackPanels = new Dictionary<int, StackPanel>();
myStackPanels.Add(1, SP1);
myStackPanels.Add(2, SP2);
Then, here's what ShowNextLine would look like:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
// Modify the StackPanel whose key is lastRowIndex;
myStackPanels[lastRowIndex] = Visibility.Visible;
}
I have following working XAML and C# code behind:
<Grid x:Name="MainGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView ItemsSource="{Binding}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="100" Width="150">
<Grid.Background>
<SolidColorBrush Color="{Binding Color}"/>
</Grid.Background>
<StackPanel>
<StackPanel.Background>
<SolidColorBrush Color="{Binding Color}"/>
</StackPanel.Background>
<TextBlock FontSize="15" Margin="10" Text="{Binding Name}"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
CODE behind:
public MainPage()
{
this.InitializeComponent();
var _Colors = typeof(Colors)
.GetRuntimeProperties()
.Select(x => new
{
Color = (Color)x.GetValue(null),
Name = x.Name
});
this.DataContext = _Colors;
}
This works fine.
But I want to do all the XAML part in C# code behind. In XAML, only MainGrid will be there, all its child elements and bindings needs to be done in code behind.
I have tried something like this in MainPage_Loaded event:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
try
{
GridView gridView = new GridView()
{
ItemTemplate = new DataTemplate
{
//Don't know what to add here
}
};
Grid grid = new Grid();
Binding bindingObject = new Binding();
bindingObject.Source = this;
grid.SetBinding(Grid.BackgroundProperty, bindingObject);
//...
// Don't know how to add grid inside gridView in Code.
//...
MainGrid.Children.Add(gridView);
}
catch (Exception ex)
{
}
}
First of all I'd like to advise you not to create elements in code unless you have a real good reason to do so.
Regardless, considering that item templates are factory-like objects for controls (you 'spawn' a new set of controls for each item). You use the FrameworkElementFactory to model the subtree and then assign that the item template's VisualTree property.
I'm writing an app to play streaming music, In app I have many list (Ranking list, Search Result List, Highlight song list .....), Each list have a same datatemplate which I bind to a LongListSelector for each Page. So I use this datatemplate as resources and put it in app.xaml
<DataTemplate x:Key="BasicVideoTemplate">
<Grid Tap="ChangeSong_Tap" RowsAuto="50,50" ColumnsAuto="150,*" Background="White" Margin="5,0,5,10">
<Grid.ColumnDefinition>
<ColumnDefinition Width = "150"/>
<ColumnDefinition Width = "*"/>
</Grid.ColumnDefinition>
<Grid.RowDefinition>
<RowDefinition Height = "50"/>
<RowDefinition Height = "50"/>
</Grid.RowDefinition>
<Border BorderThickness="1" BorderBrush="Black" Grid.RowSpan="2" Grid.Column="0" VerticalAlignment="Center" Margin="5,0,5,0">
<Image Source="{Binding Cover}"/>
</Border>
<TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1" Style="{StaticResource BlackTextBlock}" Margin="5,0,0,0"/>
<TextBlock Text="{Binding Artist}" Grid.Row="1" Grid.Column="1" Foreground="Black" Margin="5,0,0,0"/>
<!-- .............. -->
</Grid>
</DataTemplate>
And this code (which i put in app.xaml.cs) to select a song from list, create a AudioTrack from this item and navigate to playSongPage:
private void ChangeSong_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var item = (SongItemModel)(sender as FrameworkElement).DataContext;
App.Model.ChangeSong(item.Id); /// this code will create a audio track for this item
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/Pages/DetailSongPage.xaml", UriKind.Relative));
}
Problem here is, I have to create a List< AudioTrack> for my playlist , So how can I get the parent list of clicked item and add it to the List< AudioTrack> , while all this code was put in app.xaml.cs ???
I would handle it in the SelectionChanged event of each longlistselector instead. The whole Tap thing on the grid doesn't sit well with me.
<phone:LongListSelector x:Name="myLSS" SelectionChanged="myLSS_SelectionChanged"/>
// event handler changes to
private void myLSS_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
LongListSelector lls = sender as LongListSelector; // get lls
var item = (SongItemModel) lls.SelectedItem;
App.Model.ChangeSong(item.Id); /// this code will create a audio track for this item
// now your ObservableCollection is just the ItemsSource, save a reference to it
// in the State manager so you can reference it on another page if you wish
ObservableCollection<SongItemModel> obs = (ObservableCollection<SongItemModel>) lls.ItemsSource;
PhoneApplicationService.Current.State["current_obs"] = obs;
// navigate..............
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/Pages/DetailSongPage.xaml", UriKind.Relative));
}
i have a listbox which has a data template binded to task from xml file,i want to delete selected template on button click,but its throws me an exception "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."
here is the code for xaml
<TabItem>
<Canvas Height="700" Width="850">
<Canvas.Resources>
<XmlDataProvider x:Key="Tasks" XPath="tasks"
Source="http://store.tymesheet.com/templates/Graphic-Designer.xml"/>
<DataTemplate x:Key="tasktemplate1">
<Canvas Height="50" Width="850" >
<Label Content="{Binding XPath=name}" Height="30"
Width="170" Canvas.Top="10" Canvas.Left="150"
Background="LightGray"/>
<TextBox Height="30" Width="120" Canvas.Top="10"
Canvas.Left="370" Background="AliceBlue"/>
<Label Canvas.Left="500" Canvas.Top="10">$</Label>
<Button Tag="{Binding}" Click="deletebuttonclick"
Canvas.Top="12" Height="10" Width="30"
Canvas.Left="600"/>
</Canvas>
</DataTemplate>
</Canvas.Resources>
<ListBox ItemTemplate="{StaticResource tasktemplate1}"
ItemsSource="{Binding Path=ChildNodes, Source={StaticResource Tasks}}"
x:Name="tasklistbox" Height="700" Width="850"/>
<Label Canvas.Top="-18" Canvas.Left="185">Select Task</Label>
<Label Canvas.Top="-18" Canvas.Left="377" RenderTransformOrigin="0.58,0.462">Enter Bill Rates</Label>
<Button Canvas.Left="39" Canvas.Top="575" Width="139">Click to add the task</Button>
</Canvas>
</TabItem>
here is the code behind for delete button
private void deletebuttonclick(object sender,RoutedEventArgs e)
{
tasklistbox.Items.Remove(tasklistbox.SelectedItem);
}
where am i wrong?,help.thanx.
The error that you displayed is fairly self explanatory:
Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
Clearly, as you should know, you used the ItemsSource property on the ListBox:
<ListBox ItemTemplate="{StaticResource tasktemplate1}"
ItemsSource="{Binding Path=ChildNodes, Source={StaticResource Tasks}}"
x:Name="tasklistbox" Height="700" Width="850" />
The error is telling you to Access and modify elements with ItemsControl.ItemsSource instead. So instead of not doing what it says:
private void deletebuttonclick(object sender,RoutedEventArgs e)
{
tasklistbox.Items.Remove(tasklistbox.SelectedItem);
}
Try actually doing what it says:
private void deletebuttonclick(object sender,RoutedEventArgs e)
{
tasklistbox.ItemsSource = null;
}
Or even better:
private void deletebuttonclick(object sender,RoutedEventArgs e)
{
ChildNodes = null;
}
UPDATE >>>
So it seems that we have another new user that asks one thing and when given that thing asks for another thing without so much as a 'thank you'... shame... a real shame.
First you need to data bind to the ListBox.SelectedItem property so that you know which item is selected:
<ListBox ItemTemplate="{StaticResource tasktemplate1}" SelectedItem="{Binding Item}"
ItemsSource="{Binding Path=ChildNodes, Source={StaticResource Tasks}}"
x:Name="tasklistbox" Height="700" Width="850" />
This Item property should be of the same type as the items in the ChildNodes collection... if it's not clear enough - you need to add that property next to your collection property. Then to remove that item in your handler, you just need to do this:
private void deletebuttonclick(object sender,RoutedEventArgs e)
{
ChildNodes.Remove(Item);
}
If you want to remove the item, then I would suggest to create ObservableCollection of XmlNode and bind ItemsSource with that. I suggested to use ObservableCollection because it implements INotifyCollectionChanged so whenever list is updated target which in your case is ListBox will automatically be updated.
In code behind
(Add System.Collections.ObjectModel namespace to use ObservableCollection<T>)
public MainWindow()
{
InitializeComponent();
XmlDocument doc = new XmlDocument();
doc.Load("http://store.tymesheet.com/templates/Software-Developer.xml");
var taskList = doc.ChildNodes.OfType<XmlNode>()
.Where(node => node.Name == "tasks")
.SelectMany(node => node.ChildNodes.OfType<XmlNode>());
Tasks = new ObservableCollection<XmlNode>(taskList);
this.DataContext = this;
}
public ObservableCollection<XmlNode> Tasks { get; set; }
private void deletebuttonclick(object sender, RoutedEventArgs e)
{
XmlNode selectedNode = ((Button)sender).DataContext as XmlNode;
Tasks.Remove(selectedNode);
}
Of course you need to update XAML as well:
<ListBox ItemsSource="{Binding Tasks}"
ItemTemplate="{StaticResource tasktemplate1}"
x:Name="listBox" Height="700" Width="850"/>