Resolve ViewModels in WinPhone 8.1, Prism and Unity - xaml

I am developing an Windows Phone 8.1 App with Prism for Windows Store Apps and Unity as IoC container. I got the basics working so far and the convention based resolving of View and ViewModel works so far.
For navigation I want to use a Drawer Layout (with a flyout menu) and display the selected models inside the MainPage (rather than navigating to pages).
So I have a MainViewModel (which gets correctly instantiated and assigned with MainPage, using a custom name convention). And I have a DrawerViewModel. The drawer is inside the MainPage, where also the other ViewModels shall be displayed.
My MainPage.xaml:
<Page
x:Class="StackExchangeReader.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:StackExchangeReader"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:StackExchangeReader.Models"
xmlns:drawerLayout="using:DrawerLayout"
xmlns:prism="using:Microsoft.Practices.Prism.Mvvm"
xmlns:designViewModels="using:StackExchangeReader.DesignViewModels"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="True"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DataContext="{d:DesignInstance designViewModels:DrawerDesignViewModel, IsDesignTimeCreatable=True}">
<Grid x:Name="RootLayout">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Title Bar -->
<Grid x:Name="TitleBar" Background="#00ADEF" Grid.Row ="0" Height="60">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Margin="5" x:Name="DrawerIcon" Grid.Column="0" Source="/Assets/drawer_icon.png" HorizontalAlignment="Left" Tapped="DrawerIcon_Tapped" />
<TextBlock Grid.Column="1" Text="{Binding Title}" Foreground="White" VerticalAlignment="Center" FontSize="18"/>
</Grid>
<!-- Drawer Layout -->
<drawerLayout:DrawerLayout Grid.Row="1" x:Name="DrawerLayout">
<Grid x:Name="MainFragment" Background="White">
<!-- Main Content goes here -->
</Grid>
<Grid x:Name="ListFragment" Background="#F4F4F4">
<ListView DataContext="{Binding Sites}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="18" Foreground="Black" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</drawerLayout:DrawerLayout>
</Grid>
</Page>
The two things here to be notice are:
MainFragment Grid, where the other ViewModels shall be displayed (i.e. after clicking an option in the Drawer Layout menu)
ListFragment Grid, with the options the user can select
Now the actual problem/question:
Since Prism resolves ViewModels and Views by convention, of course...
... it won't resolve the DrawerViewModel.
I can't instantiate it in XAML, cause it doesn't have a parameterless constructor and it has dependencies injected, so it's required that it's resolved via Unity.
Adding a property in MainViewModel could be possible, but then I'd need to inject it. While it would solve this one problem, it doesn't solve the problem that I also need to instantiate the Content (inside MainFragment) too. Also it's a bad idea to make ViewModels independent of each other. After all, MVVM should support loose coupling.
I also tried to define ViewModel dependency on the MainPage class, but Prism/Unity do not seem to be call this constructor (which Unity should do, if they were to be resolved via IoC/DI, because Unity chooses the constructor with the highest number of dependencies when it resolves the class)
I could also inject the IoC container into the MainViewModel, but this makes my ViewModel(s) depend on IoC. And passing IoC into ViewModel is a bad practice anyways.
I also considered using an abstract factory. This would avoid dependency on IoC container, but it feels like it only shifts the problem.
First things first:
What would be the correct and elegant way to assign an instance of DrawerViewModel as ListFragment's DataContext?
Could designing the ListFragment as an UserControl help allow me that the DrawerViewModel is correctly resolved and assigned to it? Anyone (preferably with Enterprise Business Application experience) having the same issues and a solution for this problem?
Just for the record, the two ViewModel classes.
MainViewModel.cs
public class MainViewModel : ViewModel
{
private INavigationService navigation;
private IEventAggregator events;
public MainViewModel(INavigationService navigation, IEventAggregator events)
{
this.navigation = navigation;
this.events = events;
}
public string Title { get; set; } = "Example";
private ViewModel selectedViewModel;
public ViewModel SelectedViewModel
{
get { return selectedViewModel; }
set { SetProperty(ref selectedViewModel, value); }
}
}
DrawerViewModel.cs
public class DrawerViewModel : ViewModel
{
private ISiteService siteService;
private IEventAggregator events;
public DrawerViewModel(ISiteService siteService, IEventAggregator events)
{
this.siteService = siteService;
this.events = events;
sites = new ObservableCollection<Site>();
}
private ObservableCollection<Site> sites;
public ObservableCollection<Site> Sites
{
get { return sites; }
}
}

Related

How to Bind Parallax View within GridView DataTemplate?

I am trying to use a ParallaxView to make an image within the GridViewItem parallax as the GridView is scrolled. The intended effect is the same as the newsfeed in the Xbox UWP app on PC; images on the listview items there parallax as you scroll. This is shown visually in the following image:
I am running into a databinding data context issue, however. All of the examples I have seen are to make the background of the entire GridView or ListView parallax. A working example of that is as follows (very similar to the XAML Controls Gallery Sample found here):
<Grid>
<ParallaxView Name="GridViewParallaxView"
Source="{x:Bind MyGridView}"
VerticalShift="100">
<!-- This is the background image that parallaxes. -->
<Image></Image>
</ParallaxView>
<GridView Name="MyGridView">
<!-- GridView Content Here... -->
</GridView>
</Grid>
The problem I am running into is when trying to place the ParallaxView INSIDE of the DataTemplate in the ItemTemplate in the GridView.
<GridView Name="MyGridView"
ItemsSource="{x:Bind MyDataList}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="models:MyDataType">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ParallaxView Name="GridViewParallaxView"
Grid.Row="0"
Source="{x:Bind MyGridView}"
VerticalShift="100">
<!-- This is the image ON EACH GRIDVIEW ITEM that parallaxes. -->
<Image Source="{x:Bind MySource}"></Image>
</ParallaxView>
<TextBlock Name="ItemTitleTextBlock"
Grid.Row="1"
Text="{x:Bind Title}"></TextBlock>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Note that some properties (like MinWidth and Margin) have been omitted for brevity.
The code behind (not totally relevant, but to add context to some of the bindings):
public class MyPage : Page
{
public ObservableCollection<MyDataType> MyDataList { get; set; }
}
public class MyDataType
{
public string Title { get; set; }
public ImageSource MySource { get; set; }
}
This does not work because placing the 'ParallaxView' inside of the 'DataTemplate' changes the 'DataContext'. MyGridView can no longer be bound to directly like that. So how do I bind it?
Also, I read that the DataContext property is inherited by children in the XAML tree. I need the Image databinding to be in the same context as the DataTemplate. Is there a way to just change the DataContext for the ParallaxView?
You could use Binding, instead of x:Bind.
<ParallaxView Name="GridViewParallaxView"
Grid.Row="0"
Source="{Binding ElementName=MyGridView}"
VerticalShift="100">
<!-- This is the image ON EACH GRIDVIEW ITEM that parallaxes. -->
<Image Source="{x:Bind MySource}"></Image>
</ParallaxView>

How to set default NON-transparent background in Xamarin Forms?

I just started developing with Xamarin Forms and facing a rather simple problem, but I could not find a solution:
I have some UI elements stacked over each other like the following:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label x:Name="LabelLeft" Grid.Column="0" Text="LEFT" />
<Label x:Name="LabelRight" Grid.Column="2" Text="RIGHT" />
<Grid x:Name="GridMain" Grid.Column="0" Grid.ColumnSpan="3" Margin="0"
BackgroundColor="Default">
<Grid.GestureRecognizers>
<PanGestureRecognizer PanUpdated="PanGestureRecognizer_OnPanUpdated"/>
</Grid.GestureRecognizers>
<Grid Margin="10">
<Label Text="MAIN"/>
</Grid>
</Grid>
</Grid>
The GridMain can then be "panned away" to "show" the two labels.
But now I have the problem that the grids background color Default is not the target systems default background color, but Transparent -> I can "see" the labels underneath the grid. Also I can only pan the grid when I click/touch the string inside the grid and not anywhere inside the grid.
How can I set the background to use the "non-transparent" default background?
I do not want to use hard-coded White as I also want to support e.g. the dark theme for UWP and there it should be Black.
For now I used Accent to continue working, but that is not what I actually want.
As a workaround you could create a dependency service that would return the color you want to use for the given OS.
First create a IColorService interface in the shared project:
public interface IColorService
{
Color SystemBackgroundColor { get; }
}
Then implement it on each platform:
[assembly: Xamarin.Forms.Dependency(typeof(ColorService))]
namespace App.UWP
{
public class ColorService : IColorService
{
public Color SystemBackgroundColor =>
Application.Current.RequestedTheme == ApplicationTheme.Dark ?
Color.Black : Color.White;
}
}
Next, create a static class in the shared project, that will allow easy access to the color:
public static class Colors
{
public static Color SystemBackgroundColor =>
DependencyService.Get<IColorService>().SystemBackgroundColor;
}
Finally use the color as the BackgroundColor of your Grid:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App"
x:Class="App.MainPage">
<Grid BackgroundColor="{x:Static local:Colors.SystemBackgroundColor}" />
</Grid>
</ContentPage>
Notice the added xmlns:local pointing to the namespace where the Colors class is defined.

How to customize ListViewItem properly with hideable content and bind it to a model, in UWP?

I would like to create a ListView where items have a "hover" and a "selected" state, displaying different content in each state.
There have been some similar question here on StackOverflow, but none helped in my particular case.
Let's say I have a model:
public class TagFile : BaseBind // A class with INotifyProperyChanged interface
{
private string path;
public String Path
{
get { return path; }
set
{
SetProperty(ref path, value);
}
}
public void SelectButtonClick()
{
// Do something
}
public void HoverButtonClick()
{
// Do something
}
}
...then I have a main ViewModel:
public class AppViewModel : BaseBind
{
public ObservableCollection<TagFile> ItemsList { get; set; }
// Other things
}
... then a Page:
<Grid>
<ListView ItemsSource="ItemsList">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TagFile">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind Path, mode=OneWay}"/>
<Button Content="Select Button" Click="{x:Bind SelectButtonClick}"/>
<Button Content="Hover Button" Click="{x:Bind HoverbuttonClick}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I would like to customize ListViewItem Style in order to make the SelectButton appear only when the ListViewItem is selected and the HoverButton only when the cursor is onto it.
I already know that I need to play with ItemTemplate and ItemContainerStyle, but it seems to be more difficult than I tought at the beginning, because I could create a custom style for the ItemContainerStyle in this way (using the IsSelected property from ListViewItem):
<Style TargetType="ListViewItem" x:Key="TestContainerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentPresenter x:Name="Presenter" Content="{TemplateBinding Content}"/>
<Button Grid.Row="1" Content="Select Button" Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
...but in this way:
1) I loose all the default brushes;
2) I don't know how I could bind the SelectButton to the model's command;
3) I would have to find a way for the "hover" state, in order to display the HoverButton;
4) Last, but not least... I would like to include all the customizations in the App.xaml file (or in associated ResourceDictionary files, as I need them across the entire app).
This is it... I have tried various pattern from StackOverflow answers, but my case includes many differnt things (compile binding in App.xaml files, create a "hover" state, using binding in a Style without a DataType, etc.) that are answered here only separately.
I need to... put them all together, and it seems more tricky than I thought, as I said.
Thank you for your help, really appreciate.
EDIT: the behaviour should be like the Groove App list items: some buttons (Play and Remove item) appears only when the the item is selected or in hover state.
Normal state:
...and Hover/selected state:
There are two ways where you can achieve what you want:
The first one: I don't prefer it as you will need to copy the whole style and embed your item template inside the style. It also doesn't scale if you want to have the same behavior for multiple parts of your app.
You can copy the ListViewItem style which uses UIElement tree and visual states instead of a ListViewItemPresenter. This way you don't lose default brushes.
Note: look in the above link for the template with x:Key="ListViewItemExpanded".
<!-- Style for Windows.UI.Xaml.Controls.ListViewItem -->
<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
Second Note:
These styles and resources are based on the Windows Software
Development Kit (SDK) for Windows 10, Version 1511 (Windows SDK
version 10.0.10586.0). Styles and resources from other versions of the
SDK might have different values. For design purposes, generic.xaml is
available in the (Program Files)\Windows
Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic
folder from a Windows SDK installation.
Now you can use pointerover and selected states to show and hide different elements.
Second approach: Using custom control [Recommended]
Break your problem to two things:
You can define IsSelected property in your model/viewmodel for each item, and update that within your select command. Now you can control the first button visibility by binding to IsSelected.
Second is the hover part, you create a custom control which expose a dependency property IsPointerOver and you can bind visibility of the 'Hover Button' to it.
Example:
public class ExtendedControl : ContentControl
{
public static readonly DependencyProperty IsPointerOverProperty =
DependencyProperty.Register("IsPointerOver", typeof(bool), typeof(ExtendedControl),
new PropertyMetadata(false));
public bool IsPointerOver
{
get => (bool)GetValue(IsPointerOverProperty);
protected set => SetValue(IsPointerOverProperty, value);
}
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
base.OnPointerEntered(e);
IsPointerOver = true;
}
protected override void OnPointerCanceled(PointerRoutedEventArgs e)
{
base.OnPointerCanceled(e);
IsPointerOver = false;
}
protected override void OnPointerExited(PointerRoutedEventArgs e)
{
base.OnPointerExited(e);
IsPointerOver = false;
}
}
Xaml:
<ListView.ItemTemplate>
<DataTemplate >
<local:ExtendedControl x:Name="ExtendedControl">
<StackPanel>
<Button Content="Always Visible"/>
<Button Content="Only on hover" Visibility="{x:Bind ExtendedControl.IsPointerOver,Mode=OneWay,Converter={StaticResource BoolToVisibilityConverter}}"/>
</StackPanel>
</local:ExtendedControl>
</DataTemplate>
</ListView.ItemTemplate>

Update UserControl Textblock text from mainpage.xaml

I am writing a Windows Phone 8.1 Silverlight App. I made a user control NotificationsIconUserControl. It just contains a BELL/ALARM icon and textblock to display number of unread notifications.
I want to update this textblock text from mainpage.xaml
How to do this?
I tried using usercontrol expose properties but its the opposite thing. Also tried help from this question. how to use dependency property. Please edit the code below:
Usercontrol XAML:
<Grid x:Name="LayoutRoot"
Background="Transparent"
Height="Auto"
Width="Auto">
<Image
Name="Alarm_Icon"
Source="/Images/Status/Notification_Icon_1.png">
</Image>
<Ellipse
Name="Counter_Icon"
Height="45"
Width="45"
Margin="60,14,-6,50"
StrokeThickness="0"
Fill="{StaticResource DefaultTheme_IndianRedColor}">
</Ellipse>
<TextBlock
Name="Counter_Label"
Foreground="{StaticResource DefaultTheme_LightColor}"
FontSize="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
Margin="75,20,8,58"/>
</Grid>
Mainpage XAML part:
xmlns:MyUserControls="clr-namespace:Project.Custom.UserControls">
Mainpage .cs part:
private void ConfigureNotificationsIcon()
{
int NotificationsCounter = 4;
NotificationsIconUserControl NotificationsIconUserControlObject = new NotificationsIconUserControl();
NotificationsIconUserControlObject.Counter_Label.Text = NotificationsCounter.ToString();
}
I checked your code and its working completely....
ANd for the part of adding a Dependency property, write the following in the .cs file of UserControl
public partial class NotificationIconUserControl : UserControl
{
public NotificationIconUserControl()
{
InitializeComponent();
}
public string NotificationLabel
{
get { return (string)GetValue(NotificationLabelProperty); }
set { SetValue(NotificationLabelProperty, value); }
}
// Using a DependencyProperty as the backing store for Spacing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NotificationLabelProperty =
DependencyProperty.Register("NotificationLabel", typeof(string), typeof(NotificationIconUserControl), new PropertyMetadata("hellllo"));
}
After that you can use TemplateBinding to do your job

Create a component and reuse it in Windows phone 8.1

I want to create some components and reuse them in different pages, put more than one in a page, etc.
For example, I want to create a component that contains an image, some text, etc. The position of the elements are fixed, but I will change the image, the text... I mean, in a same page I want to put three circles with different image and text...
What is the best way to do it? I've found UserControl, but I'm unable to call a method from another page to change something.
This is my component XAML
<UserControl
x:Class="aa.Components.CircularGraph"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Components"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<Grid Name="view">
<Image Name="imageGraph" Source="../Assets/aa/circuloGris.png"
/>
<StackPanel Orientation="Vertical">
<TextBlock Name="firstLine" Text="1" FontFamily="Arial" FontSize="9"></TextBlock>
<TextBlock Name="secondLine" Text="2" FontFamily="Arial" FontSize="9"></TextBlock>
<TextBlock Name="thirdLine" Text="3" FontFamily="Arial" FontSize="9"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
Its code:
public sealed partial class CircularGraph: UserControl {
public CircularGraph() {
this.InitializeComponent();
Height = 300;
Width = 400;
}
public void changeFirstLine(string var) {
firstLine.Text = var;
}
}
In other page I put:
<local:CircularGraph Name="circularGraph"/>
And I've tried to put this in .cs:
protected override void OnNavigatedTo(NavigationEventArgs e) {
circularGraph.changeFirstLine("aaa");
}
But I have an error: The name 'circularGraph' does not exits in the current context.
How can I do this?
Sorry if it's a simple question. I'm newbie at Windows phone.
Thank you very much!
Try x:Name instead of Name. "All x:Name means to XAML is generate a field to store the value in the code behind class."
<local:CircularGraph x:Name="circularGraph"/>
In WPF, what are the differences between the x:Name and Name attributes?