I have a ListView and its ItemsSource.
ListView IList = new ListView();
IList.ItemsSource = _routine_names;
In order to customize the Data Template for each item I'm doing:
IList.ItemTemplate = new DataTemplate(()=>
{
Label Routine_Name = new Label(); // should be_routine_names
return new ViewCell
{
View = new StackLayout{
Children = {Routine_Name}
}
};
});
When I run this my ListView is displayed and the list items are indeed there (they have onclick handlers that work) but there is no text, which should be whatever is in _routine_names.
My question how do I get the Label in the DataTemplate to be items in _routine_names?
The reason I'm adding a DataTemplate is so I can add swipe to delete.
You can just use the built in TextCell for what you're trying to do. It has a bindable TextProperty and an optional DetailProperty if you want a smaller text line below the main one.
IList.ItempTemplate = new DataTemplate(()=>
{
var textCell = new TextCell();
textCell.ContextActions.Add(yourAction);
textCell.SetBinding(TextCell.TextProperty, ".");
return textCell;
}
IList.ItemsSource = _routine_names;
yourAction is of type MenuItem as seen here
Also, please notice that IList is a name of a class in System.Collection namespace so I'd use something else there.
Related
I have created a data template to use within a list view. This will later be expanded to add more content to each item in this list view. At the moment all the items that are bound to the observable collection are working as expected, except for one.
In each instance of the data template the bound properties are height, RouteName and routeStops. The height and RouteName are working fine but I'm not sure how to bind the routeStops correctly.
For each one of the RouteNames there are multiple stops, so for each data template use there must be one label that has the RouteName and multiple labels for each stop on the route (using routeStops).
I am not entirely sure how to achieve this, I can only seem to bind one stop to one label. I want to create them dynamically to allow for any amount of stops.
So the code behind that creates the data template (Just the constructor):
public MainRoutePageViewDetail(MessagDatabase database)
{
InitializeComponent();
BindingContext = mainroutepageviewmodel = new MainRoutePageViewModel(database,Navigation);
StackLayout mainstack = new StackLayout();
var routelisttemplate = new DataTemplate(() => {
ViewCell viewcell = new ViewCell();
stacklayout = new StackLayout();
stacklayout.SetBinding(StackLayout.HeightRequestProperty,"height");
viewcell.View = stacklayout;
// labels for template
var nameLabel = new Label { FontAttributes = FontAttributes.Bold, BackgroundColor = Color.LightGray };
nameLabel.HorizontalOptions = LayoutOptions.Center;
nameLabel.VerticalOptions = LayoutOptions.Center;
nameLabel.SetBinding(Label.TextProperty, "RouteName");
//inforLabel.SetBinding(Label.TextProperty, "Stops");
stacklayout.Children.Add(nameLabel);
StackLayout nextstack = new StackLayout();
var nameLabel2 = new Label { FontAttributes = FontAttributes.Bold, BackgroundColor = Color.Red };
nameLabel2.HorizontalOptions = LayoutOptions.Center;
nameLabel2.VerticalOptions = LayoutOptions.Center;
nameLabel2.SetBinding(Label.TextProperty, "routeStops");
nextstack.Children.Add(nameLabel2);
stacklayout.Children.Add(nextstack);
return viewcell;
});
ListView listviewofroutes = new ListView();
mainstack.Children.Add(listviewofroutes);
listviewofroutes.SetBinding(ListView.ItemsSourceProperty, "routeLabels");
listviewofroutes.ItemTemplate = routelisttemplate;
listviewofroutes.HasUnevenRows = true;
Content = mainstack;
}// end of constructor
This is bound to an ObservableCollection in the view model. Im going to leave this out as its irrelevant because the bindings work fine.
This calls down to functions in the model that collect data from SQL tables.
The function in the model that collects data:
public List<RouteInfo> getrouteInfo()
{
var DataBaseSelection = _connection.Query<RouteInfoTable>("Select * From [RouteInfoTable]");
List<RouteInfo> dataList = new List<RouteInfo>();
for (var i = 0; i < DataBaseSelection.Count; i++)
{
var DataBaseSelection2 = _connection.Query<RouteStopsTable>("Select StopOnRoute From [RouteStopsTable] WHERE RouteName = ? ",DataBaseSelection[i].RouteName);
dataList.Add(new RouteInfo
{
ID = DataBaseSelection[i].ID,
RouteName = DataBaseSelection[i].RouteName,
Stops = DataBaseSelection[i].Stops,
DayOf = DataBaseSelection[i].DayOf,
IsVisible = DataBaseSelection[i].IsVisible,
routeStops = DataBaseSelection2[i].StopOnRoute,
height = 200
});
}
return dataList;
}
The first table (RouteInfoTable) gets RouteName and some other information and the second table gets the stops on the route using the RouteName as a key. This is all added to a list of RouteInfo instances.
DataBaseSelection2 grabs all of the stops on the route but only one of them displays. I know why this is but I dont know how to display all three.
The Table definitions and class definitions as well as the selections from the tables are not an issue. I have debugged these and they are getting the correct information I just dont know how to display it on the front end in the way I want to. Here is a visual of what I mean if its getting complicated:
The best I can do is one route stop not all three.
An ideas how to achieve this?
Sorry if its complicated.
You can use Grouping in ListView to achieve this visual. Basically in this case you will be defining two DataTemplate(s) -
GroupHeaderTemplate for RouteName
ItemTemplate for StopsOnRoute.
I have a problem with a ListView. I want each Cell to have a label and a switch but the text of the label does not appear.
Here is my code:
public class FilterPage : ContentPage
{
public FilterPage()
{
List<FilterCell> listContent = new List<FilterCell>();
foreach(string type in Database.RestaurantTypes)
{
FilterCell fc = new FilterCell();
fc.Text = type;
listContent.Add(fc);
}
ListView types = new ListView();
types.ItemTemplate = new DataTemplate(typeof(FilterCell));
types.ItemsSource = listContent;
var layout = new StackLayout();
layout.Children.Add(types);
Content = layout;
}
}
public class FilterCell : ViewCell
{
private Label label;
public Switch CellSwitch { get; private set; }
public String Text{ get { return label.Text; } set { label.Text = value; } }
public FilterCell()
{
label = new Label();
CellSwitch = new Switch();
var layout = new StackLayout
{
Padding = new Thickness(20, 0, 0, 0),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { label, CellSwitch }
};
View = layout;
}
}
If I enter a fixed Text in the FilterCell-Constructor it works fine (e.g.: label.Text = "Hello World")
When I create a Method for the ItemSelected-Event and read out the SelectedItem.Text Property I get the text I assigned as Value but it's never displayed. Only the switch is displayed when I try to run this Code.
Thanks for your help
Niko
Ohh boy. This code looks like a rape (sorry I had to say this).
Now let's see what's wrong:
The reason is you are mixing up data and view heavily.
The line
types.ItemTemplate = new DataTemplate(typeof(FilterCell));
means: "For each item in the list (ItemsSource) create a new filter cell". The FilterCells that you create in the loop are never displayed.
The easy fix
public class FilterPage : ContentPage
{
public FilterPage()
{
var restaurantTypes = new[] {"Pizza", "China", "German"}; // Database.RestaurantTypes
ListView types = new ListView();
types.ItemTemplate = new DataTemplate(() =>
{
var cell = new SwitchCell();
cell.SetBinding(SwitchCell.TextProperty, ".");
return cell;
});
types.ItemsSource = restaurantTypes;
Content = types;
}
}
There is a standard cell type that contains a label and a switch SwitchCell, use it.
As ItemsSource of your list, you have to use your data. In your case the list of restaurant types. I just mocked them with a static list.
The DataTemplate creates the SwitchCell and sets the Databinding for the Text property. This is the magic glue between View and data. The "." binds it to the data item itself. We use it, because our list contains items of strings and the Text should be exactly the string. (read about Databinding: https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/#Data_Binding )
I striped away the StackLayout that contained the list. You can directly set the list as Content of the page.
Lesson
use standard controls, if possible
You should always try to remember to keep data and view apart from each other and use data binding to connect to each other.
Try to avoid unnecessary views.
I am adding tabs to my tab control through code:
TabItem tab = new TabItem();
var stack = new StackPanel() { Orientation = Orientation.Horizontal };
stack.Children.Add(new TextBlock() { Text = header });
stack.Children.Add(new TextBlock() { Name = "extra" });
tab.Header = stack;
tabControl.Items.Add(tab);
As you can see, it creates the header of the tabItem with a stack panel. It adds two text blocks; one of which is empty, but I've assigned the name "extra". What I would like to do is, later in the code, edit the textBlock named "extra" and add some new text to it.
How would I find and edit this element? I have tried the following code, but its producing an error saying the element can not be found:
object test = Application.Current.FindResource("extra");
FindName is what you are looking for but your TextBlock is not in the correct WPF Namescope.
MSDN states:
If you add an object to an object tree at a point in time after the XAML that produced that tree was parsed, a Name or x:Name value on the new object does not automatically update the information in a XAML namescope. To add a name for an object into a WPF XAML namescope after XAML is loaded, must call the appropriate implementation of RegisterName on the object that defines the XAML namescope.
For example:
var textBlock = new TextBlock() { Name = "extra" };
stack.Children.Add(textBlock );
RegisterName(textBlock);
...
TextBlock textBlock = FindName("extra") as TextBlock;
Finally, Application.Current.FindResource("extra") is returning null because the element does not exist when project resources are created. More on FindResource.
Just use FrameworkElement.FindName method:
var control = tab.FindName("extra");
if(control is TextBlock){
// your logic here
}
You don't need Application.Current.Resource dictionary here because it's different collection. If you want to use it then you should put user controls within Resource dictionary beforehand.
Because you are trying to find resource with the key "extra". It's wrong.
Try this:
TabItem tab = new TabItem();
var stack = new StackPanel() { Orientation = Orientation.Horizontal };
var textBlock = new TextBlock() { Name = "extra" }
stack.Children.Add(new TextBlock() { Text = header });
stack.Children.Add(textBlock);
tab.Header = stack;
tabControl.Items.Add(tab);
Now you can reach it with textBlock instance.
Here's some VB WPF code for what you need
Dim test As TextBlock
test = DirectCast(FindName("extra"), TextBlock)
I have no idea if it will work like that in C# WPF although if that doesn't work try looking up CType
I have searched for a solution for a day and a half. Most examples that I get that explain / semi explain the situation relate to a combobox with "static" type items. I have the following structure.
DataModel Item
ObservableCollection CombinedData inside DataFilter class (i.e has other properties including "ParentName used for buttoncontent")
ObservableCollection Filters inside DataFilterGroup class
DataFilterGroup is 1 item in DataViewModel
So window's DataContext is DataViewModel.
I have a DataTemplate in which I would like to show the DataFilterGroup items from code behind
This is where I need help with please!!
so basically: ItemsControl's Itemsource is bound to DataFilterGroup
How do I bind a combobox in the DataFilter so that it's items source point to DataFilter. Therefore the source will change (or the content of the combobox will change with every DataFilterGroup Item).
I apologise if this is a repeat question. What I have up to now is the following (and have tried several ways to bind the combobox but no items appear. Supprisingly enough the Buttoncontent show up. Special combo is control deriverd from Combobox which is a button and combobox.
private static DataTemplate DataFilterTemplate
{
get
{
DataTemplate DFT = new DataTemplate();
DFT.DataType = typeof(DataFilters);
FrameworkElementFactory Stack = new FrameworkElementFactory(typeof(VirtualizingStackPanel));
Stack.SetValue(VirtualizingStackPanel.DataContextProperty, new Binding());
Stack.SetValue(VirtualizingStackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory Item = new FrameworkElementFactory(typeof(SpecialCombo));
//Item.SetValue(SpecialCombo.DataContextProperty, new Binding());
Item.SetValue(SpecialCombo.ButtonContentProperty, new Binding("ParentName"));
Binding b = new Binding("CombinedData");
//b.RelativeSource.AncestorType = typeof(ItemsControl);
Item.SetBinding(SpecialCombo.ItemsSourceProperty, b);
//Item.SetValue(SpecialCombo.ItemsSourceProperty, new Binding("CombinedData"));
Item.SetValue(SpecialCombo.DisplayMemberPathProperty, "FullName");
Item.SetValue(SpecialCombo.SelectedValuePathProperty, "Index");
Item.SetValue(SpecialCombo.SelectedValueProperty, "SelectedItem");
Item.SetValue(SpecialCombo.ToggleVisibleProperty, new Binding("ComboVisibility"));
//Item.SetValue(SpecialCombo.SelectedValueProperty, new Binding("SelectedItem"));
Stack.AppendChild(Item);
DFT.VisualTree = Item;
return DFT;
}
}
Child is ItemsControl
Child.ItemsSource = DVM.Filters.FullCollection;
Child.ItemTemplate = DataFilterTemplate;
Thanks to punker 76 in another post where I restructured the question (here -> Can one bind a combobox Itemssource from a datatemplate of a ItemsControl) slightly the following had to be done.
(1) The Object DataFilters had to change from [creation of Dependency Object]
public class DataFilters : INotifyPropertyChanged
// to
public class DataFilters : DependencyObject
(2) Observalbe collection should also change. so
public ObservableCollection<DataModel> CombinedData;
// should become
public static readonly DependencyProperty CombinedData= DependencyProperty.Register("CombinedData", typeof(ObservableCollection<DataModel>), typeof(DataFilters), new FrameworkPropertyMetadata());
//and
public ObservableCollection<DataModel> CombinedData
{
get { return (ObservableCollection<DataModel>)GetValue(CombinedDataProperty); }
set { SetValue(CombinedDataProperty, value); }
}
(3) The correct Binding in the DataTemplate then becomes
Item.SetBinding(SpecialCombo.ItemsSourceProperty, new Binding("CombinedData") );
These are all the major steps to take to get a combobox type object be databound in a DataTemplate
I have a program in which I have to change text of label (on the click of button) which is a child of a grid
public class XLabel
{
Grid uiGrid = null;
TextBlock textblock = null;
string emptyString = "";
Public void createLabel()
{
uiGrid.Children.Add(textblock);
grid.Children.Add(uiGrid);
}
public void cleartext()
{
textblock.Text = emptyString;
}
}
In other class I have a method to clear text
public void clearText()
{
XLabel obj = new XLabel();
obj.cleartext(indexi);
}
How to select specific label to clear text from specific grid if there are many grids and each having one label .
The Grid object has properties like Name or Tag, that can be used for searching.
If you create grids programmatically, you should create a unique property for each, then in your clearText method you just receive all Grid objects from XLabel object and search for the one with proper name/tag.
To get a list of labels from grid, you could use lambda like that:
List<UIElement> list =
YourGrid.Children.Where(o => o.GetType() == typeof(Label)).ToList();
To extend Olter's answer,
Create your Textblock and Grid like this
Grid uiGrid = new Grid() { Name = "uiGrid"+1 };
TextBlock textblock = new TextBlock() { Name = "textBlock"+1 };
Each time change the number you add to the grid and textblock and somehow plan to keeptrack of that number.
Then when you want to clear the text,
(this.FindName("textBlock"+1) as TextBlock).Text = "";