How to get UIElement at specific position in ItemsControl in WP7? - windows-phone-7

I filled the icBoard with 50 Cell objects, so each Rectangle object has Cell as data object. Now, I want according to index or cell object to get the corresponding Rectangle element. For example I want to get the Rectangle in index=15. Not it's data but the Rectangle itself.
How I can do this?
public MainPage()
{
InitializeComponent();
var cells = new List<Cell>();
for (int i = 0; i < 50; i++)
{
cells.Add(new Cell());
}
icCells.ItemsSource = cells;
}
public void sector_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//some code
//....
var tappedRectangle = (sender as Rectangle);
var spesificRectangle = SOMEHOW_GET_RECTANGLE_AT_POSITION_15;
}
<ItemsControl Name="icBoard" Grid.Column="0" Margin="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Fill="#501e4696" Width="30" Height="30" Margin="1" Tap="sector_Tap" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

I believe this might work:
ContentPresenter contentPresenter = itemsControl.ItemContainerGenerator.ContainerFromIndex(15) as ContentPresenter;
Rectangle rectangle= FindVisualChild<Rectangle>(contentPresenter );
if (rectangle != null)
{
}
public static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}

Related

Xamarin binding many entrys texts for each element of array

I'm programmatically creating entrys and programmatically trying to bind every entry text to appropriate array element (based on index). But it seems like it is not binding.
Trying to implement a mvvm. Can someone help with this problem?
This is a view part:
var saveMeasurmentViewModel = new SaveMeasurementViewModel();
BindingContext = saveMeasurmentViewModel;
var radius = Convert.ToInt32(DeviceDisplay.MainDisplayInfo.Width * 0.32);
var entryList = new List<Entry>();
for (int index = 0; index < 18; index++)
{
var entry = new Entry
{
Placeholder = (index + 1).ToString(),
TabIndex = index + 1,
};
object actualValueForBinding = saveMeasurmentViewModel.ActualValues[index];
entry.SetBinding(Entry.TextProperty, nameof(actualValueForBinding));
var frame = new Frame
{
Content = entry,
Margin = new Thickness(Math.Sin(20 * index * Math.PI / 180) * radius, 0, 0, Math.Cos(20 * index * Math.PI / 180) * radius),
};
entryList.Add(entry);
MainGrid.Children.Add(frame, 0, 2, 0, 1);
}
ViewModel property, that i'm trying to bind:
public double[] ActualValues
{
get { return actualValues; }
set
{
actualValues = value;
OnPropertyChanged();
}
}
In your case , you can't bind the text of entry to the list directly . It would be better to use BindableLayout
in xaml
<ScrollView Orientation="Both">
<StackLayout BindableLayout.ItemsSource="{Binding ActualValues}" x:Name="MainGrid" HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<!-- Place new controls here -->
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Margin="{Binding Margin}" you can also set margin with data-binding -->
<Frame Grid.Row="0" Margin="10,10" WidthRequest="90" HeightRequest="50" BackgroundColor="LightBlue">
<Entry TabIndex="{Binding Id}" Text="{Binding Value}" WidthRequest="80" HeightRequest="30" TextColor="Red" />
</Frame>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
in Viewmodel
public class MyModel:INotifyPropertyChanged
{
public MyModel(int id)
{
Value = "entry" + id.ToString();
Id = id;
var radius = Convert.ToInt32(DeviceDisplay.MainDisplayInfo.Width * 0.32);
// Margin = new Thickness(Math.Sin(20 * id * Math.PI / 180) * radius, 0, 0, Math.Cos(20 * id * Math.PI / 180) * radius);
}
public event PropertyChangedEventHandler PropertyChanged;
public Thickness Margin { get; set; }
public int Id { get; set; }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string _value;
public string Value
{
get
{
return _value;
}
set
{
if (value != _value)
{
_value = value;
OnPropertyChanged("Value");
}
}
}
}
public class SaveMeasurementViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MyModel> ActualValues { get;set; }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SaveMeasurementViewModel()
{
ActualValues = new ObservableCollection<MyModel>() { };
for (int index = 0; index < 18; index++)
{
ActualValues.Add(new MyModel(index) );
}
}
}
For more details you could check https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/bindable-layouts

Xamarin Forms: How to Change the textcolor of Collectionview SelectedItem?

I have a CarouselPage having 5 children and every child has a horizontal collection view. When selecting an item in Collectionview or swiping the pages, I need to give a different text color and need to add an underline for the selected item. I have tried like below:
CarouselHomePage.cs
public partial class CarouselHomePage : CarouselPage
{
public List<Activity> activityList { get; set; }
public CarouselHomePage()
{
InitializeComponent();
activityList = new List<Activity>();
AddActivities();
MessagingCenter.Subscribe<App, string>((App)Xamarin.Forms.Application.Current, "child", (s, child) =>
{
CurrentPage = Children[Int32.Parse(child)];
});
}
private void AddActivities()
{
activityList.Add(new Activity() { Title = "PageNumber1" });
activityList.Add(new Activity() { Title = "PageNumber2" });
activityList.Add(new Activity() { Title = "PageNumber3" });
activityList.Add(new Activity() { Title = "PageNumber4" });
activityList.Add(new Activity() { Title = "PageNumber5" });
AddChild(activityList);
}
public void AddChild(List<Activity> activityList)
{
this.Children.Add(new PageNumber1(activityList));
this.Children.Add(new PageNumber2(activityList));
this.Children.Add(new PageNumber3(activityList));
this.Children.Add(new PageNumber4(activityList));
this.Children.Add(new PageNumber5(activityList));
}
}
Activity.cs
public class Activity
{
public string Title { get; set; }
public bool visibility { get; set; }
public bool Visibility
{
set
{
if (value != null)
{
visibility = value;
NotifyPropertyChanged();
}
}
get
{
return visibility;
}
}
private Color textColor;
public Color TextColor
{
set
{
if (value != null)
{
textColor = value;
NotifyPropertyChanged();
}
}
get
{
return textColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
PageNumber1.xaml
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<CollectionView
SelectionMode="Single"
x:Name="ActivityList"
Margin="5,10,5,10"
SelectionChanged="TagItemTapped"
ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout
Orientation="Vertical"
Margin="15">
<Label
TextColor="{Binding TextColor}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Text="{Binding Title}">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>18</OnIdiom.Phone>
<OnIdiom.Tablet>27</OnIdiom.Tablet>
<OnIdiom.Desktop>18</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<BoxView
HeightRequest="2"
IsVisible="{Binding Visibility}"
BackgroundColor="{Binding TextColor}"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Start"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>30</OnIdiom.Phone>
<OnIdiom.Tablet>60</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</CollectionView.HeightRequest>
</CollectionView>
<Label Text="Welcome to PageNumber1"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
PageNumber1.xaml.cs
public partial class PageNumber1 : ContentPage
{
public PageNumber1(List<Activity> activityList)
{
InitializeComponent();
if (activityList == null)
{
ActivityList.IsVisible = false;
}
else
{
for (int i = 0; i < activityList.Count; i++)
{
if (activityList[i].Title == "PageNumber1")
{
activityList[i].TextColor = Color.FromHex("#26b4d8");
activityList[i].Visibility = true;
}
else
{
activityList[i].TextColor = Color.Gray;
activityList[i].Visibility = false;
}
}
ActivityList.ItemsSource = activityList;
}
}
public void TagItemTapped(object sender, SelectionChangedEventArgs e)
{
var selectedItem = (e.CurrentSelection.FirstOrDefault() as Activity);
if (selectedItem != null)
{
string childnumber = "";
if (selectedItem.Title == "PageNumber1")
{
childnumber = "0";
}
else if (selectedItem.Title == "PageNumber2")
{
childnumber = "1";
}
else if (selectedItem.Title == "PageNumber3")
{
childnumber = "2";
}
else if (selectedItem.Title == "PageNumber4")
{
childnumber = "3";
}
else if (selectedItem.Title == "PageNumber5")
{
childnumber = "4";
}
MessagingCenter.Send<App, string>((App)Xamarin.Forms.Application.Current, "child", childnumber);
}
}
}
I have added the same code on all the other child pages with the corresponding title in the if statement. But the selected page title color is not working and underline is not showing.
Screenshot:
Also if I select the last item in the collectionview, I need to scroll the collection on the last child to the last item. For this I have used ScrollTo feature of Collectioview. But that is also not working.
protected override void OnAppearing()
{
ActivityList.ScrollTo(4);
}
The above code will work if I manually swipe the pages. When directly tap the collectionview item, the scrolling is not working.
I have uploaded a sample project here.
About underline not showing , the reason is HeightRequest of CollectionView setted too small with 30 .
Modify that to above 35 , it will show correcttly . Such as :
<CollectionView.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>40</OnIdiom.Phone>
<OnIdiom.Tablet>60</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</CollectionView.HeightRequest>
The effect :
About selected problem , this is the sample project here .

dynamically add button or image from code behind to pivotitem

How can I add many Button to the grid for the first Item and and textbox to another grid in the second item dynamically?
NOT XAML
Ican't find name GridItem - name not exist in code behind :(
I tried to find Visual Tree Helper :(
PivotItem pivotVHT = (PivotItem)mainSecondPivot.SelectedItem;
foreach (var element in VisualTreeHelper.FindElementsInHostCoordinates(new Rect(20, 0, 480, 700), pivotVHT))
{
if (element is TextBlock)
{
Debug.WriteLine("{0}", ((TextBlock)element).Text);
TextBlock test = ((TextBlock)element);
test.Text = "TEST";
}
}
VisualTreeHelper changing the text only mainFirstPivot, visual tree helper does not see mainSecondPivot
XAML:
<controls:Pivot Title="Photo Gallery" Name="mainSecondPivot" >
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<Grid
x:Name="PivitGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image
Name="PivotImageGalery"
Source="{Binding imgSrc}"
>
</Image>
<TextBlock
x:Name="TextBlockPivot"
Text="{Binding textBlockPivotName}"
>
</TextBlock>
</Grid>
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:Pivot.ItemTemplate>
<DataTemplate>
<ScrollViewer
Name="SVName"
Width="Auto"
VerticalScrollBarVisibility ="Hidden"
HorizontalScrollBarVisibility="Disabled"
>
<Grid
x:Name="GridItem"
>
**HERE**
</Grid>
</ScrollViewer>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
C#
public static class SelectedIndex
{
public static int SelectedIndexInt = 0;// OR SOME NUMBER
}
public class IListPivot
{
public ImageSource imgSrc { get; set; }
public String textBlockPivotName { get; set; }
}
public secondPage()
{
InitializeComponent();
IList<IListPivot> PivotList = new List<IListPivot>();
for (z = 0; z <= 7; z++)
{
PivotList.Add(new IListPivot()
{
imgSrc = new System.Windows.Media.Imaging.BitmapImage(new Uri("URI", UriKind.Relative)),
textBlockPivotName = "TEXT"
});
}
mainSecondPivot.ItemsSource = PivotList;
mainSecondPivot.Loaded += new RoutedEventHandler (PivotLoaded);
mainSecondPivot.SelectedIndex = SelectedIndex.SelectedIndexInt
}
public void PivotLoaded(object sender, EventArgs e)
{
PivotItem pivotItemVHT = (PivotItem)mainSecondPivot.ItemContainerGenerator.ContainerFromIndex(SelectedIndex.SelectedIndexInt);
var root = VisualTreeHelper.GetChild(((VisualTreeHelper.GetChild(pivotItemVHT, 0) as Grid).Children[0] as ContentPresenter), 0) as FrameworkElement;
Debug.WriteLine(" root " + root);
Debug.WriteLine(" root Name " + root.Name);
ScrollViewer scr = (ScrollViewer)root;
TextBox BoxText1 = new TextBox();
BoxText1.Text = a.ToString();
scr.Content = BoxText1;
}
add only to one from everyone items
HELP
Here is how to retrieve a named element from inside a PivotItem:
public FrameworkElement GetPivotElement(int pivotIndex, string name)
{
var pivot = mainSecondPivot.ItemContainerGenerator.ContainerFromIndex(pivotIndex);
var root = VisualTreeHelper.GetChild(((VisualTreeHelper.GetChild(pivot, 0) as Grid).Children[0] as ContentPresenter), 0) as FrameworkElement;
return root.FindName(name);
}

WP7 - VisualTreeHelper to loop through all ListBox Items

I need to create a new ListBox based on items that are selected (checked). The following code actually worked if I only had like 20 items on the ListBox, but adding more items make it crash. Can anybody know how to make it work, or have a different aproach? Is there a limite for looping through a listBox?
// worked fine for 20 items,
// but my actual list contems 95 items...
private void btnCreateNewList_Click(object sender, RoutedEventArgs e)
{
int totalItemsCB = ListCheckBoxVocabulary.Items.Count;
for (int ii = 0; ii < totalItemsCB-1; ii++)
{
ListBoxItem item = this.ListCheckBoxVocabulary.ItemContainerGenerator.ContainerFromIndex(ii) as ListBoxItem;
CheckBox thisCheckBox = FindFirstElementInVisualTree<CheckBox>(item);
if (thisCheckBox.IsChecked == true)
{
dataPlayListSource.Add(new SampleData() { Text = thisCheckBox.Content.ToString() + " | " + ii });
// this.PlayListCheckBoxVocabulary.UpdateLayout();
this.PlayListCheckBoxVocabulary.ItemsSource = dataPlayListSource;
}
}
}
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
and xaml:
<controls:PivotItem Header="Vocabulary" >
<ListBox x:Name="ListCheckBoxVocabulary" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--<StackPanel Margin="0,0,0,17" Width="432">-->
<CheckBox x:Name="cbVocabulary" Content="{Binding Text}" Checked="CheckBox_Checked" Unchecked="UncheckBox" />
<!--</StackPanel>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PivotItem>
The list is virtual - controls are created as they are needed and potentially reused (I think).
Your options are to turn the ListBox to not be virtualized (override the template, and for the container, instead of a SerializedStackPanel, choose a regular StackPanel).
Your other (and preferable) option is to do the checking via Data Binding. Way easier and faster in most situations.

Why is the Projection property null?

<ListBox Name="listBoxButtons"
Height="700">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="{StaticResource PhoneAccentBrush}"
Name="border"
Width="432" Height="62"
Margin="6" Padding="12,0,0,6">
<TextBlock Text="{Binding}"
Foreground="#FFFFFF" FontSize="26.667"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"/>
<Border.Projection>
<PlaneProjection RotationX="-60"/>
</Border.Projection>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code:
private void ShowAnim()
{
IEasingFunction quadraticEase = new QuadraticEase { EasingMode = EasingMode.EaseOut };
Storyboard _swivelShow = new Storyboard();
foreach (var item in this.listBoxButtons.Items)
{
UIElement container = listBoxButtons.ItemContainerGenerator.ContainerFromItem(item) as UIElement;
if (container != null)
{
Border content = VisualTreeHelper.GetChild(container, 0) as Border;
if (content != null)
{
DoubleAnimationUsingKeyFrames showAnimation = new DoubleAnimationUsingKeyFrames();
EasingDoubleKeyFrame showKeyFrame1 = new EasingDoubleKeyFrame();
showKeyFrame1.KeyTime = TimeSpan.FromMilliseconds(0);
showKeyFrame1.Value = -60;
showKeyFrame1.EasingFunction = quadraticEase;
EasingDoubleKeyFrame showKeyFrame2 = new EasingDoubleKeyFrame();
showKeyFrame2.KeyTime = TimeSpan.FromMilliseconds(85);
showKeyFrame2.Value = 0;
showKeyFrame2.EasingFunction = quadraticEase;
showAnimation.KeyFrames.Add(showKeyFrame1);
showAnimation.KeyFrames.Add(showKeyFrame2);
Storyboard.SetTargetProperty(showAnimation, new PropertyPath(PlaneProjection.RotationXProperty));
Storyboard.SetTarget(showAnimation, content.Projection);
_swivelShow.Children.Add(showAnimation);
}
}
}
_swivelShow.Begin();
}
But: Storyboard.SetTarget(showAnimation, content.Projection) throws an Exception. The content.Projection is null. How could that happen?
You are looking at the wrong Border element. The hierarchy of the item container in your case is like Border -> ContentControl -> ContentPresenter -> Border (this is yours). So you have to go deeper down the child hierarchy of your container to find the border you want.
The following code searches the children of an UIElement recursively and gives some debug output so you can see how deep it goes. It will stop when it finds a control named "border":
private UIElement GetMyBorder(UIElement container)
{
if (container is FrameworkElement && ((FrameworkElement)container).Name == "border")
return container;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
{
var child = (FrameworkElement)VisualTreeHelper.GetChild(container, i);
System.Diagnostics.Debug.WriteLine("Found child "+ child.ToString());
System.Diagnostics.Debug.WriteLine("Going one level deeper...");
UIElement foundElement = GetMyBorder(child);
if (foundElement != null)
return foundElement;
}
return null;
}
To use it:
Border content = (Border)GetMyBorder(container);

Resources