Set xaml object height as % of other xaml object - xaml

In a DataTemplate in my UWP app, I want to set the hight of a rectangle as a % of the hight of a image.
My code looks like this.
<DataTemplate
x:Key="FlipViewTemplate">
<Grid>
<Image
x:Name="image"
Margin="20"
Source="{Binding fullImgUrl}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Rectangle
Height="{Binding
Converter={StaticResource PercentageConverter},
ElementName=image,
Path=ActualHeight,
ConverterParameter=0.2}"
Fill="#FFEA1E1E"
VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>
And my converter like this
public class PercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return System.Convert.ToDouble(value) *
System.Convert.ToDouble(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
My problem is that the value in the converter that should be the actualhight of the image is always 0.

You are already using a Grid, so you could simply define two rows. In the example below the Image control span both rows, i.e. the whole Grid, while the Rectangle resides in the second row only, which has 20% of the whole height.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.RowSpan="2" .../>
<Rectangle Grid.Row="1" .../>
</Grid>

Related

How to make FlexLayout's height adjust to its content?

In my Xamarin Forms application, I have a list of strings (minimum 1, maximum 4) that I want to display evenly in a column layout. Each column needs to have the same width and the content should expand so that the whole text is wrapped and visible. I know how I can do it using the Grid control using Width="*" on its columns.
I want to achieve the same result using FlexLayout, so I can bind the list of strings to BindableLayout and easily add and remove columns (I will not be displaying strings but a more complex layout in each column).
Using FlexLayout.Grow and FlexLayout.Basis I can get the FlexLayout to display evenly sized columns. The problem is making the FlexLayout's height fit all the displayed labels. Only the first row of text is displayed.
Both the Grid and FlexLayout are wrapped in a StackLayout:
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="First label" Grid.Column="0" BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label" Grid.Column="1" BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text" Grid.Column="2" BackgroundColor="Aqua"
LineBreakMode="WordWrap"/>
<Label Text="Fourth label" BackgroundColor="Red"
Grid.Column="3" LineBreakMode="WordWrap" />
</Grid>
<BoxView BackgroundColor="Blue"></BoxView>
<FlexLayout AlignItems="Stretch">
<Label Text="First label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
VerticalOptions="FillAndExpand"
LineBreakMode="WordWrap" />
<Label Text="Fourth label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
</FlexLayout>
<BoxView BackgroundColor="Blue"></BoxView>
</StackLayout>
Grid and FlexLayout displayed
I figured out that when setting the HeightRequest of the FlexLayout to a specific number (e.g. 150), everything works as expected - the row has a height of 150 and all the labels stretch out to fit that. So what I need is somehow specify HeightRequest="Auto" so that the row fits all the column's content without being set to a specific value
Is there a way to achieve this?
FlexLayout will cut its child Elements . So in your case use Grid is the best solution .
so I can bind the list of strings to BindableLayout and easily add and remove columns
If you want to display a collection of data I suggest that you could use ListView or CollectionView(if you want to let the collection scroll in Horizontal or display multi Columns in the same row) .
<ContentPage.Resources>
<ResourceDictionary>
<local:WidthConverter x:Key="WidthConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout x:Name="stack">
<CollectionView x:Name="list" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="{Binding Source={x:Reference stack},Path=Width,Converter={StaticResource WidthConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="111111" BackgroundColor="LightBlue" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
in code behind
public class WidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)value;
if(width>0)
{
return width * 0.25;
}
return 100;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return 0;
}
}
For more details you could check https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#horizontal-list

Tooltip position in Oxyplot

I am recently using OxyPlot to draw charts for my WPF project. I have an issue with the tooltip in a line series. The rectangle box of the tooltip is too close to the point my mouse locates. I am wondering if there is any way to modify the position of the tooltip box and make it further from the cursor. Thank a lot. For example, I would like to move the tooltip box (in the red box) to right a bit.
[Edit]
Following Anu's idea, I added the following code but seems like it not works as expected.
In the xaml.cs file, I added
<UserControl.Resources>
<popups:ScreenPointToOffSetScreenPointConverter x:Key="ScreenPointToOffSetScreenPointConverter"/>
</UserControl.Resources>
And then change my OxyPlot to
<ItemsControl ItemsSource="{Binding AmplitudeDtos}" Margin="0" Name="Amplitude" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid popups:GridUtilities.ItemsPerRow="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type dtoModels:SignalDto}">
<oxy:PlotView Height="Auto" Width="Auto" MinHeight="40" MinWidth="100" HorizontalContentAlignment="Stretch"
FontStyle="Normal" FontFamily="Roboto, Microsoft YaHei" FontSize="8" Model="{Binding PlotModel}" Controller="{Binding PlotController}" Padding="8" Margin="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding SignalLoadedCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<oxy:PlotView.DefaultTrackerTemplate>
<ControlTemplate>
<oxy:TrackerControl Position="{Binding Position, Converter={StaticResource ScreenPointToOffSetScreenPointConverter}}" LineExtents="{Binding PlotModel.PlotArea}">
<oxy:TrackerControl.Content>
<TextBlock Text="{Binding}" />
</oxy:TrackerControl.Content>
</oxy:TrackerControl>
</ControlTemplate>
</oxy:PlotView.DefaultTrackerTemplate>
</oxy:PlotView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Please note that my OxyPlot is in a DataTemplate.
ScreenPointToOffSetScreenPointConverter class is the exactly the same as in Anu's example.
A relatively simple fix for the requirement would be to modify DefaultTrackerTemplate and use a Custom Converter to change the Position.For example, You could define a ScreenPoint converter as following
public class ScreenPointToOffSetScreenPointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value is ScreenPoint screenPoint)
{
return new ScreenPoint(screenPoint.X + 50, screenPoint.Y);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// TODO: Implement
throw new NotImplementedException();
}
}
And then in your XAML
<oxy:PlotView Model="{Binding MyModel}">
<oxy:PlotView.DefaultTrackerTemplate>
<ControlTemplate>
<oxy:TrackerControl Position="{Binding Position,Converter={StaticResource ScreenPointToOffSetScreenPointConverter}}" LineExtents="{Binding PlotModel.PlotArea}">
<TextBlock Text="{Binding}" />
</oxy:TrackerControl>
</ControlTemplate>
</oxy:PlotView.DefaultTrackerTemplate>
</oxy:PlotView>
The Converter would add an Offset to the ScreenPoint position for the Tracker. You could modify the Offset Value to the Converter if it makes sense for your application requirement.

How to change color of listview rows based on bindig value in xamarin form

I have a listview in which i have data, in listview i have
1 - taskname
2- date
3 - completed percentage
4 - Assigned task by
I want to put condition in list and based on condition want to change color of list row, for example if completed percentage =0, color of that row should be white if percentage is greater then 0 color should be red etc etc.
check image down below:
in listview i have 0%, 7% and 100 %, so i want if its 0 row color should be white, if its more then 0 row color should b red and if its = 100 row color should be green here is my xaml code
<ListView HeightRequest="20" HasUnevenRows="True" SeparatorColor="Red" ItemsSource="{Binding GetAssignedTask}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="rsz_pnglogocom.png" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding strTaskName}" TextColor="Black" FontSize="Medium" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding intCurrCompletePercentage , StringFormat='{0}% Completed '}" Margin="0,0,10,0" TextColor="Black" />
<Label Grid.Row="1" Grid.Column="2" Margin="0,0,20,0" Text="{Binding dtStart,StringFormat='{0:MMMM dd, yyyy}'}" TextColor="Red" HorizontalOptions="Center"/>
<Label Grid.Row="1" Grid.Column="3" Text="{Binding strAssignedByEmpName}" TextColor="Black" HorizontalOptions="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Two ways to do this.
1. Using Converters:
Create a Value converter:
public class BackgroundColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
if(value!=null)
{
if((int)value ==0)
return Color.White;
else if((int)value > 0)
return Color.Red;
else
return Color.Green;
}
}
catch (Exception ex)
{
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
xaml part:
xmlns:converter="RefrenceToConverterNameSpace"
<ContentPage.Resources>
<ResourceDictionary>
<converter:BackgroundColorConverter x:Key="BackgroundColorConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid BackgroundColor="{Binding intCurrCompletePercentage,Converter={StaticResource BackgroundColorConverter}}" />
Method 2:
In your object which containts intCurrCompletePercentage
You can also have another property Color property PercentColor
public Color PercentColor
{
get
{
if(intCurrCompletePercentage ==0)
return Color.White;
else if(intCurrCompletePercentage > 0)
return Color.Green;
else
return Color.Red;
}
}
<Grid BackgroundColor="{Binding PercentColor}" />
Whenever your intCurrCompletePercentage changes dont forget to call the propertychanged event for PercentColor.
You could also have a look at DataTemplateSelector for this case.
A look in the documentation https://learn.microsoft.com/de-de/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector
Code from documentation
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
XAML
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
ValidTemplate="{StaticResource validPersonTemplate}"
InvalidTemplate="{StaticResource invalidPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView x:Name="listView" ItemTemplate="{StaticResource personDataTemplateSelector}" />
You would have to make 3 templates (ZeroTemplate, AboveZeroTemplate, HundredTemplate) and let your DataTemplateSelector decide which template it should take.
I posted just the documentation code, because I think you always should do something on yourself to also learn something, and not just copy paste.. :)

How to bind to a list of images

I have changed my code to this:
the view:
<phone:Panorama.ItemTemplate>
<DataTemplate>
<ScrollViewer Width="800" HorizontalContentAlignment="Left" Margin="0,50,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="list_of_images" ItemsSource="{Binding ImagesUrls}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Width="300" Height="300" Source="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
<TextBlock Text="{Binding Title}"
Grid.Row="1"
Loaded="TextBlock_Loaded_1"
Margin="50,0,0,0"
FontSize="23"
TextWrapping="Wrap"
Width="360"
HorizontalAlignment="Left"
Foreground="Black"/>
<TextBox Text="{Binding ContactEmail}"
Grid.Row="2"
BorderBrush="Black"
Width="340"
HorizontalAlignment="Left"
BorderThickness="1"
Margin="40,0,0,0"
Foreground="Black" />
<TextBlock Text="{Binding Body}"
Grid.Row="3"
TextWrapping="Wrap"
Foreground="Black"
Margin="50,5,0,0"
Width="360"
HorizontalAlignment="Left"
FontSize="20" />
</Grid>
and I build a new object with different properties, with one of the properties being a list of strings which represents the imageurls, but I cannot get the images to show?
I have attached screenshots, what in my xaml must I change so that I can display the images, cause at the moment it doesn't show any images but it shows all the other details
code for populating collection:
ObservableCollection<ClassifiedAds> klasifiseerd_source = new ObservableCollection<ClassifiedAds>();
ImagesClassifieds new_Classifieds = new ImagesClassifieds();
ObservableCollection<string> gallery_images = new ObservableCollection<string>();
new_Classifieds.Title = klasifiseerd_source[0].Title;
new_Classifieds.ContactEmail = klasifiseerd_source[0].ContactEmail;
new_Classifieds.Body = klasifiseerd_source[0].Body;
foreach (var item in klasifiseerd_source[0].Gallery.Images)
{
var deserialized = JsonConvert.DeserializeObject<GalleryImages>(item.ToString());
gallery_images.Add(deserialized.ImageUrl);
//new_Classifieds.ImageUrls.Add(deserialized.ImageUrl);
}
new_Classifieds.ImageUrls = gallery_images;
// classifiedPanorama.ItemsSource = new_list;
new_Classifieds_list.Add(new_Classifieds);
classifiedPanorama.ItemsSource = new_Classifieds_list;
public class ImagesClassifieds
{
public string Title { get; set; }
public string ContactEmail { get; set; }
public string Body { get; set; }
public ObservableCollection<string> ImageUrls { get; set; }
}
here is the imageurl format, this works (in another par tof my app I simply bind to 1 image in this format and it works perfectly)
Depending on whether you want to just display a list of images or if you also want to be able to select them, you may either choose an ItemsControl or a ListBox. In both case you have to define a DataTemplate that controls how each item is displayed.
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then you should think about how you define your item class. Just in case you want to be able to dynamically add or remove images from the list and let the UI be automatically updated, you should use an ObservableCollection as container type. As there is a built-in type conversion from string to ImageSource (the type of the Image control's Source property), you may simply use an ObservableCollection<string>.
public class Gal
{
public ObservableCollection<string> Images { get; set; }
}
You may create an instance of that class like so:
var gallery = new Gal();
gallery.Images = new ObservableCollection<string>(
Directory.EnumerateFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"));
Now you may directly bind to that instance by simply setting the DataContext of your Window (or wherever the image list should be shown) to this instance:
DataContext = gallery;
Note the binding declaration in the ItemsControl or ListBox above was {Binding Images}. If you really have to have a property Gallery (which I assume is in MainWindow) and bind like {Binding Gallery.Images} you may set the DataContext like this:
DataContext = this;
so I basically created a loaded event on the listbox and added this code
private void list_of_images_Loaded_1(object sender, RoutedEventArgs e)
{
ListBox lst = (ListBox)sender;
lst.ItemsSource = new_Classifieds_list[0].ImageUrls;
}
which binds the itemssource of the listbox to the list of imageurls. since I cannot access the listbox from codebehind due to it being inside the panorama's itemstemplate

Xaml alternative to grid layout

I've been using grids to hold my controls for a new app. Such as;
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Label1:" />
<ListBox Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="1" Grid.Column="0" Content="Label2:" />
<ComboBox Grid.Row="1" Grid.Column="1" />
<Label Grid.Row="2" Grid.Column="0" Content="Label3:" />
<TextBox Grid.Row="2" Grid.Column="1" />
</Grid>
This works fine however I've now got a situation where I only want to show my third row based upon the selectedvalue from the combobox in the second row.
With a grid this seems a bit messy too set the visibilty of the entire row to be collapsed. I think I'd have to do it by setting the hight of the contents of the row to zero.
Is there a more flexible layout that the grid. I thought about the stackpannel but wasn't sure about having multiple columns and keeping the rows in synch.
This is probably a really simple question but I'm interested to get input from other people before I do anything.
I wouldn't recommend setting the height of controls to zero - for one thing, it would still be possible to tab to a 0-height control which would be confusing for users to say the least :)
As an alternative, try binding the visibility of any affected controls to the combobox selection, e.g:
<UserControl xmlns:cnv="clr-namespace:your_namespace_here">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Label1:" />
<ListBox Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="1" Grid.Column="0" Content="Label2:" />
<ComboBox Name="cbo" Grid.Row="1" Grid.Column="1" />
<Label Grid.Row="2" Grid.Column="0" Content="Label3:"
Visibility="{Binding ElementName=cbo, Path=SelectedIndex,
Converter={cnv:IntToVisibilityConverter}}" />
<TextBox Grid.Row="2" Grid.Column="1" />
</Grid>
In code, put together a converter which returns the relevant Visibility type:
namespace your_namespace_here
{
public class IntToVisibilityConverter : MarkupExtension, IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _value = (int)value;
return (_value > 0) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
Note that in this example, the converter will return Visiblity.Collapsed if the first item in the combo is selected, otherwise Visiblity.Visible.
Untested code, but the method is sound. Hope this is of some use!