I have a Pivotelement, so a Windows Phone Site with multiple sites and on every site should stand 5 menus. I have a List in which there every 5 menus and each menu of each site must be in a textbox:
//Site 1
textbox1_site1.text = list[0].menu1;
textbox2_site1.text = list[0].menu2;
...
//Site 2
textbox1_site2.text = list[1].menu1;
textbox2_site2.text = list[1].menu2;
...
//Site 3
textbox1_site3.text = list[2].menu1;
...
Here is where my list comes from.
public class Menus
{
public date Datum { get; set; }
public string menu1 { get; set; }
public string menu2 { get; set; }
...
}
public class list
{
public List<Menus> list { get; set; }
}
And I don't get it. how can I shorten this up in a loop like:
for(int i=0; i<5; i++)
{
textbox[i+1]_site[i+1] = list[i].menu1;
...
}
I know it's a beginner question and it's a little bit dumb but I don't get it.
from what i understand you have a list of values and want to display each value in a textbox. you can do this with a little data binding. You need to set the ItemsSource property of an ItemsControl to your data source (the list of values). After this all you have to do is modify the ItemsControl.ItemTemplate property. See if the code below helps. As you can see, everything is in the code-behind to keep it short.
//in ctor, after InitializeComponents()
DataContext = this;
//create the source of data (values to display in text boxes)
public List<string> Data{
get{
return new List<string>(){"item1", "item2"};
}
}
the rest is in the xaml file. Create an ItemsControl the gets the data from the Data property and displays it.
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this should solve your problem
Related
I am having issues using ListView Grouping to get the content of my lists to display in labels. The ListView's ItemsSource and GroupDisplayBinding are setting properly, but the label inside the ListView will not display anything (I even tried setting it to a literal string). All of my lists, as well as my "list of lists" are populating correctly. I have poured over the documentation, website articles, and instructional videos but I am still stuck. Can someone point me in the right direction? I am still having a tough time with databinding and MVVM in general. Thanks.
View:
<ListView ItemsSource="{Binding OAllLists}"
GroupDisplayBinding="{Binding Type}"
IsGroupingEnabled="true"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Items}">
</Label>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel:
class OrganizedViewModel
{
public ObservableCollection<Categories> OAllLists { get; set; }
public OrganizedViewModel()
{
OAllLists = new ObservableCollection<Categories>();
foreach(Categories value in Categories.AllLists)
{
OAllLists.Add(value);
}
}
}
Model:
public class Categories
{
public static List<Categories> AllLists { get; set; } = new List<Categories>();
public static Categories FruitList { get; set; } = new Categories("Fruit");
public static Categories VegetableList { get; set; } = new Categories("Vegetables");
///Each type of item has its own static Categories object
public string Type { get; set; }
public List<string> Items { get; set; } = new List<string>();
}
Method for organizing items:
class OrganizeIt
{
public void OrganizeItems(List<string> groceries)
{
foreach (string value in groceries) ///Checks each value for keywords
{
if (Keywords.fruitKeys.Any(value.Contains))
{
Categories.FruitList.Items.Add(value);
}
else if (Keywords.vegetableKeys.Any(value.Contains))
{
Categories.VegetableList.Items.Add(value);
}
}
///Adds each type of list to "list of lists" if it contains values
if (Categories.FruitList.Items.Any())
{
Categories.AllLists.Add(FruitItem);
}
if (Categories.VegetableList.Items.Any())
{
Categories.AllLists.Add(Categories.VegetableList);
}
Edit
New class per comment's recommendation. I also created another Observable Collection in the ViewModel populated by GroupedList (both are working correctly). Name changes are for clarity.
public class Groceries : List<string>
{
public string Category { get; set; }
public static List<Groceries> GroupedList { get; set; } = new List<Groceries>();
public static Groceries Fruits { get; set; } = new Groceries("Fruit");
public static Groceries Vegetables { get; set; } = new Groceries("Vegetables");
public Groceries(string s)
{
Category = s;
}
}
New View:
<ListView ItemsSource="{Binding OGroupedList}"
GroupDisplayBinding="{Binding Category}"
IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding .}"
VerticalOptions="FillAndExpand"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Edit 2
This is how I'm populating the ObservableCollection in my ViewModel now:
class OrganizedViewModel
{
public ObservableCollection<Groceries> OGroupedList { get; set; }
public string Category { get; set; }
public OrganizedViewModel()
{
OGroupedList = new ObservableCollection<Groceries>();
foreach (Groceries value in Groceries.GroupedList)
{
OGroupedList.Add(value);
}
}
Edit 3
This is the method for organizing items. It takes in a string list and checks each list item to see if it contains any of the keywords associated with a certain category (ex. "apple" is contained in "2 bags of apples"). If so, the list item is added to the corresponding Groceries object.
class OrganizeIt
{
public void OrganizeItems(List<string> groceries)
{
foreach (string value in groceries)
{
if (Keywords.fruitKeys.Any(value.Contains))
{
Groceries.Fruits.Add(value);
}
else if (Keywords.vegetableKeys.Any(value.Contains))
{
Groceries.Vegetables.Add(value);
}
}
if (Groceries.Fruits.Any())
{
Groceries.GroupedList.Add(Groceries.Fruits);
}
if (Groceries.Vegetables.Any())
{
Groceries.GroupedList.Add(Groceries.Vegetables);
}
}
Here is where the method is called on the MainPage. UnorganizedList is populated from user input.
private void SortItems()
{
OrganizeIt o = new OrganizeIt();
o.OrganizeItems(UnorganizedList);
}
Solution
All that was needed was to change the Label's binding to just {Binding} (with no period), as well as remove the "x:DataType" line. Below in the revised View in case this helps anybody:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:GroceryListMobile.ViewModels"
xmlns:mvvm="clr-namespace:MvvmHelpers;assembly=MvvmHelpers"
xmlns:model="clr-namespace:GroceryListMobile.Models"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="GroceryListMobile.Views.OrganizedView"
x:Name="Organized">
<ContentPage.BindingContext>
<viewmodels:OrganizedViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<ListView ItemsSource="{Binding OGroupedList}"
GroupDisplayBinding="{Binding Category}"
IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding}"
VerticalOptions="FillAndExpand"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
You need a <ViewCell> as the child node of your <DataTemplate>:
<ListView ItemsSource="{Binding OAllLists}"
GroupDisplayBinding="{Binding Type}"
IsGroupingEnabled="true"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding .}">
</Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ListView.ItemTemplate always requires a DataTemplate which is a Cell.
EDIT
The next thing to address is the collection used as the ItemSource. When using grouping, that needs to be a collection of collections. The thing I would try is to change your model class:
public class Categories : List<string>
{
public static List<Categories> AllLists { get; set; } = new List<Categories>();
public static Categories FruitList { get; set; } = new Categories("Fruit");
public static Categories VegetableList { get; set; } = new Categories("Vegetables");
///Each type of item has its own static Categories object
public string Type { get; set; }
}
And see the revised Binding in the Xaml above.
A way to think about grouped ListViews is that it is a list of lists. The "outer" list elements have a member bound to GroupDisplayBinding, and the "inner" list elements have members bound to the elements in the DataTemplate.
In your case, the "outer" collection is the ObservableCollection, so the GroupDisplayBinding will bind to something on Categories. Then each Categories needs to be a collection itself. In the revisions, that is a List, so the DataTemplate is given a string as the BindingContext. So the Label just needs to bind to the string (hence the Binding ..
The rest of the code would have to adjust a bit as the Items collection that used to be a member of Categories is now the Categories object itself (through inheritance).
EDIT 2
I created a new app with a page containing the ListView exactly as you have it (New View under your first Edit), and the Groceries class exactly as it is under the first Edit.
I revised OrganizedViewModel a little. It doesn't seem to use Category, and I wanted to make sure OGroupedList was being populated with Categories:
class OrganizedViewModel
{
public ObservableCollection<Groceries> OGroupedList { get; set; }
public OrganizedViewModel()
{
OGroupedList = new ObservableCollection<Groceries>();
OGroupedList.Add(Groceries.Fruits);
OGroupedList.Add(Groceries.Vegetables);
}
}
Finally, I added some items to each category in the page's constructor when creating the page:
public Page1()
{
InitializeComponent();
var bc = new OrganizedViewModel();
var index = 1;
foreach (var g in bc.OGroupedList)
{
g.Add(g.Category + $" {index++}");
g.Add(g.Category + $" {index++}");
g.Add(g.Category + $" {index++}");
g.Add(g.Category + $" {index++}");
}
BindingContext = bc;
}
And for me this is showing the lists with the item names and the group headers correctly. So whatever the problem is you're still seeing is somewhere else. The basic class structure and Xaml definition for the grouped ListView is now correct.
My two guesses:
Make sure the collections are being populated correctly.
Try changing to public class Groceries : ObservableCollection<string>. This is important if the Groceries list can change after the page is initially rendered.
EDIT 3
Got to the bottom of it. Here are comments that should get you going:
There are subtle differences between {Binding .} and {Binding}. In your case, {Binding} works but {Binding .} does not.
A more common case is where the elements are objects, not strings, so something like this also solves it:
public class GroceryItem
{
public string Name { get; set; }
}
...
public class Groceries: ObservableCollection<GroceryItem>
{
...
This will, of course, require changes in adding to the Groceries collection and the Label needs to be Text="{Binding Name}".
When I tried #2, Visual Studio was causing problems with x:DataType after making that change. Its detection of binding contexts in the IDE is not the best, so I had to delete that line.
Be careful when using static collections here. If you add items, organize, go back, and organize again, the app will crash because it tries to re-add the fruits and vegetables collections.
According to your code, for the OAllLists to be displayed, you can create OAllLists like this:
OAllLists = new ObservableCollection<Categories>
{
new Categories("FruitList"){"aaa","bbb","ccc},
new Categories("VegetableList"){"ddd","eee"}
};
Among them, "FruitList" is the type in the Categories class, and "aaa" is the string array you added in it. You can check this link for details (https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#grouping)
I've recently started learning C# and I've encountered a problem. I display a set of keywords in a generated checkbox in my WPF and I want to check the element (IsChecked) based on an input check from a TXT file.
If the currently selected element from a different listbox matches the read modelclass(from a txt file) then set the checked key true.
I'm generating a Checkbox in my WPF to list a set of keywords that my app reads from a txt file.
The txt file contains the following items per line:
-id
-key
-pair
-description
WPF code:
<ListView ItemsSource="{Binding XAMLModelKeywords}" SelectedItem="{Binding XAMLModelKeyword}" Margin="5" x:Name="listofallkeys" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" >
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding XAMLAssignedKeys}" Content="{Binding Key}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#:
public ModelTemplates XAMLModelTemplate { get; set; }
public ModelKeywords XAMLModelKeyword { get; set; }
public List<bool> XAMLAssignedKeys { get; set; }
public string XAMLKeyword { get; set; }
public ViewModelMain()
{
//This creates a new instance of ObservableCollection above
XAMLModelTemplates = new ObservableCollection<ModelTemplates>();
XAMLModelKeywords = new ObservableCollection<ModelKeywords>();
XAMLAssignedKeys = new List<bool>();
Refresh();
}
public void Refresh()
{
XAMLModelTemplates.Clear();
foreach (ModelTemplates tpl in ReadInput.ReadTemplateDirectory(Path))
{
XAMLModelTemplates.Add(tpl);
}
//Selecting the first item from the returned list
XAMLModelTemplate = XAMLModelTemplates.FirstOrDefault();
XAMLModelKeywords.Clear();
foreach (ModelKeywords tpl in ReadInput.ReadKeywordsFile(KeyWordsPath))
{
XAMLModelKeywords.Add(tpl);
}
XAMLModelKeyword = XAMLModelKeywords.FirstOrDefault();
XAMLAssignedKeys.Clear();
foreach (ModelKeywords tpl in XAMLModelKeywords)
{
XAMLAssignedKeys.Add(ReadInput.CheckPairedtemplates(tpl, XAMLModelTemplate));
}
ModelKeyword:
public class ModelKeywords
{
public int Id { get; set; }
public string Key { get; set; }
public List<string> PairedTemplates { get; set; }
public string Description { get; set; }
}
ModelTemplate:
public class ModelTemplates
{
//path to a template
public int Id { get; set; }
public string TemplatePath { get; set; }
public string TemplateName { get; set; }
public ExcelRange TemplateRange { get; set; }
}
ReadKeywordsFile:
Returns a list of Template Models (name of template file, path) and the displays it in a listbox.
ReadKeywordsFile:
Returns a list of Keywords Model (id, key, pair, desc) and then displays it in a generated listbox.
CheckPairedtemplates:
Returns a list of booleans based on the currently selected Template Model matches the Keywords Model pair (list of string).
TLDR:
I have a list of booleans ( XAMLAssignedKeys) and I want to match it to my generated checkbox in WPF, however the generation happens based on an item template and I'm not sure how to link one element from my list of booleans to the checkbox "IsChecked" property.
ScreenshotofApp
Thank you much in advance for the advices.
Since you set ObservableCollection<ModelKeywords> (XAMLModelKeywords) to ItemsSource property of ListView (listofallkeys), each item of the ListView will be bound to a member of the ObservableCollection. In this case, the CheckBox inside DataTemplate will be bound to a ModelKeywords in XAMLModelKeywords. Therefore, you need to bind the properties of CheckBox with the properties of ModelKeywords.
In the Xaml, you mistakenly set XAMLAssignedKeys to IsChecked property of CheckBox. It must be a property of ModelKeywords. The remedy will be to add a bool property to ModelKeywords, somehow copy a value in the XAMLAssignedKeys to the property, and then bind the property with IsChecked properties of CheckBox.
Let's say, if you add IsEnabled bool property to ModelKeywords, the Xaml will be
<CheckBox IsChecked="{Binding IsEnabled}" Content="{Binding Key}"/>
In addition, unless you implement INotifyPropertyChanged interface to ModelKeywords, only initial value of the property will be sent to the CheckBox and the subsequent values will not be notified and reflected to the CheckBox when the property is changed.
I have a repeater view which's item source is an observable collection of view models and which has a data template. Now I want to use the index of the specific item to be used inside the data template. Is there any way to achieve this?
NOTE: The repeater view is part of UXDivers.Artina Library, but it should expose the same API as a listview and thus a solution that would work for a listview, would probably also work for the repeater.
Here is the code I have so far:
Xaml:
<ctlRep:Repeater
ItemsSource="{ Binding ListItems }"
Padding="10, 10"
Spacing="10"
Orientation="Vertical">
<ctlRep:Repeater.ItemTemplate>
<DataTemplate>
<elements:StructuredVideoDescriptionItemTemplate />
</DataTemplate>
</ctlRep:Repeater.ItemTemplate>
</ctlRep:Repeater>
Viewmodel:
public class VideoDescriptionStructureListItem : ObservableObject
{
public string Title { get; set; }
public bool IsNumberic { get; set; }
public ObservableCollection<string> ListItems { get; set; }
}
Inside the data template I would like to have access to the ListItems item (which is a string) and the index of the specific item.
It may not be the solution you are looking for, but I have solved the exact issue lately with the following strategy
Introduce an intermediate object that contains a property for an index
Build an ObservableCollection of those objects instead of string objects and assign the correct indices
Bind to the string and the index instead of the direct object
The class may look like this:
class VideoDescriptionViewModel
{
public string Description { get; private set; }
public int Index { get; private set; }
public VideoDescriptionViewModel(string description, int index)
{
Description = description;
Index = index;
}
}
and you can build the instances like
var videoDescriptionViewModels = videoDescriptions.Select((description, index) => new VideoDescriptionViewModel(description, index));
and bind it from your XAML
<elements:StructuredVideoDescriptionItemTemplate Description="{Binding Description}"
Id="{Binding Id}" />
So I have a WPF app(with MVVM) and in this I have a combobox which binds to a table in my database and displays the values, this works just fine.
However, now I want to make a new combobox and bind it to the same table, but now I only want it to display SOME of the values. Is there a simple way to do this?
The table has has four entries but I only want to show 3 of them in this new combobox.
I know I could just make a new table in the database to bind to, but I might have to use several of these comboboxes(with different values) and I'd rather not go through all that bother if I can avoid it.
XAML:
<ComboBox
Name="cmComp"
MinWidth="150"
Margin="12 0 0 12"
ItemsSource="{Binding SelectedComponentLookup}"
DisplayMemberPath="ComponentChoice"
SelectedValuePath="ComponentChoice"
SelectedItem="{Binding ComponentChosen}">
</ComboBox>
VIEWMODEL:
private IEnumerable<ComponentLookupDto> _selectedComponentLookup;
public IEnumerable<ComponentLookupDto> SelectedComponentLookup
{
get { return _selectedComponentLookup; }
set
{
_selectedComponentLookup = value;
}
}
DTO:
public class ComponentLookupDto
{
public int ComponentLookupId { get; set; }
public string ComponentChoice { get; set; }
}
The way I achieve this is that I filter out the items I don't want to display in the getter for the property to which I bind my ItemsSource. :
XAML:
<ComboBox ItemsSource={Binding SelectedComponentLookupOther} ... />
And in your ViewModel:
public IEnumerable<ComponentLookupDto> SelectedComponentLookupOther
{
get { return _selectedComponentLookup.Where(c => c.SomeProperty == "however you want to pick it out"); }
}
Ive looked about 50 or more webpages about this control. and it seems to me that it might be useless to me.
It seams that the "GridView" is a "View" of the "ListView" control.
i dont have any issues in using the control and loading data but manipulating it seems to be difficult.
public partial class Designer : UserControl
{
public Designer()
{
InitializeComponent();
List<RandomData> NewItem = new List<RandomData>();
NewItem.Add(new RandomData { Column1 = "Item1", Column2 = "MoreData", Column3 = "MoreData", Column4 = "MoreData" });
ListView_1.ItemsSource = NewItem;
}
}
public class RandomData
{
public String Column1 { get; set; }
public String Column2 { get; set; }
public String Column3 { get; set; }
public String Column4 { get; set; }
}
Simple enough, it loads the data into the columns.
however what it i want to load other stuff in there. like a checkbox or a image file or something.
public Designer()
{
InitializeComponent();
CheckBox AttemptThis = new CheckBox();
AttemptThis.Content = "Testing";
AttemptThis.IsChecked = true;
List<RandomData> NewItem = new List<RandomData>();
NewItem.Add(new RandomData { Column1 = AttemptThis, Column2 = "MoreData", Column3 = "MoreData", Column4 = "MoreData" });
ListView_1.ItemsSource = NewItem;
}
}
public class RandomData
{
public CheckBox Column1 { get; set; }
public String Column2 { get; set; }
public String Column3 { get; set; }
public String Column4 { get; set; }
}
And i get the checkbox.tostring() appear in the column??
is this control going to be able to do this?
Also is there a way to have a checkbox or image appear if the cell is a certian value ?
If you're working with WPF, you really need to forget any and all notions and approaches you might be used to from archaic technologies such as winforms and understand and embrace The WPF Mentality.
manipulating it seems to be difficult
YES. The WPF Visual Tree is a really complex structure with all sorts of arcane behavior that you really do not want to get into. That's why you must Learn MVVM before you ever write a single line of code in WPF
I want to load other stuff in there. like a checkbox or a image file
or something.
You do not "load" stuff into the UI. You define the UI in XAML and use DataBinding to bind the UI to relevant data, defined in either a Data Model or a ViewModel:
<Window x:Class="WpfApplication7.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<ListView ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"
Content="{Binding DisplayName}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Window>
Code Behind:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = Enumerable.Range(0,10)
.Select(x => new DataItem()
{
DisplayName = "Item" + x.ToString(),
IsSelected = x % 2 == 0
});
}
}
Data Item:
public class DataItem
{
public bool IsSelected { get; set; }
public string DisplayName { get; set; }
}
Result:
See how there is no need at all to manipulate any UI element in procedural code. You simply define the Data in the form of simple properties and then set the UI's DataContext to that.
Two-Way DataBinding will also save you the need to "read the values" back from the UI after they've been modified. There is no such thing as "read data from the UI" in WPF because DataBinding takes care of that. Simply read the data from your Data Items.
WPF Rocks. Copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.