Array of string not binding to the listview - arrays

I'm trying to bind an array to list view. It's not binding and showing blank. I used Model class to place array.
View Model:
public class RunningLateOptions
{
public string[] runningLateOptions = new[] { "30 Mins", "1 Hour", "1:30 Hour", "2 Hours" };
public string[] runningLateOption
{
get{ return runningLateOptions; }
}
}
XAML code:
<ListView x:Name="RunningLateOptions" ItemsSource="{Binding RunningLateOptions}" ItemSelected="runningLateSelectedItem">
<ListView.ItemTemplate>
<DataTemplate>
<Label x:Name="listItems" Text="{Binding runningLateOption}" HorizontalTextAlignment="Center"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm not able to understand what's wrong with this. Help me.

You need to modify the xaml as follows:-
<ListView x:Name="RunningLateOptions" ItemsSource="{Binding runningLateOption}" ItemSelected="runningLateSelectedItem">
<ListView.ItemTemplate>
<DataTemplate>
<Label x:Name="listItems" Text="{Binding .}" HorizontalTextAlignment="Center"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Let me know if you face more difficulties.

The type ItemsSource of ListView or CollectionView should be a list which implement the Interface IEnumerable .
So we normally set it as an ObservableCollection<T> or List<T> .
In your case , you could set the ItemsSource of the ListView like
public ObservableCollection<string> RunningLateOptions {get; set;}
ObservableCollection has implemented the Interface INotifyPropertyChanged. So you don't need to implement it any more .
For more details about ListView you could refer https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/data-and-databinding
<ListView x:Name="RunningLateOptions" ItemsSource="{Binding RunningLateOptions}" ItemSelected="runningLateSelectedItem">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Label x:Name="listItems" Text="{Binding runningLateOption}" HorizontalTextAlignment="Center"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

I gave the array of strings in the CS file and bind to listview by using itemsource.
I didn't use any View model here.
CS code:
string[] runningLateOptions = { "30 Mins", "1 Hour", "1:30 Hour", "2 Hours" };
RunningLateOptions.ItemsSource = runningLateOptions;
XAML code:
<ListView x:Name="RunningLateOptions" ItemsSource="{Binding Items}" ItemSelected="runningLateSelectedItem">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label BackgroundColor="Transparent" x:Name="listItems" Text="{Binding}" TextColor="Black" HorizontalOptions="Center"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
TextCell not providing the HorizontalTextAlignment attribute. That's why I used view cell for the label.
Thanks for helping me.
Click here to see the output

Related

how to bind a list of tuple in a listbox?

I have a listbox like this:
<ListBox x:Name="list1" ItemsSource="{Binding MyListWithTuples}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding value1}" />
<Label Content="{Binding value2}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my view model I have this collection:
private ObservableCollection<(decimal value1, decimal value2)> _myCollection= new ObservableCollection<(decimal value1, decimal value2)>();
public ObservableCollection<(decimal vaule1, decimal value2)> MyCollection
{
get { return _myCollection; }
set
{
_myCollection= value;
base.RaisePropertyChangedEvent("MyCollection");
}
}
But the data isn't show. However if I convert the tuple to a dictionary, I can bind to Key and Value properties and the data is shown. But I would like to avoid to convert the tuple into a dictionary.
Are there some way to bind the listbox to a list of tuples?
Thanks.
Unlike the Tuple Class, the new C# tuple types only define fields, but no properties. So you can't use them with WPF data binding.
However, with
public ObservableCollection<Tuple<decimal, decimal>> MyCollection { get; }
you could use this XAML:
<ListBox ItemsSource="{Binding MyCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Item1}" />
<Label Content="{Binding Item2}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

ListView items do not display

I have a rather simple ListView with GridView style. Cells are binded to some properties of an ObservableCollection elements. I have no idea why thaey are not displaying. I were thinking about implementing INotifyPropertyChanged in a class of element contained by collection, but once an element is constructed and added to a collection it is never changed so it should be working just fine.
C#:
public ObservableCollection<SceneElement> SceneObjects { get; set; }
...
SceneObjects.Add(new Camera());
SceneObjects.Add(new Cuboid(20, 30, 40));
Camera and Cuboid both inherit from SceneObject (SceneObject <- Figure <- Cuboid and SceneObject <- Camera)
XAML:
<ListView Grid.Column="2" Margin="5"
ItemsSource="{Binding }"
DataContext="{Binding SceneObjects}"
ScrollViewer.CanContentScroll="True">
<ListView.Resources>
<GridView x:Key="StyleOne">
<GridViewColumn Header="Type" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="1" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Position" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="X: " />
<TextBlock Text="{Binding Position.X, RelativeSource={RelativeSource Mode=FindAncestor}}" />
<TextBlock Text=" Y: " />
<TextBlock Text="{Binding Position.Y}" />
<TextBlock Text=" Z: " />
<TextBlock Text="{Binding Position.Z}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.Resources>
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="View" Value="{StaticResource StyleOne}" />
</Style>
</ListView.Style>
</ListView>
Position property is defined inside an abstract SceneElement class.
Position's X, Y and Z are properties as well.
EDIT
This has solved the issue:
private ObservableCollection<SceneElement> sceneObjects;
public ObservableCollection<SceneElement> SceneObjects
{
get
{
return sceneObjects;
}
set
{
sceneObjects = value;
NotifyPropertyChanged("SceneObjects");
}
}
Can anyone explain why this was necessary? I've used ObservableCollection severeal time so far and it always worked without notifying.
Reason why your application wasn't displaying the information about the collection is because the UI wasn't aware of any changes.
Using ObservableCollection doesn't guarantee the results will be displayed if said collection is not initialized before it is used. As it is a plain property on the ViewModel it needs to notify if said object, in this case ObservableCollection has been changed, but not in the sense items inside of the collection.
This is due to the fact of NOT raising the PropertyChanged event for SceneObjects, which is crucial for WPF apps using Bidning.
at the point where the wpf system attempts to create the binding, your observablecollection was null, as xamimax above said.
instantiate your observablecollection in your constructor, and later you can add the items and they will appear.
for example, the following works because i first instantiated the observable collection and then added items 5 seconds later.
public MainWindow()
{
InitializeComponent();
this.SceneObjects = new ObservableCollection<MyClass>();
Task.Delay(5000).ContinueWith((old) =>
{
this.Dispatcher.Invoke(() =>
{
this.sceneObjects.Add(new MyClass("name"));
this.sceneObjects.Add(new MyClass("name"));
});
});
this.DataContext = this;
}
and here is an example that doesnt work, because i instantiate the observable collection 5 secs after the wpf system tried to bind to it:
public MainWindow()
{
InitializeComponent();
Task.Delay(5000).ContinueWith((old) => { this.SceneObjects = new ObservableCollection<MyClass> { new MyClass("name"), new MyClass("name") }; });
this.DataContext = this;
}
and as you already realized, you can throw propchanged to make wpf system display those items, but i do think you should simply instantiate the observablecollection earlier so that you dont need to throw propchanged later.

WPF DataGrid Single Click to Create New Item

I have a DataGrid with DataGridTemplateColumn and ComboBox in it.
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
public ObservableCollection<TestClass> TestItemCollection { get; set; } = new ObservableCollection<TestClass>
{
new TestClass(),
new TestClass(),
new TestClass(),
};
public class TestClass
{
public ObservableCollection<string> TestChildCollection { get; set; } = new ObservableCollection<string>
{
"First test item", "Second test item" , "Third test item" , "Fourth test item"
};
}
When I click on the ComboBox in the blank row it apparently doesn't create a new instance of my collection and only gives a blank list.
I have to doubleclick on empty row space.
And only then I would get data in the ComboBox.
How can I get data in the Combobox with a single click on blank row??
If you need to get data in the ComboBox with a single click on blank row, I suggest you to use Caliburn.Micro to "attach" a command to the DropDownOpened event of your ComboBox.
Here a sample: first of all the XAML
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
Title="MainView" Height="600" Width="600"
Name="_window">
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
cal:Message.Attach="[Event DropDownOpened] = [Action Choose($dataContext)]"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Then the ViewModel:
public class MainViewModel : Caliburn.Micro.PropertyChangedBase
{
public MainViewModel()
{
TestItemCollection = new ObservableCollection<TestClass>
{
new TestClass(),
new TestClass(),
new TestClass(),
};
}
public void Choose(object data)
{
if (!(data is TestClass))
{
TestItemCollection.Add(new TestClass());
}
}
public ObservableCollection<TestClass> TestItemCollection { get; set; }
}
Please consider that in my sample the TestClass code is the same that you wrote. Of course you must configure you application in order to work with Caliburn.Micro (if you do not know how to do it, you can read the documentation).
If you do not want (or maybe you cannot) use Caliburn.Micro, you can obtain the same result by using the System.Windows.Interactivity library (see my edit below).
Try the code: just a click and a new row is automatically created.
I hope it can help you.
EDIT:
alternative solution with System.Windows.Interactivity
If you cannot use Caliburn.Micro, you just need to modify the MainView XAML in this way:
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="MainView" Height="600" Width="600"
Name="_window">
<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DropDownOpened">
<ei:CallMethodAction MethodName="ChooseWithInteraction"
TargetObject="{Binding ElementName=_window, Path=DataContext}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
As you can see, I just added the references to Microsoft.Expression.Interactions and System.Windows.Interactivity libraries. Then I added an EventTrigger and a CallMethodAction to the ComboBox.
Now in the MainViewModel you can replace the Choose method with the ChooseWithInteraction one (of course you can also simply add it to the code):
public void ChooseWithInteraction(object sender, EventArgs args)
{
object data = ((ComboBox)sender).DataContext;
if (!(data is TestClass))
{
TestItemCollection.Add(new TestClass());
}
}
In this way you can obtain the same behaviour of my first solution, but without using Caliburn.
I've come to a solution which you can achieve your desired functionality by following two steps.
Step One: You should specify a CellEditingTemplate for the DataGridTemplateColumn and set IsHitTestVisible to false for the DataGridTemplateColumn.CellTemplate. This will force UI to enter the edit-mode when a DataGridCell (e.g. your ComboBox) is being clicked and as a result a new instance of your collection will be created. To do so, you should first define a property in your TestClass to keep the selected value of each ComboBox:
public class TestClass
{
public ObservableCollection<string> TestChildCollection { get; set; }
// keeps the selected value of each ComboBox
public string SelectedTestItem { get; set; }
public TestClass()
{
TestChildCollection = new ObservableCollection<string> {"First test item", "Second test item" , "Third test item" , "Fourth test item" };
}
}
Then the xaml for your DataGrid should change like this:
<DataGrid Grid.Row="0"
SelectionUnit="Cell"
DataGridCell.Selected="DataGridCell_GotFocus"
GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Test Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="150" IsHitTestVisible="False"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
SelectedItem="{Binding SelectedTestItem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Width="150"
HorizontalAlignment="Left"
ItemsSource="{Binding TestChildCollection}"
SelectedItem="{Binding SelectedTestItem}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Step Two: As you can see in the above xaml, SelectionUnit is set to Cell and also an event handler for DataGridCell.Selected has been defined. The event handler code is as below:
private void DataGridCell_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
DataGrid dataGrid = (DataGrid)sender;
dataGrid.BeginEdit(e);
}
}
This makes sure that every time you click on a DataGridCell it will enter editing-mode. therefore you need one less click each time you try to open the ComboBox inside the newly created DataGridRow.

Two way binding with a DataGrid vs. a ListView

I'm having trouble understanding a discrepancy in the handling of two-way binding between a DataGrid vs. a ListView. To illustrate, I have a class DataItem with a few properties, and a List of DataItems for binding to the DataGrid/ListView:
public class DataItem
{
public bool Flag { get; set; }
public int IntValue { get; set; }
public string StringValue { get; set; }
public List<DataItem> SubList { get; set; }
public DataItem()[...]
}
I create a main DataItem object with a number of additional DataItem objects adde into the SubList. The main DataItem is set to the DataContext of a containing Grid, and the SubList is bound to both a ListView:
<ListView ItemsSource="{Binding Path=SubList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Flag" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="FlagCheckBox" IsChecked="{Binding Path=Flag}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="String Value" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="StringTextBox" Text="{Binding Path=StringValue}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
and to a DataGrid:
<DataGrid ItemsSource="{Binding Path=SubList}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Flag" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="FlagCheckBox" IsChecked="{Binding Path=Flag}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="String Value" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="StringTextBox" Text="{Binding Path=StringValue}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Both the ListView and the DataGrid properly display the SubList items. However, when I modify the data in the UI, and examine the source DataItem.SubList, the ListView works and the DataGrid doesn't: I can see the changes when they are made in the ListView, but when the changes are made in the DataGrid, there are no changes.
The bindings must be correct, otherwise I wouldn't see the values displayed properly. But for some reason, two-way binding works in the ListView to move changes made in the UI back to the source object, but not in the DataGrid.
You need to set UpdateSourceTrigger to PropertyChanged to propagate changes back to your DataObject class in case of DataGrid.
<CheckBox Name="FlagCheckBox" IsChecked="{Binding Path=Flag,
UpdateSourceTrigger=PropertyChanged}"/>
And on TextBox too -
<TextBox Name="StringTextBox" Text="{Binding Path=StringValue,
UpdateSourceTrigger=PropertyChanged}"/>

In WPF how to bind a collection within the parent control's itemssource

I'm new to WPF so this may be easier than it seems. I have a DataTable object that I set as the itemssource of a combobox. For each row in the DataTable I want a ComboBoxItem. For each ComboBoxItem I want to create a label for every column name and a text box for the corresponding value, in the current row, of that column. Nothing I try seems to work but heres my shot at the datatemplate in XAML.
<Grid x:Name="LayoutRoot" Background="White" Height="107" Width="358">
<ComboBox Name="pCombo" ItemsSource="myTable">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel DataContext="{Binding pCombo.ItemsSource.Columns}">
<TextBlock Text="{Binding ColumnName}"></TextBlock>
</StackPanel>
<StackPanel DataContext="{Binding pCombo.ItemsSource.Rows}">
<TextBox Text="{Binding RowValue}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
I know all my Bindings are wrong I just can't figure out what should be there instead. Thanks for anyone that helps me out.
XAML:
<ListView ItemsSource="{Binding Path=Tbl}">
<ListView.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Key}"></Label>
<Label Content="{Binding Path=Value}"></Label>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code behind:
private object tbl = new[]
{
new[] {
new KeyValuePair<string, string>("col1", "val1"), new KeyValuePair<string, string>("col2", "val1")
},
new[] {
new KeyValuePair<string, string>("col1", "val2"), new KeyValuePair<string, string>("col2", "val2")
},
new[] {
new KeyValuePair<string, string>("col1", "val3"), new KeyValuePair<string, string>("col2", "val3")
}
};
public object Tbl { get { return tbl; } set { tbl = value; } }
Don't forget to set the DataContext (i.e. in the .ctor of the window) like this:
DataContext = this;
I just hope you get the idea behind this!

Resources