Xamarin Forms Collectionview not updating when observable collection is updated - c#

I have been stuck with this issue for quite sometime now. It might be quite simple for you guys.
I have a collection view which has its item source that shows an observable collection. It shows messages from users when the app starts and then as a new message comes, I want to add the new message as the 1st element in the collectionview but it gets distorted and removes the prior items (only from the UI and not the actual observable collection data) and only shows 1 item. And when I navigate to other page and come back it shows correctly. Could someone please help me with this.
Xaml
<CollectionView Grid.Row="1" x:Name="myMessagesCV" SelectionMode="Single" SelectionChanged="MyMessagesCV_SelectionChanged" RemainingItemsThresholdReached="MyMessagesCV_RemainingItemsThresholdReached" RemainingItemsThreshold="5">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="8, 8, 8, 0">
<Grid Padding="0" ColumnSpacing="0" RowSpacing="0" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ffimageloading:CachedImage x:Name="userImage" Source="{Binding userImage}" Aspect="AspectFill" HeightRequest="75" Grid.Row="0" Grid.Column="0" CacheType="All" DownsampleToViewSize="True">
<ffimageloading:CachedImage.Transformations>
<transformations:CircleTransformation/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<Grid Grid.Row="0" Grid.Column="1" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Padding="10, 0, 0, 5" Text="{Binding userName}" LineBreakMode="TailTruncation" TextColor="Black" FontSize="Medium" Grid.Row="0" Grid.Column="0"/>
<Label Padding="10, 0, 0, 5" Text="{Binding message}" FontAttributes="{Binding newMessage}" FontSize="Small" TextColor="Black" Grid.Row="1" Grid.Column="0" HorizontalOptions="StartAndExpand" />
<Image Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Source="dot.png" Aspect="AspectFill" WidthRequest="10" IsVisible="{Binding IsNewMessage}" HorizontalOptions="Center" VerticalOptions="Center"/>
</Grid>
<BoxView BackgroundColor="LightGray" HeightRequest="1" Grid.Row="1" Grid.Column="1"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code Behind Page
public ObservableCollection<Messages> MyMessagesList = new ObservableCollection<Messages>();
public async void GetMyMessages()
{
if (IsBusy)
return;
IsBusy = true;
var messages = await FirebaseDataHelper.GetMyMessages(uid);
var allmyMessages = await Task.WhenAll(FirebaseDataHelper.GetUserMessagesDetail(messages));
myunreadmsg = 0;
allmyMessagesCount = messages.Count;
IsSubscribe = false;
foreach (var message in allmyMessages)
{
if (message.Count > 0)
{
for (int i = 0; i < message.Count; i++)
{
if (message[i].status == "Delivered" && message[i].senderId != uid)
{
message[i].newMessage = FontAttributes.Bold;
message[i].IsNewMessage = true;
myunreadmsg++;
}
if (!MyMessagesList.Any(m => m.otheruserId == message[i].otheruserId) && message[i].message != null)
MyMessagesList.Add(message[i]);
}
}
}
myMessagesCV.ItemsSource = MyMessagesList;
}
public void GetMyNewMessages(Messages messageData)
{
IsSubscribe = false;
myunreadmsg = 0;
Messages newMessageData = new Messages();
if (messageData.status == "Delivered" && messageData.senderId != uid)
{
newMessageData.newMessage = FontAttributes.Bold;
newMessageData.IsNewMessage = true;
newMessageData.otheruserId = messageData.otheruserId;
newMessageData.senderId = messageData.senderId;
newMessageData.sellerId = messageData.sellerId;
myunreadmsg++;
}
else
{
newMessageData.newMessage = FontAttributes.None;
}
for (int i = 0; i < MyMessagesList.Count; i++)
{
if (MyMessagesList[i].otheruserId == messageData.otheruserId)
{
if (i == 0)
{
MyMessagesList[i].message = messageData.message;
if (myunreadmsg > 0)
{
MyMessagesList[i].IsNewMessage = true;
MyMessagesList[i].newMessage = FontAttributes.Bold;
}
break;
}
else
{
newMessageData.userName = MyMessagesList[i].userName;
newMessageData.userImage = MyMessagesList[i].userImage;
newMessageData.message = messageData.message;
newMessageData.time = messageData.time;
newMessageData.messageId = messageData.messageId;
MyMessagesList.Remove(MyMessagesList[i]);
MyMessagesList.Insert(0, newMessageData);
break;
}
}
}
myMessagesCV.ItemsSource = MyMessagesList;
}
Thank you guys. Hope I can solve this.

When your new message arrives, don't set ItemSource again like you do it in your GetMyNewMessages method:
myMessagesCV.ItemsSource = MyMessagesList;
Just insert your new message into MyMessagesList ObservableCollection.
MyMessagesList.Insert(0, newMessageData);
I made a simple project where it is demonstrated.
Here is xaml:
<ContentPage
x:Class="Search.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="White">
<Grid Margin="15">
<CollectionView
x:Name="CollectionView"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding}" TextColor="Red" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
And here is code behind of this page:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
public ObservableCollection<int> Data { get; set; } = new ObservableCollection<int>(Enumerable.Range(1, 10));
protected override void OnAppearing()
{
base.OnAppearing();
Device.StartTimer(TimeSpan.FromSeconds(3), () =>
{
Data.Insert(0, new Random().Next(1, 1000));
return true;
});
CollectionView.ItemsSource = Data;
}
}

Related

Positioning Label and Images components Xamarin Forms

I need something like this:
And I have this right now:
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="PetCare.Client.View.Forgot.ForgotPasswordView">
<ContentPage.Content>
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" ></ColumnDefinition>
<ColumnDefinition Width="auto" ></ColumnDefinition>
<ColumnDefinition Width="2*" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
x:Name="BackgroundTop">
</StackLayout>
<Image Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
x:Name="Icon"/>
<StackLayout Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Grid.RowSpan="4"
x:Name="BackgroundBot"
Spacing="0">
<Label Grid.Row="2"
Grid.Column="1"
x:Name="TextTitleLable"
/>
<StackLayout Grid.Row="3"
Grid.Column="1"
BackgroundColor="Red"
x:Name="BackgroundImage"
Spacing="0">
<Image x:Name="Phone"/>
</StackLayout>
<StackLayout Grid.Row="3"
Grid.Column="1"
BackgroundColor="Blue"
x:Name="BackgroundTextDescription"
Spacing="0">
<Label x:Name="TextDescription"></Label>
</StackLayout>
<StackLayout Grid.Row="4"
Grid.RowSpan="2"
x:Name="BackgroundInputNavigation">
<Entry x:Name="InputUser" />
<Label x:Name="ErrorMessage"/>
<Label x:Name="OtherOption"/>
</StackLayout>
</StackLayout>
<Button Grid.Row="8"
Grid.Column="1"
x:Name="BtnContinue"
Clicked="ClickedBtnContinue"/>
</Grid>
</ContentPage.Content>
</ContentPage>
C#:
//BackgrouTextndInputNavigation.
public ForgotPasswordView()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
SetColorsApp();
SetBtnContinueProperties();
SetTextTitleLableProperties();
SetIconProperties();
SetBackgroundTopProperties();
SetBackgroundBotProperties();
SetInputUserProperties();
SetOtherOptionProperties();
SetErrorMessageProperties();
SetPhoneProperties();
SetTextDescriptionProperties();
SetBackgroundImageProperties();
SetBackgroundTextDescriptionProperties();
}
private void SetBackgroundTextDescriptionProperties()
{
BackgroundTextDescription.Orientation = StackOrientation.Horizontal;
BackgroundTextDescription.HorizontalOptions = LayoutOptions.End;
BackgroundTextDescription.VerticalOptions = LayoutOptions.Center;
}
private void SetBackgroundImageProperties()
{
BackgroundImage.Orientation = StackOrientation.Horizontal;
BackgroundImage.HorizontalOptions = LayoutOptions.Start;
BackgroundImage.VerticalOptions = LayoutOptions.Center;
}
private void SetTextDescriptionProperties()
{
Label textPhoneNumber = new Label();
textPhoneNumber.Text = "XXXXX-XX89";
textPhoneNumber.TextColor = Color.FromHex(ColorsApp.ColorAppWarning);
textPhoneNumber.FontSize = 18;
string Text = "Foi enviada uma mensagem\ncom Código de Verificação\npara o Telefone "+ textPhoneNumber.Text+ "\ncadastrado em sua conta.\nInforme este Código para\nprosseguir.".Replace("\n", System.Environment.NewLine);
TextDescription.Text = Text;
TextDescription.FontSize = 18;
TextDescription.HorizontalTextAlignment = TextAlignment.End;
TextDescription.VerticalOptions = LayoutOptions.Center;
}
private void SetPhoneProperties()
{
Phone.Source = ImageSource.FromFile("phone.png");
Phone.HorizontalOptions = LayoutOptions.Start;
Phone.VerticalOptions = LayoutOptions.Center;
Phone.HeightRequest = 110;
}
private void SetErrorMessageProperties()
{
ErrorMessage.FontAttributes = FontAttributes.Bold;
ErrorMessage.Margin = new Thickness(0, 0, 0, 30);
}
private void SetOtherOptionProperties()
{
var tap = new TapGestureRecognizer();
tap.Tapped += async (s, e) => await Navigation.PushAsync(new OtherOptionResetPasswordView());
OtherOption.Text = "Usar outra opção de verificação";
OtherOption.FontSize = 18;
OtherOption.GestureRecognizers.Add(tap);
OtherOption.TextColor = Color.FromHex(ColorsApp.ColorAppPrimary);
tap = null;
}
private void SetInputUserProperties()
{
InputUser.Placeholder = "Inserir o Código";
InputUser.Margin = new Thickness(0, 20, 0, 0);
}
private void SetBackgroundBotProperties()
{
BackgroundBot.Padding = 30;
BackgroundBot.HeightRequest = 300;
BackgroundBot.HorizontalOptions = LayoutOptions.FillAndExpand;
BackgroundBot.VerticalOptions = LayoutOptions.FillAndExpand;
}
private void SetBackgroundTopProperties()
{
BackgroundTop.BackgroundColor = Color.FromHex(ColorsApp.ColorAppThemePrimary);
BackgroundTop.HorizontalOptions = LayoutOptions.FillAndExpand;
BackgroundTop.VerticalOptions = LayoutOptions.FillAndExpand;
BackgroundTop.Padding = 10;
}
private void SetIconProperties()
{
Icon.Source = ImageSource.FromFile("smartphone.png");
Icon.HorizontalOptions = LayoutOptions.Center;
Icon.VerticalOptions = LayoutOptions.Center;
}
private void SetTextTitleLableProperties()
{
TextTitleLable.HorizontalTextAlignment = TextAlignment.Start;
TextTitleLable.VerticalTextAlignment = TextAlignment.Start;
TextTitleLable.Text = "Verificar Identidade";
TextTitleLable.FontSize = 28;
TextTitleLable.TextColor = Color.FromHex(ColorsApp.ColorAppDark);
}
private void SetBtnContinueProperties()
{
BtnContinue.BackgroundColor = Color.FromHex(ColorsApp.ColorAppThemePrimary);
BtnContinue.VerticalOptions = LayoutOptions.End;
BtnContinue.HorizontalOptions = LayoutOptions.End;
BtnContinue.TextColor = Color.White;
BtnContinue.Padding = new Thickness(3);
BtnContinue.Margin = new Thickness(0, 0, 30, 30);
BtnContinue.Text = "Continuar";
BtnContinue.FontSize = 22;
BtnContinue.WidthRequest = 140;
}
private void SetColorsApp()
{
BackgroundBot.BackgroundColor = Color.FromHex(ColorsApp.ColorAppThemeDefault);
}
private async void ClickedBtnContinue(object sender, EventArgs e)
{
await Navigation.PushAsync(new ResetPasswordView());
}
I need two things: put the phone number in another color; and place the image next to the label.
I am very beginner, so I ask for some code along with the explanation. Please
It appears that even if the components are oriented horizontally, they
cannot stand side by side. I tried to put both in the same stack
layout but the stack layout height is based on the image, leaving one
or two lines of text hidden
Being honest with you, your Grid is a mess XD
First of all, you should be aware that by using Grid you should be able to get rid of most (if not all) of your Stacklayouts. Please take a look at the great docu that Xamarin has on Grid and many other topics :D
Next i share with you a Grid that i worked out to display what you want (notice the lack of Stacklayout!):
<Grid Padding="20" RowSpacing="70"
BackgroundColor="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Text="Verificar Identidade"
FontSize="30"/>
<Image Grid.Row="1" Grid.Column="0"
Source="TelefonImage"
WidthRequest="100"/>
<Label Grid.Row="1" Grid.Column="1"
FontSize="Medium"
HorizontalTextAlignment="End">
<Label.FormattedText>
<FormattedString>
<Span Text="Foi enviada uma mensagem com Código de Verificação para o Telefone "/>
<Span Text="XXXXX-XX89"
TextColor="Orange"/>
<Span Text=" cadastrado em sua conta. Informe este Código para prosseguir."/>
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>
On my side this looks like:
Also take note of what #Jason said in the comment: "everything you're doing in C# could be included in your XAML" (and that mean all the property setting).
I hope that will get you going!
Edit 1
I was able to quickly achieve the result in the image above by using the Hot Reload in Xamarin.Forms: play with it!

Xamarin page constrution XML and code behind

I am trying to create a menu page where I need to keep a button on screen even if a scroll, like button stops at the edge of the screen, and I have this layout in XML but I needed it in code behind to add some frame dynamic. This is what I made but it's not show up the same. Any suggestions please?
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
</OnPlatform>
</Grid.Margin>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="FIXED HEADER DEMO" Margin="12" Grid.Row="0" FontSize="14" />
<Grid x:Name="ContentGrid" RowSpacing="0" ColumnSpacing="0" Grid.Row="1">
<ScrollView x:Name="TheScroll">
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition x:Name="ImageRow" Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image x:Name="BearImage" Source="bear.jpg"
Aspect="AspectFill"
Grid.Row="0" />
<Label LineBreakMode="WordWrap"
Margin="12,5,12,5"
Grid.Row="1">
Text="abc"
</Label>
</Grid>
</ScrollView>
<Label x:Name="TitleText"
Text="Bear found in the wild!"
TextColor="White"
BackgroundColor="#FF264778"
HeightRequest="40"
VerticalOptions="Start" VerticalTextAlignment="Center"
HorizontalTextAlignment="Center" />
</Grid>
</Grid>
My code behind
public void PageConstructor()
{
Grid thirdG = new Grid
{
RowDefinitions =
{
new RowDefinition { Height = new GridLength(1,GridUnitType.Auto) },
new RowDefinition { Height = new GridLength(1,GridUnitType.Star) },
},
};
var image1 = new Image
{
Source = "bear.jpg",
};
var labeltext = new Label
{
LineBreakMode = LineBreakMode.WordWrap,
Text = msg + msg2 + msg3,
};
thirdG.Children.Add(image1, 0, 0);
thirdG.Children.Add(labeltext, 0, 1);
Grid secondG = new Grid
{
};
SecondTitle= new Label { Text = "Second title text", TextColor = Color.Black };
scrollV = new ScrollView { Content = thirdG };
secondG.Children.Add(SecondTitle);
secondG.Children.AddVertical(scrollV);
Grid firstG = new Grid
{
RowDefinitions =
{
new RowDefinition { Height = new GridLength(1,GridUnitType.Auto) },
new RowDefinition { Height = new GridLength(1,GridUnitType.Star) },
},
};
firstG.Children.Add(new Label { Text = "Title" }, 0, 0);
firstG.Children.Add(secondG, 0, 1);
Content = firstG;
}
According to your code, I find you use ScrollView in Grid, it will be covered by label TitleText, so I suggest you can put scrollview outside the grid
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Margin="12"
FontSize="14"
Text="FIXED HEADER DEMO" />
<ScrollView Grid.Row="1">
<Grid x:Name="ContentGrid">
<Grid.RowDefinitions>
<RowDefinition x:Name="ImageRow" Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image
x:Name="BearImage"
Grid.Row="0"
HeightRequest="50"
Source="a11.jpg"
WidthRequest="50" />
<Label
Grid.Row="1"
Margin="12,5,12,5"
LineBreakMode="WordWrap">
Text="abc"
</Label>
<Label
x:Name="TitleText"
BackgroundColor="#FF264778"
HeightRequest="40"
HorizontalTextAlignment="Center"
Text="Bear found in the wild!"
TextColor="White"
VerticalOptions="Start"
VerticalTextAlignment="Center" />
</Grid>
</ScrollView>
</Grid>
Same effect in code behind:
public Page3()
{
InitializeComponent();
var image1 = new Image
{
Source = "a11.jpg",WidthRequest=70,HeightRequest=70
};
var label1 = new Label
{
Text = "abc"
};
var label2 = new Label
{
Text = "Bear found in the wild!",HeightRequest=20,VerticalTextAlignment=TextAlignment.Center,HorizontalTextAlignment=TextAlignment.Center
};
Grid secondG = new Grid
{
RowDefinitions =
{
new RowDefinition { Height = new GridLength(1,GridUnitType.Auto) },
new RowDefinition { Height = new GridLength(1,GridUnitType.Star) },
},
};
var scrollV = new ScrollView { Content = secondG };
secondG.Children.Add(image1,0,0);
secondG.Children.Add(label1,0,1);
secondG.Children.Add(label2);
Grid firstG = new Grid
{
RowDefinitions =
{
new RowDefinition { Height = new GridLength(1,GridUnitType.Auto) },
new RowDefinition { Height = new GridLength(1,GridUnitType.Star) },
},
};
var labeltext = new Label
{
LineBreakMode = LineBreakMode.WordWrap,FontSize=14,
Text = "FIXED HEADER DEMO"
};
firstG.Children.Add(labeltext, 0, 0);
firstG.Children.Add(scrollV, 0, 1);
Content = firstG;
}
}

How to display items from different types in a Table in Xamarin Forms

Desired Result :
I have a list of objects that contains the Title of every section in the menu and in this list I have the list of images and other information
I've been strugeling on how to display them properly
my xaml :
<ContentPage.Content>
<TableView Intent="Form">
<TableRoot>
<TableSection Title="Getting Started">
<ViewCell>
<Grid VerticalOptions="FillAndExpand" Padding = "20, 0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" ></RowDefinition>
<RowDefinition Height="Auto" ></RowDefinition>
</Grid.RowDefinitions>
<StackLayout Grid.Row="0" x:Name="Titre" Orientation="Horizontal">
</StackLayout>
<ScrollView Grid.Row="1">
<StackLayout
Padding="0"
x:Name="Image">
</StackLayout>
</ScrollView>
</Grid>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</ContentPage.Content>
The C# Method :
private void PopulateProductsLists(List<PlanDefinition> listArticles)
{
for (var i = 0; i < listArticles.Count; i++)
{
//Display Title
Titre.Children.Add((new Label { Text = listArticles[i].Name, Style = Device.Styles.TitleStyle, FontAttributes = FontAttributes.Bold, TextColor = Color.White }));
//Display Images from listArticles[i].ArticlesAvailable
for (int j= 0; j < listArticles[i].ArticlesAvailable.Count; j++)
{
listArticles[i].ArticlesAvailable[j].ImageProduit = "https://fit-plans.com/" + listArticles[i].ArticlesAvailable[j].UrlSmallImage;
Image image = new Image();
image.Source = ImageSource.FromUri(new Uri(listArticles[i].ArticlesAvailable[j].ImageProduit));
Image.Children.Add(image);
}
What I am droing wrong?
First of all here were my mistakes :
1- Not using a scrollable view
2- Using different stack layouts
The remedy to my issue :
<!----Scrollable view MUST be used otherwide data simply won't appear --->
<ScrollView
Grid.Column="0"
Grid.Row="1">
<Grid
RowSpacing="3"
x:Name="MyGrid"
ClassId="myGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
x:Name="ProduitsLayout">
<!----Products will be displayed here --->
</StackLayout>
</Grid>
</ScrollView>
And in the code behind :
private void PopulateProductsLists(List<PlanDefinition> listArticles)
{
int labelIndex = 0;
int count = 0;
for (var i = 0; i < listArticles.Count; i++)
{
//Calling a view to display data
item = new PlanOrderTemplate();
var lastHeight = "100";
var y = 0;
int column = 1;
var productTapGestureRecognizer = new TapGestureRecognizer();
//Add label in the stack layout
ProduitsLayout.Children.Add((new Label
{
Text = listArticles[i].Name,
Style = Device.Styles.TitleStyle,
FontAttributes = FontAttributes.Bold,
TextColor = Color.Black
}));
for (int j = 0; j < listArticles[i].ArticlesAvailable.Count; j++)
{
productTapGestureRecognizer.Tapped += OnProductTapped;
item = new PlanOrderTemplate();
listArticles[i].ArticlesAvailable[j].ThumbnailHeight = lastHeight;
//Applying binding to the view in order to display data
item.BindingContext = listArticles[i].ArticlesAvailable[j];
item.GestureRecognizers.Add(productTapGestureRecognizer);
//This is mandatory you need to call the add the view page to the stack layout
ProduitsLayout.Children.Add(item);
}
}
}
Now as for the view page I'm talking about : ProduitsLayout.xaml
Configure the image I want to load with a stepper
<StackLayout Orientation="Vertical"
Spacing="1">
<ffimageloading:CachedImage
FadeAnimationEnabled="true"
Source="{Binding ImageProduit}"
HeightRequest="{Binding ThumbnailHeight}"
Aspect="AspectFill"/>
<Label x:Name="lbldisp" Text="1" VerticalOptions="Center" HorizontalOptions="Center"></Label>
<Stepper Minimum="0" Maximum="10" Increment="1" HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand" ValueChanged="OnValueChanged" />
</StackLayout>

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 find element in visual tree in wp7?

I need to find element in visual tree. For example I have a grid and I need to set my own text in tbox:WatermarkTextBox when ExpanderView expands.
xaml
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" >
<Border/>
</Grid>
<Grid Grid.Row="0" />
<Grid Grid.Row="0" Grid.Column="2" />
<toolkit:ExpanderView Expanded="setText" Collapsed="hideAppBar">
<Image Height="100" Margin="-53,0,0,0"/>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="expText" IsExpanded="False" Tag="{Binding}" Grid.Row="1" Grid.Column="1" VerticalContentAlignment="Stretch" Grid.ColumnSpan="2" Background="White" Foreground="Black" BorderBrush="White">
<tbox:WatermarkTextBox TextChanged="DisableOrEnable" TextWrapping="Wrap" AcceptsReturn="True" WatermarkText="Введите комментарий" BorderThickness="0" InputScope="Chat" Margin="-51,0,0,0" Padding="0" Background="White" BorderBrush="White"/>
</toolkit:ExpanderView>
...many elements
</Grid>
c#
public string message="my message";
private void setText(object sender, RoutedEventArgs e)
{
setMessage(((sender as ExpanderView).Parent as Grid));
}
Function that recursively searching through visual tree and set value to the element that you need:
public void setMessage(DependencyObject parent)
{
if (parent == null)
{
return;
}
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null && frameworkElement is WatermarkTextBox/*Type of element that you need*/)
{
(frameworkElement as WatermarkTextBox).Text = message;/*Value that you need*/
break;
}else
if (frameworkElement != null)
{
int CountInChildren = VisualTreeHelper.GetChildrenCount(frameworkElement);
for (int z = 0; z < CountInChildren; z++)
{
DependencyObject child1 = VisualTreeHelper.GetChild(frameworkElement, z);
setMessage(frameworkElement);
}
}
}
}

Categories

Resources