I have a list of strings that I want to display on a menu. I used a Listbox and it works just that it won't let me highlight or copy/paste.
Here is my XAML
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition Width="500"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="450"/>
<RowDefinition Height="318"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="1" Grid.Column="1" x:Name="uiOCRData" />
</Grid>
Heres what I have in C#
List<string> lines = new List<string>();
uiOCRData.ItemsSource = lines;
Thanks for the help!
You must use a ListBox.ItemTemplate so that you can include a control inside your ListBox.
Since you want to be able to select text etc., the best option is to use a TextBox.
<ListBox Grid.Row="0" Name="uiOCRData">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT
Let's say you want to bind to a list of some class objects instead of a simple list of strings. Say your class looks like this:
public class Data
{
public int Id { get; set; }
public string Name { get; set; }
}
Then you can bind to any one of chosen Properties of the class like this:
<ListBox Grid.Row="0" Name="uiOCRData">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Width="100" Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have created sample app for demonstrating the issue.
Sorry its quite difficult to put all the code here since there are model classes, datamodel, service file which fetches the data from rest api.
So only few files are being included which gives information.
_placeList = await DataModel.PlaceDataSource.GetData(url); this line of statement from PlacePage.xaml.cs file is actually fetching records but doesn't get binded and displayed in listview.
But gridViewPlaces.ItemsSource = await DataModel.PlaceDataSource.GetData(url); works.
You can find the source code here. Project Download Link
MainPage.xaml
<SplitView x:Name="splitView" IsPaneOpen="True" OpenPaneLength="250" Grid.Row="1" DisplayMode="Inline">
<SplitView.Pane>
...
</SplitView.Pane>
<SplitView.Content>
<Grid>
<Frame x:Name="rootFrame" />
</Grid>
</SplitView.Content>
</SplitView>
PlacePage.xaml
<GridView Name="gridViewPlaces" ItemsSource="{x:Bind PlaceList}" SelectionMode="Single">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Width="200" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Key" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Value" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Value}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
PagePage.xaml.cs file
private IEnumerable<Place> _placeList;
public IEnumerable<Place> PlaceList
{
get { return _placeList; }
}
public event EventHandler GroupsLoaded;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
url = e.Parameter.ToString();
LoadPlaces();
}
async private void LoadPlaces()
{
_placeList = await DataModel.PlaceDataSource.GetData(url);
//gridViewPlaces.ItemsSource = await DataModel.PlaceDataSource.GetData(url); // This works
gridViewPlaces.UpdateLayout();
if (GroupsLoaded != null)
GroupsLoaded(this, new EventArgs());
}
Your PlaceList property needs to fire notifications to let the binding know there’s a change. As is, when you replace _placeList you don't notify anybody that PlaceList changed and so nothing updates. The typical pattern here is to initialize the PlaceList property read only and then add things to that existing collection rather than swapping out the collection, though if you notify that you've swapped the collection that should work too.
Additionally, the IEnumerable inside PlaceList needs to provide notifications when its contents change. The standard way to do this is to make it an ObservableCollection since OC implements INotifyPropertyChanged and INotifyCollectionChanged for you. See theBinding to collections Quickstart
I am creating a little application that has one feature to show list of people in popup when user click on button. That list, that is in popup, can be pretty big, it can have as many as 3k entries. I am using virtualization, and I don't have problem with performance when list is drawn. But, when user open application for the first time and click on button for popup with list of people, it can take 2-5 seconds before popup is shown. After, that if user try to open popup again popup will be open without delay.
So my question is could I say to ListView to prepare items while it is not shown. Because there is very big chance that user will use application for quiet some time before it will need this popup.
Can I optimize this in some other way? Is there some collection that is better for this purpose in WPF?
Also people collection will be populated when page is loaded, and popup is at beginning closed.
Here it is code that I have:
public class AddressBookViewModel : BaseViewModel
{
...
private ObservableCollection<PeopleModel> people;
...
public ObservableCollection<PeopleModel> People
{
get { return people; }
}
}
public class PeopleModel
{
public PeopleModel(string address, string name)
{
Address = address;
Name = name;
}
public string Name{ get; set; }
public string Address { get; set; }
}
<Button x:Name="btnChoosePerson"
Command="{Binding TogglePeopleAddressPopupCommand}"
Content="..." />
<Popup MaxHeight="520"
IsOpen="{Binding ShowPeopleAddressPopup}"
Placement="Bottom"
PlacementTarget="{Binding ElementName=btnChoosePerson}"
StaysOpen="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="5" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView
Grid.Row="2"
Width="500"
HorizontalAlignment="Left"
ItemsSource="{Binding People}"
SelectionMode="Single"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.CacheLength="10"
VirtualizingPanel.CacheLengthUnit="Item">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Left"
Text="{Binding Address}" />
<TextBlock Grid.Column="1"
HorizontalAlignment="Right"
Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Popup>
Hey I have a list box that I set the ItemsSource to be an ObservableCollection of objects from my database, and I need to add a single object at the end of this list. However I keep getting an invalid operation exception. Somehow my listbox is in use (which in my mind is a given as it is displayed and already have items inside.) Here is my code for the list box:
<ListBox x:Name="CarList" SelectionChanged="ItemSelected" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel FlowDirection="LeftToRight" ItemHeight="300" ItemWidth="300"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="10,10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding image_path}" VerticalAlignment="Stretch"/>
<Grid Grid.Row="1" Background="SteelBlue">
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3" Text="{Binding model}"/>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Margin="3" Text="{Binding price}"/>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And I first set the ItemsSource like so:
CarList.ItemsSource = CarController.GetAllCars();
And then want to add my custom object like this:
ListBoxItem carAdd = new ListBoxItem();
carAdd.Content = new CarModel{ image_path = "/../Assets/add-512.png", id=-1};
CarList.Items.Add(carAdd);
But alas the last operation fails with this message:
Operation is not valid while ItemsSource is in use. Access and modify
elements with ItemsControl.ItemsSource instead.
I have looked for a few other suggestions but all use strings and single bindings in their examples and thus I haven't been able to figure out what exactly to do. If anyone got a suggestion it would be much appreciated.
-
Thanks.
You need to add the item to the items source, and the source should be observable so that the ListBox takes the new item into account:
var cars = new ObservableCollection<CarModel>(CarController.GetAllCars());
CarList.ItemsSource = cars;
...
var car = new CarModel{ image_path = "/../Assets/add-512.png", id=-1};
cars.Add(car);
I am currently building a Windows Phone Applicaation, based off of the HubAppTemplate.
The template comes with a sample .JSON data source that it uses to populate the data of each HubSection. However, I want to use a non JSON type of data as the basis of my code. Inside my C# code, I need to make a function call to my backend to get the type of data I want out of it.
I can put this data inside of my own custom list (on the C# side), but how can I make that list act as the data source for my HubSection? Any old listview/list box works perfectly. Basically, I need help wiring the C# to the XAML -- the main issue is that I cannot access my listView inside of the datatemplate by name.
Can anyone give me some pointers to get going in the right direction?
Here is some reference code to show you what I am talking about:
<HubSection x:Uid="Clubs" Header="Clubs" DataContext="{Binding Groups}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView Name="ClubsList"
IsItemClickEnabled="True"
ItemsSource="{Binding}"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,27.5">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>
The above XAML is basically pulled straight from the hubapp template. I want to be able to use my own itemssource inside of that ListView that is generated from my C# code -- however, I cannot figure out how this ItemsSource works. I also cannot access my listview by name (ClubsList).
Here is the initialization code going on up top (wasn't sure if it was important to post this or not):
<Page
x:Class="HubAppTemplate.HubPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HubAppTemplate"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:HubAppTemplate.Data"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
d:DataContext="{Binding Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="HubSectionHeaderTemplate">
<TextBlock Margin="0,0,0,-9.5" Text="{Binding}"/>
</DataTemplate>
<!-- Grid-appropriate item template as seen in section 2 -->
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<Grid Margin="0,0,9.5,9.5" Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="138.5" Width="138.5"/>
<TextBlock Text="{Binding Title}" VerticalAlignment="Bottom" Margin="9.5,0,0,6.5" Style="{ThemeResource BaseTextBlockStyle}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardTripleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Description}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" Foreground="{ThemeResource PhoneMidBrush}" />
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardDoubleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid x:Name="LayoutRoot">
<Hub x:Name="Hub" x:Uid="Hub" Header="Club Alert" Background="{ThemeResource HubBackgroundImageBrush}">
It is pulling from the JSON backend, but I want to just use my own custom listview for each section. Deleting the DataSource and data template headers gives me errors, however.
Thank you so much for your help in advance!
--A total newbie
HubSection elements require their contents to be populated via a template, so you can't just remove the <DataTemplate> tags, unfortunately. However, there is a simple way to accomplish what you are trying to do, if I understand you correctly.
If you're starting with the default Hub template, you should have this function in your HubPage.xaml.cs file
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
MainViewModel viewModel = DataContext as MainViewModel;
if (!viewModel.IsDataLoaded)
{
viewModel.Load();
}
}
this.DefaultViewModel is just a Dictionary, and they have loaded the sample JSON into a variable and stored this in the ["Groups"] key of the dictionary. Since the Page's DataContext is being bound to {Binding DefaultViewModel, RelativeSource={RelativeSource Self}}, the HubSection's DataContext is being bound to {Binding Groups}, and the ItemsSource of the ListView in each DataTemplate is being bound to {Binding}, each element of the loaded JSON is being used to fill the items of the ListView.
A simple solution would be to assign this.DefaultViewModel["Groups"] to the C# List you are creating from the data you load from your back end.
Something like this:
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var myData = await GetListOfThingsFromBackend();
this.DefaultViewModel["Groups"] = myData;
MainViewModel viewModel = DataContext as MainViewModel;
if (!viewModel.IsDataLoaded)
{
viewModel.Load();
}
}
A better approach would probably be to separate out all ViewModel functionality to it's own class that is better suited to your needs, and then adjust the various DataContext properties throughout the XAML, but that would likely take more time. I can elaborate if needed, but the simple solution is probably enough for now.