OnIdiom for Grid columnspan - xaml

I need to use OnIdiom for grid column definition, but it isn't working. See my code below. Can anyone tell me what I am doing wrong and suggest a solution?
<Grid.ColumnDefinitions>
<ColumnDefinition>
<ColumnDefinition.Width>
<OnIdiom x:TypeArguments="GridLength" Tablet="64.9*" Phone="99.8*"/>
</ColumnDefinition.Width>
</ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition.Width>
<OnIdiom x:TypeArguments="GridLength" Tablet="0.1*" Phone="0.1*"/>
</ColumnDefinition.Width>
</ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition.Width>
<OnIdiom x:TypeArguments="GridLength" Tablet="35*" Phone="0.1*"/>
</ColumnDefinition.Width>
</ColumnDefinition>
This does not help, so I did:
<Grid.ColumnSpan>
<OnIdiom x:TypeArguments="x:Int32" Phone="3" Tablet="1"/>
</Grid.ColumnSpan>
This also not helps.

x:DataType instead of x:TypeArguments works for me..
<Grid.RowDefinitions>
<RowDefinition >
<RowDefinition.Height>
<OnIdiom x:DataType="GridLength" Tablet=".25*" Phone="0.075*" />
</RowDefinition.Height>
</RowDefinition>
<RowDefinition >
<RowDefinition.Height>
<OnIdiom x:DataType="GridLength" Tablet="*" Phone="0.85*" />
</RowDefinition.Height>
</RowDefinition>
<RowDefinition >
<RowDefinition.Height>
<OnIdiom x:DataType="GridLength" Tablet=".25*" Phone="0.075*" />
</RowDefinition.Height>
</RowDefinition>
</Grid.RowDefinitions>

override void OnSizeAllocated(double width, double height)
{
//according with height and width you can design your layouts
if (Device.Idiom == TargetIdiom.Phone)
{
// layout views vertically
}
else
{
// layout views horizontally or use different Page
}
}

Related

Reuse large quantity of Xaml interface elements

I have made a very similar post about this earlier where I asked how to reuse a part of some Xaml code. Although that worked for the small element I had, it does not quite work on the current scale.
Using Xamarin, I have a tabbed layout with two pages. A page consists of a grid with three layers, two of which are the same on both pages. The third layer needs to be different on both pages. Both pages are currently in the same file. This means that a large portion of the code is simply a copy-paste, and it makes it more difficult to edit values of certain labels in those first two rows since they can't have similar IDs / Names.
I currently set a string variable in the codebehind with a setter.
string _vesselCode;
public string VesselCode
{
get
{
return _vesselCode;
}
set
{
_vesselCode = value;
currentVessel.Text = _vesselCode;
currentVesselClone.Text = currentVessel.Text;
}
}
Although this works, I still have a lot of duped code (as shown below).
This is the content of each page. The only page-specific content is at the bottom. The rest is literately copy-pasted to the second page. Code below is without any IDs.
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<!-- first row from top {0} - app name, vessel. -->
<RowDefinition Height="Auto"/>
<!-- second row {1} - originele freq // nieuwe frequentie. -->
<RowDefinition Height="Auto"/>
<!-- third row {2} - tablayout. -->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- header contents -->
<Grid Grid.Row="0" StyleClass="headerBar">
<StackLayout Grid.Column="0" HorizontalOptions="Start" StyleClass="inGrid">
<Label StyleClass="header"
Text="OV Frequentie"/>
<Label StyleClass="headerSub"
Text="huidig voertuig:"/>
</StackLayout>
</Grid>
<!-- freq bar contents -->
<Grid Grid.Row="1" StyleClass="subHeaderBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<BoxView Grid.ColumnSpan="2"
BackgroundColor="#2d313a"
WidthRequest="2"
HeightRequest="2"
Margin="5,0,5,-3"/>
<StackLayout Grid.Column="0" HorizontalOptions="Start" StyleClass="inGrid">
<Label StyleClass="generalSmallText"
Text="originele freq: 10"/>
</StackLayout>
<StackLayout Grid.Column="1" HorizontalOptions="End" StyleClass="inGrid">
<Label StyleClass="generalSmallText"
Text="nieuwe freq: 10"/>
</StackLayout>
</Grid>
<!-- info page content -->
<Grid Grid.Row="2">
<StackLayout StyleClass="infoDisplayPage">
<!-- page unique stuff -->
</StackLayout>
</Grid>
</Grid>
Like I said, this sort of works, but is incredibly bad code. There are multiple Labels I want to be able to change the value of, but I can't give them the same ID. This means I have to either always set those values twice, or get some set attribute on one of the Labels, automatically setting the other one / have attributes in the code behind with such setter.
I got told that for using bigger area's of code, I could use a ContentView. However, I can't seem to reuse them across two ContentPages.
How do I make it so I can reuse the code of the first two grid rows on both pages, and be able to have different contents of the third grid layout? I'm OK with having to possibly set both Labels using the codebehind as above if that is needed.
I'm quite new to app development, so any extra source of explenation of the code below is appriciated.

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.

Issues with child page's display when using Frames

Problem
This is the layout of my main pane:
<Page
x:Class="Communities.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Communities"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Loaded="Page_Loaded" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
...
<SplitView Grid.Row="1" Name="hamburgerMenu" OpenPaneLength="200" PaneBackground="#F02A2A2A">
<SplitView.Pane>
<ListView ItemsSource="{Binding}" IsItemClickEnabled="True" ItemClick="HamburgerItemClick">
... </ListView>
</SplitView.Pane>
<Frame Name="frame" />
</SplitView>
<Grid Grid.RowSpan="3" Name="popupArea" />
</Grid>
</Page>
the frame is where I load all my pages, so that the layout is always consistant.
Now, in most of my child pages I have defined AppBar control and attached it to the BottomAppBar property of that child page:
PostView.xaml
...
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Label="Back" Icon="Back" Click="TryGoBack" />
<AppBarButton Label="Refresh" Icon="Refresh" Click="TryRefreshComments" />
<AppBarButton Label="Go to Community" Icon="Go" Click="TryOpenCommunity" />
</CommandBar>
</Page.BottomAppBar>
...
Here's where the trouble starts. It works fine on PC, as the layout is mostly static on desktop. There are no software keyboards required most of the time etc. On mobile it's more problematic: Screenshots
My thoughts
It seems like the frame that is used to display the child page is causing all sorts of problems. When the AppBar is defined in the main page it positions correctly.
I'd like to avoid the keyboard covering the textbox as well as the AppBar but I don't want to get rid of the frame control. I'd also prefer it if the page got "squished" when the keyboard shows up, instead of getting pushed upwards, but I'm not sure how to display the keyboard on the frame level, instead of the entire MainPage, default level.
What would be the best way to solve this situation?
Cheers!
As you know, if we set the Page.BottomAppBar in the root of the Page, there is no issue with Touch keyboard. It seems it is the best way to add the Page.BottomAppBar.
If you want to add the Page.BottomAppBar in the other page in the Frame, you should be able to customize your UI. The UWP provides similar behavior on the appearance of the touch keyboard by handling the Showing and Hiding events exposed by the InputPane object.
We can use the InputPaneVisibilityEventArgs.OccludedRect to get the region of the application's window that the input pane is covering.
For example:
public PostView()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += PostView_Showing;
InputPane.GetForCurrentView().Hiding += PostView_Hiding;
}
private void PostView_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
MyTextBox.Margin = new Thickness(0, args.OccludedRect.Height, 0, 0);
}
private void PostView_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
MyTextBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
}

Creating RowDefinitions and ColumnDefinitions in code

I developing application for windows-phone,
I want to create table with 2 rows an 2 columns
I create xaml code for this table
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions/>
</Grid>
I want to create this Grid in code
Grid chat_userpicgrid = new Grid();
newgrid.Children.Add(chat_userpicgrid);
But I don't know how to create RowDefinitions and ColumnDefinitions.
Grid newGrid = new Grid();
newGrid.Background = new SolidColorBrush(Colors.White);
newGrid.ColumnDefinitions.Add(new ColumnDefinition());
newGrid.ColumnDefinitions.Add(new ColumnDefinition());
newGrid.RowDefinitions.Add(new RowDefinition());
newGrid.RowDefinitions.Add(new RowDefinition());
To position elements in specific cells:
Grid chat_userpicgrid = new Grid();
Grid.SetColumn(chat_userpicgrid, 1);
Grid.SetRow(chat_userpicgrid, 1);
newGrid.Children.Add(chat_userpicgrid);
Have a look at the code at the bottom of this MSDN page

silverlight xaml layout issue: grid should be able to grow, but not off the screen

See the following XAML:
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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="400" MinHeight="150">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid x:Name="grid" VerticalScrollBarVisibility="Visible" />
<Button x:Name="button" Grid.Row="1" Content="hello" VerticalAlignment="Top" HorizontalAlignment="Center" Click="button_Click" />
</Grid>
</UserControl>
Corresponding code:
public partial class MainPage : UserControl
{
public class dataclass
{
public string data { get; set; }
}
ObservableCollection<dataclass> list;
public MainPage()
{
InitializeComponent();
grid.ItemsSource = list = new ObservableCollection<dataclass>();
}
private void button_Click(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 5; i++)
list.Add(new dataclass
{
data = "hello" + i
});
}
}
How it works now: The grid takes up the entire screen height minus the height of the button. When too many new items are added, you scan start scrolling. The position of the button never changes, it's always at the bottom of the screen.
What I would like: The grid should take up as little space as possible, so when it's empty, only the header should be visible, and the button immediately below it. When too many items are added, and the button is already at the bottom of the screen, it shouldn't grow any more, but start scrolling instead.
If I swap the two RowDefinition's, then the grid is small at first, but grows indefinitely, pushes the button off the screen and never starts scrolling. How can I do this nicely?
In order to achieve this, nest another Grid in LayoutRoot and then use that nested grid as your main one. Then set both rows to Auto.
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="innerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="itemInTheFirstRow" Grid.Row="0" />
<Button x:Name="itemInTheSecondRow" Grid.Row="1" />
</Grid>
</Grid>
Finally, you need to track the size of the grid and change the sizing rules accordingly. This "pinning" code looks something like.
RowDefinition row = this.innerGrid.RowDefinitions[0];
if (row.Height.GridUnitType == GridUnitType.Auto)
{
if (this.innerGrid.ActualHeight > this.ActualHeight)
{
row.Height = new GridLength(1, GridUnitType.Star);
}
}
else
{
if (this.itemInTheFirstRow.DesiredSize.Height < row.ActualHeight)
{
row.Height = GridLength.Auto;
}
}
In my implementation, I wrap this code in an UpdateRowPinning method that actually uses the dispatcher to call this code. I then call UpdateRowPinning on resize events for the main grid and the inner grid as well as on adding and removing items from the grid and expand/collapse operations of grid groups. This ensures that the second row behaves properly by sitting at the base of the first row until the screen is full and then floating over it after that.
My answer here also covers this issue. I searched for a XAML only solution but it just doesn't seem possible (unless you write some XAML extensions, then you might be able to pull it off with XAML but that's kind of cheating).