xmal code:
<ListBox x:Name="listbox2" Margin="0,0" SelectionChanged="listbox2_SelectionChanged" Hold="listbox2_Hold" >
<ListBox.ItemContainerStyle >
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="Gray">
<Grid Width="auto" HorizontalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" FontSize="40" Grid.Column="1" Grid.Row="0" Foreground="White" Text="{Binding NAME}"></TextBlock>
<TextBlock VerticalAlignment="Center" FontSize="25" Grid.Column="1" Grid.Row="1" Foreground="Blue" Text="{Binding PHONE}"></TextBlock>
<Image Name="c1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="100" Stretch="Fill" Margin="0" Source="{Binding IMGS}" Grid.RowSpan="2" Grid.Column="0" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
class list which is bind to list box is
List < contactsclass > contacts = new List < contactsclass >();
PHONE and NAME are getter setter of the contactclass's variables
how can i get this variable's value on hold event of listbox .. i am trying following code
private void listbox2_Hold(object sender, System.Windows.Input.GestureEventArgs e)
{
//contextmenucontact = (contactsclass)(sender as ListBox).DataContext;
contextmenucontact = (contactsclass)listbox2.SelectedItem;
MessageBox.Show(contextmenucontact.name);
}
if is just the selected item is just use the ToString Function, see this:
if (listBox1.SelectedItem != null)
{
string itemText = listBox1.SelectedItem.ToString();
contextmenucontact = new contactsclass();
contextmenucontact.name = itemText;
MessageBox.Show(contextmenucontact.name);
}
Related
I have viewlist in which contain elements. When I click on them, my ItemViewModel handler clicks. Then I can select information about that element. Afthe I must to open new page in which will be all information about it. For example like hypertext in browser.
<Border MaxHeight="300" MaxWidth="1140" CornerRadius="25" Margin="73,269,65,182">
<Grid x:Name="MainGrid" RenderTransformOrigin="0.5,0.5" Margin="0,0,10,-3">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView Width="1031" ItemsSource="{Binding GetTopItem}"
ScrollViewer.VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#DD000000"
x:Name="ListBook">
<ListView.DataContext>
<vm:BookMainVM/>
</ListView.DataContext>
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Left" CornerRadius="25,25,25,25" BorderThickness="1" BorderBrush="#FF474747" Height="288" Margin="0,0,0,0" MouseDown="Border_MouseDown" >
<Grid Grid.Column="0" Height="Auto" Width="216" Margin="-1" IsEnabled="False">
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding DataContext.DelegateCommands,
RelativeSource={RelativeSource AncestorType=ListView}}"
CommandParameter="{Binding}">
</MouseBinding>
</Grid.InputBindings>
<Grid.RowDefinitions>
<RowDefinition Height="49*"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="18*"/>
</Grid.RowDefinitions>
<Border CornerRadius="25,25,0,0" RenderTransformOrigin="0.5,0.5">
<Border.Background>
<ImageBrush ImageSource="{Binding Img_src}" Stretch="Fill" />
</Border.Background>
</Border>
<Label Content="{Binding Rate}" Background="#FFBFBFBF" RenderTransformOrigin="0.5,0.5" Grid.Row="1" FontSize="11" FontFamily="Meiryo UI" HorizontalContentAlignment="Center"/>
<Border CornerRadius="0,0,25,25" Grid.Row="2" Background="#FF8F8F8F">
<TextBlock Margin="6,0,0,0" Text="{Binding Name}" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="2" Height="61" Width="190"/>
</Border>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Border>
class ItemMainVM:ViewModel
{
private LibraryModel libraryModel;
private ItemDb itemsDb;
private const string Dir = "D:\\repos\\Frame\\Frame\\src";
public ItemMainVM()
{
libraryModel = new LibraryModel();
itemsDb = new BooksDb();
DelegateCommands = new DelegateCommand(o => EventHandler(o));
}
public ObservableCollection<BookPreviewInfo> GetTopItem
{
get
{
return libraryModel.GetTopItems();
}
}
public ICommand DelegateCommands { get; }
public void EventHandler(dynamic item)
{
itemDb.SelectItemId(item.Id);
}
}
Code Behind is empty.
When I click an element and executing EventHandler in ItemMainVM. And next must be create new page with new ElementViewModel.
ElementViewModel should to get information about element in ItemMainVM.
I would like to have ListViewItem with data from binding and with 2 events.
My code:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Name="MailListViewItem">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold" Text="{Binding Topic}"/>
<TextBlock Grid.Column="1" Grid.Row="0" FontSize="8" Foreground="Blue" Text="{Binding Time}"/>
<TextBlock Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Foreground="Gray" Text="{Binding Text}"/>
</Grid>
</DataTemplate>
Where should I put MouseDoubleClick="Mail_DoubleClick" MouseLeftButtonUp="Mail_MouseLeftButtonUp"?
You could define an ItemContainerStyle with EventSetters:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseLeftButtonUp" Handler="Mail_DoubleClick" />
<EventSetter Event="MouseDoubleClick" Handler="Mail_MouseLeftButtonUp" />
</Style>
</ListView.ItemContainerStyle>
...
Or you could handle the events of the Grid in the DataTemplate provided that you set its Background property to some brush:
<Grid Name="MailListViewItem" Background="Transparent" MouseLeftButtonDown="...">
If you handle MouseLeftButtonDown, there is a ClickCount property of the MouseButtonEventArgs that you can check to determine whether the element as double clicked:
private void MailListViewItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
//doule click
}
else
{
//click...
}
}
In my WPF Application I have a two textblocks which get filled from the code behind with a run. Every second line should have a different background color so it gets easier to read. Unfortunately, the lines are only dyed as far as they are written. But I want the background color to go over the entire line and not just the written area.
Code:
for (int i = 0; i < GlobalSettings.prefixList.Count; i++)
{
runLeft = new Run(GlobalSettings.prefixList[i].prefix + "\n");
runRight = new Run(GlobalSettings.prefixList[i].amount + "\n");
if (i % 2 == 0)
{
runLeft.Background = Brushes.Gray;
runRight.Background = Brushes.Gray;
}
else
{
runLeft.Background = Brushes.LightGray;
runRight.Background = Brushes.LightGray;
}
tblock_StatisticsLeft.Inlines.Add(runLeft);
tblock_StatisticsRight.Inlines.Add(runRight);
}
Example Picture
The two textblocks are seamlessly together in the middle so it would look like it is a single line in a single textblock. The spaces between the lines are negligible if this makes it easier.
Is there a solution without using a textbox or richtextbox?
EDIT:
XAML Code:
<UserControl x:Class="MuseKeyGenApp.UCStartUp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MuseKeyGenApp"
mc:Ignorable="d"
Background = "#FF0069B4"
d:DesignHeight="500" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="0"/>
<RowDefinition Height="*" MinHeight="80"/>
<RowDefinition Height="*" MinHeight="80"/>
<RowDefinition Height="*" MinHeight="80"/>
<RowDefinition Height="*" MinHeight="80"/>
<RowDefinition Height="*" MinHeight="80"/>
<RowDefinition Height="0"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="150"/>
<ColumnDefinition Width="0.75*" MinWidth="100"/>
<ColumnDefinition Width="0.75*" MinWidth="100"/>
<ColumnDefinition Width="0.75*" MinWidth="100"/>
<ColumnDefinition Width="0.75*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="2" Grid.RowSpan="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,35,12,12" Name="sv_PrefixList">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="tblock_StatisticsLeft" HorizontalAlignment="Stretch" LineHeight="20" TextWrapping="Wrap" Margin="0,0,0,0" Text="TextBlock" VerticalAlignment="Stretch" TextAlignment="Left"/>
<TextBlock Grid.Column="1" x:Name="tblock_StatisticsRight" HorizontalAlignment="Stretch" LineHeight="20" TextWrapping="Wrap" Margin="0,0,10,0" Text="TextBlock" VerticalAlignment="Stretch" TextAlignment="Right"/>
</Grid>
</ScrollViewer>
</Grid>
I left out all the other stuff of the control that is not relevant.
You could (should) use an ItemsControl. Set the ItemsSource property of it to your GlobalSettings.prefixList collection, i.e. you replace your for loop with this:
ic.ItemsSource = GlobalSettings.prefixList;
Make sure that "prefix" and "amount" are public properties (and not fields) of your "prefix" type or whatever you call it:
public string prefix { get; set; }
You then put the Grid with the TextBlocks in the ItemTemplate of the ItemsControl and bind the TextBlocks to the "prefix" and "amount" properties:
<ScrollViewer Grid.Row="2" Grid.RowSpan="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,35,12,12" Name="sv_PrefixList">
<ItemsControl x:Name="ic" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="tblock_StatisticsLeft" HorizontalAlignment="Stretch" LineHeight="20" TextWrapping="Wrap" Margin="0,0,0,0" VerticalAlignment="Stretch" TextAlignment="Left"
Text="{Binding prefix}"/>
<TextBlock Grid.Column="1" x:Name="tblock_StatisticsRight" HorizontalAlignment="Stretch" LineHeight="20" TextWrapping="Wrap" Margin="0,0,10,0" VerticalAlignment="Stretch" TextAlignment="Right"
Text="{Binding amount}"/>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="Gray" TargetName="grid"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightGray" TargetName="grid"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
This should get you coloured lines.
Here is the correct way of doing this:
Xaml:
<Window.Resources>
<local:BackConverter x:Key="BackConverter"/>
</Window.Resources>
<Grid Margin="10">
<ItemsControl Name="ic">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="False">
<DockPanel.Background>
<MultiBinding Converter="{StaticResource BackConverter}">
<Binding />
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}"/>
</MultiBinding>
</DockPanel.Background>
<TextBlock DockPanel.Dock="Left" Text="{Binding Left}">
</TextBlock>
<TextBlock DockPanel.Dock="Right" Text="{Binding Right}">
</TextBlock>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
c# code:
public class obj
{
public string Left { get; set; }
public string Right { get; set; }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
List<obj> objects = new List<obj>();
for (int i = 0; i < 10; i++)
{
var left = "aaaaa";
var right = "bbbbb";
objects.Add(new obj() { Left = left, Right = right });
}
ic.ItemsSource = objects;
}
the converter:
public class BackConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var index = ((ItemsControl)values[1]).Items.IndexOf(values[0]);
if (index % 2 == 0)
return Brushes.Gray;
return Brushes.White;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I am trying to create a flipview on the an ItemDetailpage. I am using the default template provided in visual studio when creating a grid app.
Pages in the App: GroupItemsPage, GroupDetailPage, ItemDetailPage
The problem is, I am getting this error when I click on an item on the GroupItemsPage or GroupDetailPage:
Object reference not set to an instance of an object.
Error details:
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=AppError
StackTrace: at AppError.ItemDetailPage.<navigationHelper_LoadState>d__0.MoveNext()
This is my code:
GroupItemsPage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.GroupedItemsPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<x:String x:Key="ChevronGlyph"></x:String>
<!--
Collection of grouped items displayed by this page, bound to a subset
of the complete item list because items in groups cannot be virtualized
-->
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding Groups, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="0,0,0,2">
<Button Foreground="{ThemeResource ApplicationHeaderForegroundThemeBrush}"
AutomationProperties.Name="Group Title"
Click="Header_Click"
Style="{StaticResource TextBlockButtonStyle}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Margin="0,-11,10,10" Style="{StaticResource SubheaderTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{StaticResource ChevronGlyph}" FontFamily="Segoe UI Symbol" Margin="0,-11,0,10" Style="{StaticResource SubheaderTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
GroupItemsPage.xaml.cs
namespace AppError
{
public sealed partial class GroupedItemsPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public GroupedItemsPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
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;
}
void Header_Click(object sender, RoutedEventArgs e)
{
// Determine what group the Button instance represents
var group = (sender as FrameworkElement).DataContext;
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
this.Frame.Navigate(typeof(GroupDetailPage), ((SampleDataGroup)group).UniqueId);
}
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
}
GroupDetailpage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.GroupDetailPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- Collection of items displayed by this page -->
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"
d:Source="{Binding Groups[0].Items, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding Group}"
d:DataContext="{Binding Groups[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Items In Group"
TabIndex="1"
Grid.RowSpan="2"
Padding="120,126,120,50"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Width="480" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.Header>
<StackPanel Width="480" Margin="0,4,14,0">
<TextBlock Text="{Binding Subtitle}" Margin="0,0,0,20" Style="{StaticResource SubheaderTextBlockStyle}" MaxHeight="60"/>
<Image Source="{Binding ImagePath}" Height="400" Margin="0,0,0,20" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,0,0" Style="{StaticResource BodyTextBlockStyle}"/>
</StackPanel>
</GridView.Header>
<GridView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="52,0,0,2"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
GroupDetailPage.xaml.cs
namespace AppError
{
public sealed partial class GroupDetailPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public GroupDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
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 group = await SampleDataSource.GetGroupAsync((String)e.NavigationParameter);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Items"] = group.Items;
}
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
}
ItemDetailPage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.ItemDetailPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="itemViewSource"
Source="{Binding Items}"
d:Source="{Binding Groups[0].Items, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding Item}"
d:DataContext="{Binding Groups[0].Items[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--
TODO: Content should be placed within the following grid
to show details for the current item
-->
<FlipView
Grid.Row="1"
x:Name="flipView"
Margin="50,0,0,0"
ItemsSource="{Binding Source={StaticResource itemViewSource}}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<Border>
<Image Source="{Binding ImagePath}"/>
</Border>
<TextBlock Text="{Binding Description}" Padding="0,30,0,0" TextWrapping="Wrap"/>
</StackPanel>
<Grid Grid.Column="1" Margin="30,0,0,0">
<TextBlock Text="{Binding Content}" TextWrapping="Wrap"/>
</Grid>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
ItemDetailPage.xaml.cs
namespace AppError
{
public sealed partial class ItemDetailPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public ItemDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
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 group = await SampleDataSource.GetGroupAsync((String)e.NavigationParameter);
var item = await SampleDataSource.GetItemAsync((String)e.NavigationParameter);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Items"] = group.Items;
this.flipView.SelectedItem = item;
}
}
I have a usercontrol with 2 ListViews in it. One for holding a list of predefined categories and one for the list with all the categories in it.
When i place the ListViews inside a <Grid> than everything works perfect.
The working xaml code (with Grid):
<Grid Style="{StaticResource ResourceKey=ContentStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView x:Name="lstPredefinedCategories" Grid.Row="0" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
<ListView.Header>
<StackPanel>
<TextBlock Text="Voorgestelde categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20,0">
<TextBlock Text="Alle categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
<TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
<Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
<Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
<Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
</Grid>
</Grid>
</Grid>
The only problem with this is that I dont get the UI behaviour that I want. If I use a grid then only the red border is scrollable (because of the ListView). But what I need is that the entire green border is scrollable.
So I want to put everything in a <ScrollViewer><StackPanel></StackPanel></ScrollViewer>.
But when I do so, I occasionally get an out-of-memory exception (sometimes the apps just freezes and close without an exception).
Here is my not working xaml with the <ScrollViewer>:
<ScrollViewer>
<StackPanel>
<ListView x:Name="lstPredefinedCategories" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
<ListView.Header>
<StackPanel>
<TextBlock Text="Voorgestelde categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20,0">
<TextBlock Text="Alle categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
<TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
<Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
<Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
<Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
</Grid>
</Grid>
</StackPanel>
</ScrollViewer>
Any thoughts on why my app is freezing or get an OOM-exception?
Update
It comes because in the 2nd ListView they are too much objects loaded. So I'm gonna try to fix it with ISupportIncrementalLoading.
Or is there an other way?
The solution was to use virtualization (ISupportIncrementalLoading) like suggested in the comments.
Here you can find my implementation class of ISupportIncrementalLoading:
public class StringKeyValueIncrementalCollection : ObservableCollection<StringKeyValue>, ISupportIncrementalLoading
{
private List<StringKeyValue> allCategories;
private int lastItem = 1;
public StringKeyValueIncrementalCollection(List<StringKeyValue> categories)
{
this.allCategories = categories;
}
public bool HasMoreItems
{
get
{
if (lastItem == allCategories.Count)
{
return false;
}
else
{
return true;
}
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(async () =>
{
List<StringKeyValue> items = new List<StringKeyValue>();
for (int i = 0; i < count; i++)
{
items.Add(allCategories[i]);
lastItem++;
Debug.WriteLine(lastItem);
if (lastItem == allCategories.Count)
{
break;
}
}
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
foreach (StringKeyValue item in items)
{
this.Add(item);
}
});
return new LoadMoreItemsResult() { Count = count };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
And then my code in the ViewModel. As you can see, I use the StringKeyValueIncrementalCollection instead of a regular List<object>:
private StringKeyValueIncrementalCollection categories;
private StringKeyValueIncrementalCollection allCategories;
public StringKeyValueIncrementalCollection Categories
{
get { return categories; }
set
{
filteredCategories = value;
RaisePropertyChanged("Categories");
}
}
public async void LoadCategories()
{
List<StringKeyValue> temp = await this.openVlaanderenService.GetCategoriesData();
allCategories = new StringKeyValueIncrementalCollection(temp);
Categories = allCategories;
}
The only problem that you than have is that the ScollViewer will allow it's content to fill as much space as it wants, so the data just will keep loading. To fix that I did what was suggested in ISupportIncrementalLoading inside ScrollViewer not supported?
So I added a SizeChanged="ScrollViewer_SizeChanged" event to my ScrollViewer and in code behind set the size of the ListView based on the viewport size properties of the ScrollViewer:
private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
lstCategories.Height = scrollViewer.ViewportHeight;
}