I am searching for a good example how to build a master/detail view for UWP Win 10 app like shown on this page: https://msdn.microsoft.com/en-us/library/windows/apps/dn997765.aspx
For example Windows Mail app has the same master/detail view. How can I implement this style? On the left side I think to use a listview, but how to show the data in the Detail side? Can I use a Frame or ContentPresenter? How can enable/disable the detail view on phone/tablet/pc?
Hope there is example or tutorial which shows how to deal with this.
It's good to have some app architecture... The Windows XAML community already worked on it.
https://github.com/Windows-XAML/Template10/tree/master/Samples/MasterDetail
I think:
https://blogs.msdn.microsoft.com/johnshews_blog/2015/09/09/a-minimal-mvvm-uwp-app/
is a good exemple.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Vertical">
<ListView x:Name="MainList"
ItemsSource="{x:Bind Organization.People, Mode=OneWay}"
SelectedIndex="{x:Bind Organization.SelectedIndex, Mode=TwoWay}"
MinWidth="250" Margin="5">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModels:PersonViewModel" >
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Vertical">
<TextBox
Text="{x:Bind Organization.SelectedPerson.Name, Mode=TwoWay, FallbackValue=''}"
Margin="5" />
<TextBox
Text="{x:Bind Organization.SelectedPerson.Age, Mode=TwoWay, FallbackValue='0'}"
Margin="5" />
</StackPanel>
</Grid>
You can also find another exemple in samples app :https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlListView
You can also use the MasterDetailsView Control provided by the UWP Community Toolkit.
You can use region frames. Here is an example for MvvmCross.
<SplitView x:Name="RootSplitView"
DisplayMode="Inline"
OpenPaneLength="256"
IsTabStop="False">
<SplitView.Pane>
<StackPanel Margin="0,50,0,0">
<Button Content="Second" Command="{x:Bind Vm.SecondCommand}" />
<Button Content="Third" Command="{x:Bind Vm.ThirdCommand}" />
</StackPanel>
</SplitView.Pane>
<!-- OnNavigatingToPage we synchronize the selected item in the nav menu with the current page.
OnNavigatedToPage we move keyboard focus to the first item on the page after it's loaded and update the Back button. -->
<Frame x:Name="FrameContent">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition>
<NavigationThemeTransition.DefaultNavigationTransitionInfo>
<EntranceNavigationTransitionInfo/>
</NavigationThemeTransition.DefaultNavigationTransitionInfo>
</NavigationThemeTransition>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</SplitView>
In the code behind file add
public Frame AppFrame { get { return (Frame)this.WrappedFrame.UnderlyingControl; } }
Add this in setup.cs file
protected override IMvxWindowsViewPresenter CreateViewPresenter(IMvxWindowsFrame rootFrame)
{
return new MvxWindowsMultiRegionViewPresenter(rootFrame);
}
for newer versions use:
protected override IMvxWindowsViewPresenter CreateViewPresenter(IMvxWindowsFrame rootFrame)
{
return new MvxWindowsMultiRegionViewPresenter(rootFrame);
}
Add following attribute at the top of the code behind file of child view:
[MvxRegion("FrameContent")]
For later versions:
[MvxRegionPresentation("FrameContent")]
Use this for navigating to child view:
ShowViewModel<SecondViewModel>()
Refer to this link: https://depblog.weblogs.us/2015/11/23/mvvmcross-uwp-splitview/
Examples: https://github.com/MvvmCross/MvvmCross-Samples/tree/master/XPlatformMenus
Related
To be honest I somehow have a hard time with MVVM.
To be more exact I have a hard time with creating/changing pages in MVVM.
This is my MainPage containing my SplitView:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<!--#region Splitview-->
<Grid.RowDefinitions >
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<RelativePanel >
<Button
FontFamily="Segoe MDL2 Assets"
Content=""
FontSize="30"/>
</RelativePanel>
<SplitView Grid.Row="1"
x:Name="SplitView"
DisplayMode="CompactOverlay"
IsPaneOpen="False"
CompactPaneLength="49"
OpenPaneLength="160"
PaneBackground="DarkGray"
>
<SplitView.Pane>
<StackPanel>
<ListBox SelectionMode="Single"
x:Name="lbHamburgerMenue"
Background="Gray"
HorizontalAlignment="Left">
<ListBoxItem Margin="0,5,0,2.5"
Background="DarkGray">
<StackPanel Orientation="Horizontal">
<TextBlock FontFamily="Segoe MDL2 Assets"
Text=""
FontSize="30"
VerticalAlignment="Center"/>
<TextBlock Margin="15,0,0,0"
Text="Home"
FontSize="20"
VerticalAlignment="Center"/>
</StackPanel>
</ListBoxItem>
<ListBoxItem Margin="0,2.5,0,2.5"
Background="DarkGray">
<StackPanel Orientation="Horizontal">
<TextBlock FontFamily="Segoe MDL2 Assets"
Text=""
FontSize="30"
VerticalAlignment="Center"/>
<TextBlock Margin="15,0,0,0"
Text="New Movie"
FontSize="20"
VerticalAlignment="Center"/>
</StackPanel>
</ListBoxItem>
</ListBox>
</StackPanel>
</SplitView.Pane>
<SplitView.Content>
<!--#endregion-->
<!--#region content area-->
<Grid Grid.Row="1" Grid.Column="1" >
</Grid>
</SplitView.Content>
</SplitView>
</Grid>
For the Content I am kinda lost.
How do I get a View displayed when another ListBox.Item gets selected?
I know I need a Frame and inside that Frame the View has to be, but I would like some help in that regard because I am stuck here. (or with views in MVVM in general).
Yes, you are correct on your first assumption. You need to replace your GridView in the SplitView.Content section with a Frame. Then when you want to navigate to a particular view, you call Frame.Navigate.
For MVVM to keep your main page ViewModel from having intimate knowledge of your views, you can define a NavigationService that encapsulates page and view structure and the view types.
Here is a good article about the basics of MVVM and building a NavigationService: https://msdn.microsoft.com/en-us/magazine/jj651572.aspx (go to 'Setting up Navigation' section).
Another great description from StackOverflow: Navigating to a new page from the View Model in Windows Phone 8.1 universal app.
Due to "not understanding everything quite right" I ended up with a "bad" solution - works fine, but should be much less code/work.
This helped me very much understanding switching between Views! And I ended up using it.
Sadly I could not find a good tutorial to just change the <SplitView.Content> so I "Copy & Paste" it on every View.
I have currently a hamburger menu for my UWP app build using SplitView.Pane.
The problem with this implementation is, only the "Symbol" or <Button> element is actually clickable, not the text beside it in the pane.
I read on this forum and other tutorials that some have solved this problem by using a ListView, but in the GitHub samples I see that they have done this with CS instead of a simpler XAML implementation.
Does anybody know how to do this in the XAML directly?
ListView is an ItemsControl, so it can contain a collection of items of any type. To populate the view, add items to the Items collection, or set the ItemsSource property to a data source.
For more info, see ListView.
A common scenario is to bind to a collection of business objects. In C# and Visual Basic, the generic ObservableCollection class is a good collection choice for data binding. For more info, see Binding to a collection of items.
But, we can also add ListViewItem to the ListView in XAML code.
For example:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<RelativePanel>
<Button FontFamily="Segoe MDL2 Assets" FontSize="36" Content="" Click="Button_Click"></Button>
</RelativePanel>
<SplitView Grid.Row="1" Name="mySplitView" DisplayMode="CompactOverlay" OpenPaneLength="200" CompactPaneLength="56" HorizontalAlignment="Left">
<SplitView.Pane>
<ListView Name="MyListView" SelectionChanged="ListView_SelectionChanged">
<ListView.Items>
<ListViewItem Name="FristItem">
<StackPanel Orientation="Horizontal">
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="36" Text=""></TextBlock>
<TextBlock Margin="20,0,0,0" Text="Click" FontSize="36"></TextBlock>
</StackPanel>
</ListViewItem>
<ListViewItem Name="SecondItem">
<StackPanel Orientation="Horizontal">
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="36" Text=""></TextBlock>
<TextBlock Margin="20,0,0,0" Text="Click" FontSize="36"></TextBlock>
</StackPanel>
</ListViewItem>
</ListView.Items>
</ListView>
</SplitView.Pane>
<SplitView.Content>
<Frame Name="MyFrame"></Frame>
</SplitView.Content>
</SplitView>
</Grid>
In code behind:
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MyListView.SelectedItem.Equals(FristItem))
{
}
else if (MyListView.SelectedItem.Equals(SecondItem))
{
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mySplitView.IsPaneOpen = !mySplitView.IsPaneOpen;
}
Well there are some xaml examples of this available on GitHub...
Here is one: https://github.com/AppCreativity/Kliva/blob/master/src/Kliva/Controls/SidePaneControl.xaml#L25
And here is another one:
https://github.com/JustinXinLiu/SwipeableSplitView/blob/master/GestureDemo/Shell.xaml#L103
In short, you just add the ListView in your Pane part of the SplitView and take note of the DataTemplates used to be sure that you have an icon and text.
Like this:
https://github.com/AppCreativity/Kliva/blob/c36d65058c4c35f0a3d2c7c886df81ba5ecfb31b/src/Kliva/XAMLResources/DataTemplates.xaml#L410
I'm trying to create master/detail UI, like in Win10 email app. I have split my app to user controls, and my problem is how to communicate between them.
When user selects item from main list (which is inside pivot item
usercontrol), I want to display detailed info on the right side of the
main list.
MainView.xaml (simplified)
<Page>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="GridMainColumn" Width="400" />
<ColumnDefinition x:Name="GridDetailColumn" Width="*" />
</Grid.ColumnDefinitions>
<!-- Pivot -->
<Grid x:Name="GridPivot" Grid.Row="0" Grid.Column="0">
<Pivot SelectedIndex="{Binding Path=SelectedPivotIndex, Mode=TwoWay}"
Margin="0,0,0,0">
<PivotItem>
<view:CarsPivotItemView/>
</PivotItem>
<PivotItem>
<view:HotelsivotItemView/>
</PivotItem>
</Pivot>
</Grid>
<!-- Detailed view -->
<Grid x:Name="GridDetail" Grid.Column="1" Grid.Row="0">
<view:CarDetailsView
Visibility="{x:Bind ViewModel.CarSelected,
Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}"/>
<view:HotelDetailsView
Visibility="{x:Bind ViewModel.HotelSelected,
Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}"/>
</Grid>
</Grid>
<Page>
Note* CarsPivotItemView is user-control which has its own view model.
CarsPivotItemView.xaml (simplified)
<UserControl>
<RelativePanel>
<TextBox x:Name="SearchBox" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<interactivity:Interaction.Behaviors>
<behaviors:TextBoxEnterKeyBehavior>
<core:InvokeCommandAction Command="{Binding EnterKeyDownCommand}"/>
</behaviors:TextBoxEnterKeyBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<ListView ItemsSource="{x:Bind ViewModel.Cars}" SelectedItem="{Binding SelectedCar, Mode=TwoWay}" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignBottomWithPanel="True" RelativePanel.Below="SearchBox" Margin="0,0,0,0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding CarSelectedCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:ICar">
<StackPanel Margin="0,10,0,10">
<TextBlock Text="{x:Bind Name}"/>
<TextBlock Text="{x:Bind FullMakeModel}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RelativePanel>
</UserControl>
Now when user selects car from CarsPivotItemView ListView, I want to display CarDetailsView on MainView.
How should I bind this selected car from CarsPivotItemView to CarDetailsView?
Should I use e.g. MVVM light Messenger to send message from CarsPivotItemViewViewModel to CarDetailsViewModel?
Overall, is this good idea to split this kind of pieces to user control?
I'm using MVVM light and Template10 on my app.
Windows App Studio team has good UWP samples in two ways:
The generated code you can get in their portal (http://appstudio.windows.com) where you can find good implementation patterns,
they have a github account where you can find a bunch of responsive controls and usage samples (https://github.com/wasteam/waslibs)
On the Cars ListView, it looks like the SelectedItem binding is pointing at a different binding source than everything else.
It should be {x:Bind ViewModel.SelectedCar, Mode=TwoWay}. Then in the setter for SelectedCar, you can set ViewModel.CarSelected = true, which should display it in the details view
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.
I'm having some trouble with databinding to a LongListSelector. When I initally run this method, it works. The data is binded and everything is good.
The problem is when I press the refresh button, my app fetches a new JSON feed, parses it and tries to bind it. My app seems to download the JSON and stuff, but doesn't refresh the UI. Where have I gone wrong?
I've tried BoardLongList.ItemsSource = null; and BoardLongList.ItemsSource.Clear(); to no avail.
Any ideas? The JObject is from newtonsoft.json and the RootObject is from ViewModels.RootObject.
Thanks in advance!
private void Bind(JObject rootObject)
{
string rootObjectString = rootObject.ToString();
RootObject obj = JsonConvert.DeserializeObject<RootObject>(rootObjectString);
// Bind to LongListSelector
BoardLongList.ItemsSource = obj.Movements;
}
My XAML:
<phone:LongListSelector Grid.Row="1" x:Name="BoardLongList" Margin="0,0,-12,1" ItemsSource="{Binding Movement}">
<phone:LongListSelector.ItemTemplate >
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2" Height="50">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding A1}" />
<TextBlock Grid.Column="1" Text="{Binding A2}" />
<TextBlock Grid.Column="2" Text="{Binding A3}" />
<TextBlock Grid.Column="3" Text="{Binding A4}" />
</Grid>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
Is your ItemsSource an ObservableCollection?.ObservableCollection is collection with notification that when something is changed in the collection, it notifies the UI.
Make obj.Movements an ObservableCollection
Did you try the pull to refresh feature?
Check this out!
You can accomplish this with the ItemRealized event and using a ListHeader(or ListFooter to pull from bottom). Within the ItemRealized event you check if the item is your header object. If it is then load more items.
Hope it helps.
I finally figured it out. It was because of C# web caching my request. Not because of my databinding!