Binding ObservableCollection of List - c#

In my Windows Phone application I have a listBox with ContentItems binding :
private ObservableCollection<ContentItemViewModel> _contentItems;
public ObservableCollection<ContentItemViewModel> ContentItems
{
get { return _contentItems; }
}
<ListBox x:Name="ContentListBox" Margin="0,117,12,0" VirtualizingStackPanel.VirtualizationMode="Standard" Logic1:TiltEffect.IsTiltEnabled="True" ItemsSource="{Binding ContentItems}" Tap="ContentListBox_Tap" MinHeight="656" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="vertical" >
<Grid Height="{Binding ItemHeight}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" x:Name="itemIco1" Width="Auto" Height="Auto" HorizontalAlignment="Left" Source="{Binding IconURL}" Stretch="Fill" CacheMode="BitmapCache" VerticalAlignment="Top" Margin="5,5,5,5" Visibility="Visible"/>
<ListBox IsHitTestVisible="False" Grid.Column="1" VerticalAlignment="Center" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<TextBlock x:Name="ContentCategoryTitle" Height="70" Text="{Binding ContentTitle}" Width="460" Margin="5,34,0,0" TextTrimming="WordEllipsis" TextWrapping="NoWrap" FontSize="28" FontFamily="Segoe WP Semibold" Foreground="#FFF7F7F7" VerticalAlignment="Bottom" />
</ListBox>
</Grid>
<Rectangle Fill="#FF585858" Height="1" Margin="0,0,0,0" Width="460" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Is it possible to binding not an ObservableCollection<ContentItemViewModel>, but - ObservableCollection<List<ContentItemViewModel>> ?

Yes that is possible, if you have a collection of collections. Or why not this?
ObservableCollection<ObservableCollection<ContentItemViewModel>>
If you want your UI to be notified of collection changes to your sub collections.
Update
For example:
View Model
public ObservableCollection<ObservableCollection<ContentItemViewModel>> ContentItems
{
get { return _contentItems; }
set { _contentItems = value; // Notify of property change here, this allows you to change the ContentItems reference after view model construction }
}
public MyViewModel()
{
// Populate content items
this.ContentItems = new ObservableCollection
{
new ObservableCollection { new ContentItemViewModel() },
new ObservableCollection { new ContentItemViewModel(), new ContentItemViewModel() }
};
}
View
<ListBox ItemsSource="{Binding ContentItems}" ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding MyContentItemViewModelProperty}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>

Related

WPF ListView - detect ListVewItem when selected item is not clicked

I'm trying to get an item in ListView in WPF when user doesn't select an item by clicking on it but the user taps on it.
Is there a way to achieve this in WPF ListView.
ListView dataSource is filled by a dynamic collection in code-behind.
I'm getting a null object when user taps on it.
Item selectedItem = (Item)lv_CartItems.SelectedItem;
this is how my data-template looks like for listview.
<ListView.ItemTemplate>
<DataTemplate>
<Viewbox>
<Grid Width="230" Height="110" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width=".1*" />
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1"
Grid.Row="0" Grid.Column="0"
Grid.ColumnSpan="6" Grid.RowSpan="3" >
</Border>
<Viewbox Grid.Row="0" >
<Image Name="img_ItemImage"
Source="{Binding Image, Mode=TwoWay }"
Width="20" Height=" 25" />
</Viewbox>
<Viewbox Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Top" >
<TextBlock Name="lbl_ItemName" TextWrapping="Wrap" Width="180" Foreground="Gray"
Text="{Binding Name , Mode=TwoWay }" Tag="{Binding SKU_No,Mode=TwoWay}" >
</TextBlock>
</Viewbox>
<Viewbox Grid.Row="1" Margin="10,0" VerticalAlignment="Top" >
<TextBlock Foreground="Gray" >Qty:</TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Margin="0,0" VerticalAlignment="Top" >
<StackPanel Orientation="Horizontal" >
<Button Name="btn_Minus" FontWeight="ExtraBold" Padding="0" Width="12"
Resources="{StaticResource cartitembutton}" Click="btn_Minus_Click" >
<Image Source="/Resources\Icons\minus.png" ></Image>
</Button>
<Border BorderThickness="1" Margin="2,0" Width="13" CornerRadius="2" BorderBrush="LightGray" >
<TextBlock Name="lbl_Quantity" FontWeight="Bold" Foreground="Gray"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Quantity , Mode=TwoWay }">
</TextBlock>
</Border>
<Button Name="btn_Increment" FontWeight="ExtraBold" Width="12"
Resources="{StaticResource cartitembutton}"
Padding="0"
Click="btn_Increment_Click">
<Image Source="/Resources\Icons\union_10.png" ></Image>
</Button>
</StackPanel>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="2" Margin="5,0"
HorizontalAlignment="Left" Grid.ColumnSpan="3" >
<TextBlock Name="lbl_Price" FontWeight="DemiBold"
Text="{Binding Price , Mode=TwoWay}" ></TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3"
VerticalAlignment="Top" Margin="0,0" >
<TextBlock Name="lbl_Appearence"
Text="{Binding Appearance , Mode=TwoWay }"
TextWrapping="Wrap" Foreground="Gray" Width="210" >
</TextBlock>
</Viewbox>
<Viewbox Grid.Column="5" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="2,2"
>
<Button Name="btn_DeleteItem"
Click="btn_DeleteItem_Click"
Resources="{StaticResource cartitembutton}" >
<Image Source="/Resources/Icons/delete.png" ></Image>
</Button>
</Viewbox>
</Grid>
</Viewbox>
</DataTemplate>
</ListView.ItemTemplate>
You should use data binding to retrieve the selected item. The source property will be automatically updated when the ListView.SelectedItem changes.
Touch is handled the same way as click is handled. The framework converts touch events to mouse events.
I noticed that you wrapped every element into a ViewBox. That is not necessary and will only degrade performance. When the elements are hosted inside a Grid they will resize automatically by default. Exceptions are elements that need an initial size like Shape or Image. But since the item containers won't resize themselves, the content of the containers won't resize too.
To force all elements to occupy max horizontal space you can set a Style that targets the item container:
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<Item>),
typeof(MainWindow),
new PropertyMetadata(default(ObservableCollection<Item>)));
public ObservableCollection<Item> Items
{
get => (ObservableCollection<Item>) GetValue(MainWindow.ItemsProperty);
set => SetValue(MainWindow.ItemsProperty, value);
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
"SelectedItem",
typeof(Item),
typeof(MainWindow),
new PropertyMetadata(default(Item), MainWindow.OnSelectedItemChanged));
public Item SelectedItem
{
get => (Item) GetValue(MainWindow.SelectedItemProperty);
set => SetValue(MainWindow.SelectedItemProperty, value);
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Items = new ObservableCollection<Item>();
// Initialize data source
CreateItems();
}
// Property changed callback of the SelectedItem property
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Reference to instance members in a class scope
var _this = d as MainWindow;
if (e.NewValue is ItemCollection selectedItem)
{
// Handle currently selected item
}
}
}
MainWindow.xaml
<Window>
<ListView ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}" />
</Window>

UWP access controls in ListViewItem from colletion

I have a ListView with two DataTemplate for items and headers.
Items from the ListView are binded to CollectionViewSource which looks like this:
<CollectionViewSource
x:Name="groupedItemsViewSource3"
Source="{Binding Groups2}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding Groups, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
I can manage to get ListViewItem but I cant get control of its child controls.
My ListView looks like this:
<ListView
Margin="0,40,0,0"
Width="580"
HorizontalAlignment="Right"
x:Name="itemGridView1"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource2}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick" Background="White">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Background="LightGray" Width="2500" Height="25">
<Border HorizontalAlignment="Stretch" BorderThickness="0,0,0,1" BorderBrush="Black">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Time}" Margin="10,0,0,0" Width="50" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding LiveTime}" Foreground="{Binding LiveTimeBGColor}" Margin="10,0,0,0" Width="40" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding TeamOne}" Margin="0,0,10,0" HorizontalTextAlignment="Right" Width="150" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
<Border Background="DarkGray" Width="35" Margin="0,0,2,0" Padding="15,0,0,0">
<TextBlock Text="{Binding ScoreTeamOne}" Width="30" Foreground="White" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</Border>
<Border Background="DarkGray" Width="35" Padding="15,0,0,0" Margin="2,0,0,0">
<TextBlock Text="{Binding ScoreTeamTwo}" Foreground="White" Width="30" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</Border>
<TextBlock Text="{Binding TeamTwo}" Margin="10,0,0,0" HorizontalAlignment="Left" Width="150" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="0,0,0,2" Width="2500" Background="{Binding HeaderLiveBGColor}">
<Button Foreground="{ThemeResource ApplicationHeaderForegroundThemeBrush}"
AutomationProperties.Name="Group Title"
Click="Header_Click"
Style="{StaticResource TextBlockButtonStyle}" Width="2500">
<StackPanel Orientation="Horizontal" Width="2500">
<TextBlock Text="{Binding LeagueTitle}" Margin="10,0,0,0" Width="441.9" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,20,0" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Any idea how can I check if the rights child control was clicked?
What I want to eventually achieve is the handle a click based on the controls of the ListViewItem clicked.
To get the clicked item from ListViewItem
If you add break points to debug your code, you could know there's a ClickedItem in ItemClickEventArgs class object. The ClickedItem should be that you want.
Another way is using TwoWay Binding on the SelectedItem.
Both the two ways are included in the following code sample:
<Page.Resources>
<CollectionViewSource
x:Name="groupedItemsViewSource3"
Source="{Binding Groups2}"
IsSourceGrouped="true"
ItemsPath="Items" />
</Page.Resources>
<Grid>
<ListView
Margin="0,40,0,0"
Width="580"
HorizontalAlignment="Right"
x:Name="itemGridView1"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource3}}"
SelectedItem="{Binding SelectedSong,Mode=TwoWay}"
SelectionMode="Single"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemGridView_ItemClick" Background="White">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Background="LightGray" Width="2500" Height="25">
<Border HorizontalAlignment="Stretch" BorderThickness="0,0,0,1" BorderBrush="Black">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Margin="10,0,0,0" Width="50" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="0,0,0,2" Width="2500" Background="{Binding HeaderLiveBGColor}">
<Button Foreground="{ThemeResource ApplicationHeaderForegroundThemeBrush}"
AutomationProperties.Name="Group Title"
Style="{StaticResource TextBlockButtonStyle}" Width="2500">
<StackPanel Orientation="Horizontal" Width="2500">
<TextBlock Text="{Binding Key}" Margin="10,0,0,0" Width="441.9" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,20,0" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
public sealed partial class MainPage : Page
{
public ObservableCollection<SongGroup> Groups2 { get; set; }
private Song _SelectedSong;
public Song SelectedSong
{
get { return _SelectedSong; }
set
{
_SelectedSong = value;
}
}
public MainPage()
{
this.InitializeComponent();
Groups2 = GenerateData();
this.DataContext = this;
}
private ObservableCollection<SongGroup> GenerateData()
{
ObservableCollection<SongGroup> songGroups = new ObservableCollection<SongGroup>();
ObservableCollection<Song> songs = new ObservableCollection<Song>();
songs.Add(new Song() { Title = "Song1" });
songs.Add(new Song() { Title = "Song2" });
songGroups.Add(new SongGroup() { Key = "A", Items = songs });
ObservableCollection<Song> songs2 = new ObservableCollection<Song>();
songs2.Add(new Song() { Title = "Song2_1" });
songs2.Add(new Song() { Title = "Song2_2" });
songGroups.Add(new SongGroup() { Key = "B", Items = songs2 });
return songGroups;
}
private void ItemGridView_ItemClick(object sender, ItemClickEventArgs e)
{
var song = e.ClickedItem;
}
}
public class Song
{
public string Title { get; set; }
}
public class SongGroup
{
public string Key { get; set; }
public ObservableCollection<Song> Items { get; set; }
}

How to set Listview Itemtemplate when drag drop in wpf

I have create simple drag drop in WPF. In my application there are two Listviews. I have to drag list items from first listview and drop the item to second listview. I have created custom data template for first listview. When i drag the first listview item into second listview the data template is not customized so items are not displayed. How to display the list items with generic. Please help. My Code is as below,
<Grid Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ListBox
Name="memberCollection"
Grid.Column="1"
Width="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PreviewMouseLeftButtonDown="memberCollection_PreviewMouseLeftButtonDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid
Name="gridDrop"
Grid.Column="0"
Margin="20,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ListBox.Drop="grid_Drop"
ShowGridLines="True">
<ListBox
Grid.Row="0"
Grid.Column="0"
Margin="10,10,0,0"
AllowDrop="True" />
</Grid>
</Grid>
Code Behind
ObservableCollection<Member> member = new ObservableCollection<Member>();
public MainWindow()
{
InitializeComponent();
member.Add(new Member { Name = "Karthick", ID = "20011", Address = "10, MainRoad, Chennai" });
memberCollection.ItemsSource = member;
DataContext = new Member();
}
private void memberCollection_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
object selectedMember = memberCollection.SelectedItem as Member;
if (selectedMember != null)
DragDrop.DoDragDrop(memberCollection, selectedMember, DragDropEffects.All);
}
private void grid_Drop(object sender, RoutedEventArgs e)
{
ListBox listContent = e.Source as ListBox;
if (listContent != null)
Console.WriteLine("", Grid.GetColumn(listContent), Grid.GetRow(listContent));
DataObject item = (((DragEventArgs)e).Data) as DataObject;
object Target = ((Grid)(sender)).DataContext;
object listItem = item.GetData(Target.GetType());
if (listItem != null)
{
//listContent.Items.Add(listItem.Name.ToString());
//listContent.Items.Add(listItem.ID.ToString());
//listContent.Items.Add(listItem.Address.ToString());
//listContent.ItemTemplate = memberCollection.ItemTemplate;
listContent.Items.Add(listItem);
}
}
If you define the DataTemplate as a reusable resource, you can use it in both ListBoxes:
<Grid Margin="0,20,0,0">
<Grid.Resources>
<DataTemplate x:Key="dataTemplate">
<StackPanel>
<TextBox Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ListBox
Name="memberCollection"
Grid.Column="1"
Width="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PreviewMouseLeftButtonDown="memberCollection_PreviewMouseLeftButtonDown"
ItemTemplate="{StaticResource dataTemplate}" />
<Grid
Name="gridDrop"
Grid.Column="0"
Margin="20,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ListBox.Drop="grid_Drop"
ShowGridLines="True">
<ListBox
Grid.Row="0"
Grid.Column="0"
Margin="10,10,0,0"
AllowDrop="True"
ItemTemplate="{StaticResource dataTemplate}"/>
</Grid>
</Grid>
If you want to display some other properties of the dropped Member in the second ListBox, you should define another ItemTemplate:
<ListBox
Grid.Row="0"
Grid.Column="0"
Margin="10,10,0,0"
AllowDrop="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Id}" />
<TextBlock Tag="{Binding Address}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Image Gallery with Flipview

I want to create a gallery image on the first page contains a thumbnail for that category and when thumbnail is selected, it will open the image and description of the selected image in flipview (can be swipe to the right and to the left when thumbnail is selected for the image before and after). I have difficulties when applying it into flipview.
Code:
MainPage XAML
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="3"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick" Background="#FF6996D1" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="240" Height="180">
<Border>
<Image Source="{Binding ImagePath}" Stretch="Uniform" AutomationProperties.Name="{Binding Title}"/>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
MainPage:
public MainPage()
{
this.InitializeComponent();
Gallery();
}
private async void Gallery()
{
var sampleDataGroups = await DataItemSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
}
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:
<Grid Grid.Row="1" x:Name="contentRegion" Background="#FF6996D1">
<Image Source="{Binding ImagePath}" HorizontalAlignment="Left" Height="559" Margin="84,20,0,49" VerticalAlignment="Center" Width="732"/>
<ScrollViewer x:Name="myScroll" VerticalScrollBarVisibility="Auto" Margin="852,60,50,91" VerticalScrollMode="Auto" HorizontalScrollBarVisibility="Auto">
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Height="2210" Width="425" FontSize="27" TextAlignment="Justify" />
</ScrollViewer>
</Grid>
ItemDetailPage Code:
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 item = await DataItemSource.GetItemAsync((String)e.NavigationParameter);
this.DefaultViewModel["Item"] = item;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
}
How do I apply flipview on ItemDetailPage?
Note:
For more code detail you can view the sample
To apply flipview on ItemDetailPage, we can add FlipView under "contentRegion" and set the Image and ScrollViewer as FlipView's ItemTemplate like following:
<Grid x:Name="contentRegion" Grid.Row="1" Background="#FF6996D1">
<FlipView ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Width="732"
Height="559"
Margin="84,20,0,49"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Source="{Binding ImagePath}" />
<ScrollViewer x:Name="myScroll"
Margin="852,60,50,91"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto">
<TextBlock Width="425"
Height="2210"
FontSize="27"
Text="{Binding Description}"
TextAlignment="Justify"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
And in code-behind, set the data source like following:
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 item = await DataItemSource.GetItemAsync((String)e.NavigationParameter);
var group = await DataItemSource.GetGroupByItemAsync(item);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Item"] = item;
}
Here I add a GetGroupByItemAsync(SampleDataItem item) method in DataItemSource which can retrieve the group according to the item.
public static async Task<SampleDataGroup> GetGroupByItemAsync(SampleDataItem item)
{
await _DataItemSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
var matches = _DataItemSource.Groups.Where(group => group.Items.Contains(item));
if (matches.Count() == 1) return matches.First();
return null;
}
Besides these, we also need to remove DataContext="{Binding Item}" form root Grid and put in in <Grid Background="#FF6996D1" DataContext="{Binding Item}">.
After this, the FlipView should be able to work. However here is a strange behavior, if we select the second or third image, the previous image in flip view won't show like following
We are investigating on this issue. As a workaround, we can disable FlipView's virtualization by changing its ItemsPanel like:
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
The complete XAML code of ItemDetailPage might like:
<Page x:Class="ImageGalerry.ItemDetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:ImageGalerry.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:ImageGalerry.Data"
xmlns:local="using:ImageGalerry"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding DefaultViewModel,
RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid d:DataContext="{Binding Groups[0].Items[0], Source={d:DesignData Source=/DataModel/DataItem.json, Type=data:DataItemSource}}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid Background="#FF6996D1" DataContext="{Binding Item}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1366" />
</Grid.ColumnDefinitions>
<Button x:Name="backButton"
Margin="39,59,0,0"
VerticalAlignment="Top"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
AutomationProperties.Name="Back"
Command="{Binding NavigationHelper.GoBackCommand,
ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}" />
<TextBlock x:Name="pageTitle"
Margin="120,40,30,40"
VerticalAlignment="Top"
IsHitTestVisible="false"
Style="{StaticResource HeaderTextBlockStyle}"
Text="{Binding Title}"
TextWrapping="NoWrap" />
<!--<MediaElement x:Name="mediaplayer" Source="images/ost.mp3" AudioCategory="BackgroundCapableMedia" />
<Button x:Name="PlayButton" Content="Stop" Click="PlayButton_Click" Margin="1274,72,0,30" />-->
</Grid>
<Grid x:Name="contentRegion" Grid.Row="1" Background="#FF6996D1">
<FlipView ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}">
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Width="732"
Height="559"
Margin="84,20,0,49"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Source="{Binding ImagePath}" />
<ScrollViewer x:Name="myScroll"
Margin="852,60,50,91"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto">
<TextBlock Width="425"
Height="2210"
FontSize="27"
Text="{Binding Description}"
TextAlignment="Justify"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
</Grid>
</Page>
Please note that disabling UI virtualization for FlipView may negatively impact performance especially when there are a lot of images. If you have a lot of images, you can try to use incremental loading and data virtualization.

Gridsplitter in a Grid with an ItemsControl

I'm trying to make a PropertyGrid Custom Control. The PropertyGrid will be very similar to the PropertyGrid used in Visual Studio. I've tried using the PropertyGrid of the Extended WPF Toolkit but you have to specify the category of a property with an attribute and we need to change the categories runtime. Which as far as I know is impossible with attributes.
So I'm making the PropertyGrid myself. This is my code so far:
The Generic.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HomeMadePropertyGrid"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<ControlTemplate x:Key="toggleButtonTemplate" TargetType="ToggleButton">
<Grid Width="15" Height="13" Background="Transparent">
<Path x:Name="ExpandPath" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="toggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Template" Value="{StaticResource toggleButtonTemplate}" />
</Style>
<Style TargetType="{x:Type local:PropertyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PropertyGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsControl ItemsSource="{TemplateBinding ItemsSource}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="toggleButton" Height="20" Width="20" Style="{StaticResource toggleButtonStyle}"/>
<TextBlock Text="{Binding Name}" FontWeight="Bold"></TextBlock>
</StackPanel>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}"
Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<TextBlock Background="White" Text="{Binding Path=Name}"/>
</Border>
<GridSplitter Width="1"
Grid.RowSpan="4" Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}"/>
<Border Grid.Column="2" BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<ContentPresenter Grid.Column="2" Content="{Binding Value}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Int32}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Double}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Boolean}">
<CheckBox IsChecked="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
PropertyGrid.cs
public class PropertyGrid : ItemsControl
{
public Brush GridColor
{
get { return (Brush)GetValue(GridColorProperty); }
set { SetValue(GridColorProperty, value); }
}
public static readonly DependencyProperty GridColorProperty =
DependencyProperty.Register("GridColor", typeof(Brush), typeof(PropertyGrid), new UIPropertyMetadata(new SolidColorBrush(Colors.Transparent)));
static PropertyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
}
PropertyGroup
public class PropertyGroup
{
public string Name { get; set; }
public List<PropertyGridItem> Items { get; set; }
public PropertyGroup()
{
Items = new List<PropertyGridItem>();
Name = "";
}
}
PropertyGridItem
public class PropertyGridItem
{
public string Name { get; set; }
public object Value { get; set; }
public PropertyGridItem(string propertyName, object propertyValue)
{
Name = propertyName;
Value = propertyValue;
}
}
This code in my MainWindow.xaml:
<local:PropertyGrid ItemsSource="{Binding Path=Groups}" GridColor="#f0f0f0"/>
Code behind of my ViewModel:
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
The ViewModel
public class ViewModel
{
public List<PropertyGroup> Groups { get; set; }
public ViewModel()
{
Groups = new List<PropertyGroup>();
PropertyGroup group1 = new PropertyGroup();
group1.Name = "Group1";
group1.Items.Add(new PropertyGridItem("Item1", "test"));
group1.Items.Add(new PropertyGridItem("Item2", 300));
group1.Items.Add(new PropertyGridItem("Item3", true));
group1.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group1);
PropertyGroup group2 = new PropertyGroup();
group2.Name = "Group2";
group2.Items.Add(new PropertyGridItem("Item1", "test"));
group2.Items.Add(new PropertyGridItem("Item2", 300));
group2.Items.Add(new PropertyGridItem("Item3", true));
group2.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group2);
}
}
The problem I'm having is that the GridSplitter is applied every row. I want the GridSplitter to be applied to all rows of a group. I understand that this is because I make a new Grid for every item. For the attached properties to work the items have to be a direct child of the Grid.
A DataGrid also isn't an option because the GridSplitter is only available between column headers.
So to make a long story short: how can I use a Grid in an ItemsControl with a GridSplitter that applies to all rows of ideally a group or the entire Grid if that isn't possible.
I finally found the solution to this problem.
To get this to work I had to:
Set the Grid.IsSharedSizeScope to true on the parent ItemsControl.
Set the SharedSizeGroup property on the name column with an arbitrary name.
Remove the star sizing on the name column. Having both the first and third column with star sizing resulted in a stuck GridSplitter for some reason.
Set a fixed width on the GridSplitter, I set the Width to 2.
Set the ResizeBehaviour of the GridSplitter to PreviousAndNext.
Here is a relevant piece of the resulting code:
<ItemsControl ItemsSource="{Binding Items}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="nameColumn"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource BodyPropertyGrid_CellBorder}">
<TextBlock Text="{Binding Path=Name}"/>
</Border>
<GridSplitter Grid.Column="1" Width="2"
ResizeBehavior="PreviousAndNext"
Style="{StaticResource BodyPropertyGridSplitter}"/>
<Border Grid.Column="2" Style="{StaticResource BodyPropertyGrid_CellBorder}">
<ContentControl Content="{Binding}"
ContentTemplateSelector="{StaticResource propertyItemTemplateSelector}"/>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Unfortunately you didn't provide us with a nice simple piece of code that only demonstrates your problem and I don't have time to get it to compile and run in my test project, so I can only give you suggestions and not tested solutions. Next time, please take the time to show a code example that we can just copy and paste into a project.
You might be able to get your 'stretched' GridSplitter if you join all of the row Grids using the Grid.IsSharedSizeScope Attached Property:
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter=
{StaticResource BoolToVisConverter}}" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="FirstColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="GridSplitterColumn" />
<ColumnDefinition Width="*" SharedSizeGroup="LastColumn" />
</Grid.ColumnDefinitions>
...
</Grid>

Categories