I'm using the WebView and building my own html. I want to bind the webview to changes elsewhere on the form. Here is my xaml:
<WebView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Margin="0">
<WebView.Source>
<HtmlWebViewSource x:Name="WebViewSoruce1" Html="{Binding Description}"/>
</WebView.Source>
</WebView>
Here is my model code for the Description:
public string Description
{
get {
return _description;
}
set
{
_description = value;
RaisePropertyChanged();
}
}
This works fine on Android but not for iOS. Any suggestions would be greatly appreciated.
So if anyone else runs into this problem. This is what finally worked for me on both iOS and Android.
I had to bind as a WebViewSource to its Source attribute and not to the HTML. Here is my XAML:
<WebView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Margin="0" Source="{Binding WebViewSource}"/>
In my view model I have two properites. One to bind the HTML changes, I named it as Description. The other was to bind to the WebViewSource.
Here is the code-behind:
public HtmlWebViewSource WebViewSource
{
get
{
return new HtmlWebViewSource { Html = _description };
}
}
public string Description
{
get {
return _description;
}
set
{
_description = value;
RaisePropertyChanged();
RaisePropertyChanged("WebViewSource");
}
}
This worked for me.
Related
I'm trying to create custom tabs control in .Net MAUI, for that, I had first tried it with ScrollView and BindableStackLayout control but in that, I'm facing a problem.
Reported here Custom tabs with ScrollView bug
So, as an alternative approach or work-around, I have tried to develop the same Tabs control using CollectionView.
This alternative approach is working fine in iOS but not working properly in Android.
There is one problem that is common in both Android and iOS. I have taken BoxView control as an Indicator for the selected tab. That I'm going to show only for the Selected tab but this just shows in the first tab, when I click on other tabs the tabs get changed but it does not get hidden from the first tab and get visible in the other selected tab.
I have used the visual state manager with white color for the selected state because it gives looks like an indicator which I',m trying to create using BoxView. But this also shows Selected item for Android only when that view gets loads for iOS I have to select the tab first then only it shows the selected color there.
Here is what I have done:
MainPage.xaml
<ContentPage.Content>
<Grid RowDefinitions="50, *" RowSpacing="0">
<CollectionView x:Name="TabsView"
Grid.Row="0"
ItemsSource="{Binding Tabs,Mode=TwoWay}"
ItemsUpdatingScrollMode="KeepItemsInView"
ItemSizingStrategy="MeasureAllItems"
SelectedItem="{Binding SelectedTab}"
SelectionChangedCommand="{Binding Path=BindingContext.TabChangedCommand,Source={x:Reference TabsView}}"
SelectionChangedCommandParameter="{Binding SelectedTab}"
SelectionMode="Single">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0" RowDefinitions="*, 3">
<Label Grid.Row="0"
Text="{Binding TabTitle}"
TextColor="White"
BackgroundColor="navy"
Padding="20,0"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="12" />
<BoxView Grid.Row="1" Color="{Binding BoxColor}"
IsVisible="{Binding IsSelected}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"/>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<tabs:ParentRecordTabView Grid.Row="1"
IsVisible="{Binding IsParentRecordTabVisible}"
VerticalOptions="FillAndExpand"/>
<tabs:AdditionalInfoTabView Grid.Row="1"
IsVisible="{Binding IsAdditionalInfoTabVisible}"
VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
MainPageViewModel.cs
public class MainPageViewModel : BaseViewModel
{
public MainPageViewModel()
{
GetTabs();
}
private bool _isParentRecordTabVisible = true;
private bool _isAdditionalInfoTabVisible;
private ObservableCollection<TabViewModel> _tabs { get; set; }
private TabViewModel _selectedTab { get; set; }
public bool IsParentRecordTabVisible
{
get => _isParentRecordTabVisible;
set { _isParentRecordTabVisible = value; OnPropertyChanged(nameof(IsParentRecordTabVisible)); }
}
public bool IsAdditionalInfoTabVisible
{
get => _isAdditionalInfoTabVisible;
set { _isAdditionalInfoTabVisible = value; OnPropertyChanged(nameof(IsAdditionalInfoTabVisible)); }
}
public ObservableCollection<TabViewModel> Tabs
{
get => _tabs;
set { _tabs = value; OnPropertyChanged(nameof(Tabs)); }
}
public TabViewModel SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
OnPropertyChanged(nameof(SelectedTab));
}
}
public ICommand TabChangedCommand { get { return new Command<TabViewModel>(ChangeTabClick); } }
private void GetTabs()
{
Tabs = new ObservableCollection<TabViewModel>();
Tabs.Add(new TabViewModel { TabId = 1, IsSelected = true, TabTitle = "Parent record" });
Tabs.Add(new TabViewModel { TabId = 2, TabTitle = "Additional Info" });
Tabs.Add(new TabViewModel { TabId = 3, TabTitle = "Contacts" });
Tabs.Add(new TabViewModel { TabId = 4, TabTitle = "Previous inspections" });
Tabs.Add(new TabViewModel { TabId = 5, TabTitle = "Attachments" });
SelectedTab = Tabs.FirstOrDefault();
}
public void ChangeTabClick(TabViewModel tab)
{
Tabs.All((arg) =>
{
if (arg.TabId == tab.TabId)
{
arg.IsSelected = true;
}
else
{
arg.IsSelected = false;
}
return true;
});
SelectedTab = Tabs.Where(t => t.IsSelected == true).FirstOrDefault();
switch (SelectedTab.TabId)
{
case 1:
IsParentRecordTabVisible = true;
IsAdditionalInfoTabVisible = false;
break;
case 2:
IsParentRecordTabVisible = false;
IsAdditionalInfoTabVisible = true;
break;
}
}
}
TabViewModel.cs
public class TabViewModel : BaseViewModel
{
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
private int _TabId;
public int TabId
{
get { return _TabId; }
set
{
_TabId = value;
OnPropertyChanged(nameof(TabId));
}
}
private string _TabTitle;
public string TabTitle
{
get { return _TabTitle; }
set
{
_TabTitle = value;
OnPropertyChanged(nameof(TabTitle));
}
}
}
Note: This same approach again works fine in Xamarin.Forms (Visual Studio 2019), this just not working in MAUI, so does anyone notice something like this?
How to Reproduce error: check github
Remove the BoxView default style in your project. Resource> Styles> Styles.xml
<Style TargetType="BoxView">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
You could use the IsVisible property to show the BoxView or not instead of binding color with BoxColor. Remove the SelectionChangedCommand, SelectionChangedCommandParameter and VisualStateManager.VisualStateGroups in CollectionView as well.
<BoxView Grid.Row="1"
Color="Yellow"
IsVisible="{Binding IsSelected}"/>
Set the SelectedTab property like below.
public TabViewModel SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
SetSelection();
OnPropertyChanged(nameof(SelectedTab));
}
}
private void SetSelection()
{
foreach (var item in Tabs)
{
item.IsSelected = false;
}
SelectedTab.IsSelected = true;
}
In my Xamarin Forms 5 app, I have a form users will fill out to create a post -- similar to Facebook posts.
The effect I'm trying to create is this:
There's an "Add Image" button that allows user to upload an image. Once the image is uploaded, I want to no longer display the button but display a thumbnail version of the uploaded image.
Here's what my XAML looks like:
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState.None}"
xct:StateLayout.CurrentCustomStateKey="{Binding PostImageState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Custom" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{Binding PostImageUrl}"
WidthRequest="30"
HeightRequest="30"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
BackgroundColor="{StaticResource SecondaryBackground}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0"/>
</StackLayout>
Here's an abbreviated version of my view model:
public class MyViewModel : BaseViewModel
{
public LayoutState _mainState;
string postImageUrl { get; set; }
string postImageState { get; set; } = "No image";
public MyViewModel()
{
Title = string.Empty;
IsBusy = true;
MainState = LayoutState.None;
AddImageCommand = new AsyncCommand(Add_Image_Tapped);
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set
{
if (postImageUrl == value)
return;
postImageUrl = value;
OnPropertyChanged();
}
}
public string PostImageState
{
get => postImageState;
set
{
if (postImageState == value)
return;
postImageState = value;
OnPropertyChanged();
}
}
async Task Add_Image_Tapped()
{
// Upload image
// Once upload is done
PostImageUrl = uploadedFileUrl;
PostImageState = "Image set";
}
}
I haven't been able to get this to work. Currently, it's not even showing the "Add Image" button. Where am I making a mistake?
There are several problems with your code.
1.Since you use Binding for xct:StateLayout.CurrentState, we should bind it to a variable in ViewModel, here we should use MainState not MainState.None:
xct:StateLayout.CurrentState="{Binding MainState}"
2.Based on your requirement, we can use the value from LayoutState enumeration(for example StateKey="Success"),, we don't need add custom states.
3.If we want to hidden the button once uploading the image, we can bind MainState to property IsVisible of button , but need use Converter StateToBooleanConverter to convert State to bool.
Based on your code ,I created a simple demo, and it works properly on my side.
You can refer to the following code:
MyPage.Xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:viewmodel="clr-namespace:FormApp0314.ViewModel"
x:Class="FormApp0314.TestPage1">
<ContentPage.BindingContext>
<viewmodel:MyViewModel></viewmodel:MyViewModel>
</ContentPage.BindingContext>
<ContentPage.Resources>
<xct:StateToBooleanConverter x:Key="StateToBooleanConverter" />
</ContentPage.Resources>
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Success" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{ Binding PostImageUrl}"
WidthRequest="60"
HeightRequest="60"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
IsVisible="{Binding MainState, Converter={StaticResource StateToBooleanConverter}, ConverterParameter={x:Static xct:LayoutState.None}}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0" />
</StackLayout>
</ContentPage>
MyViewModel.cs
public class MyViewModel: BaseViewModel
{
public LayoutState _mainState;
string postImageUrl;
string postImageState = "No image";
public ICommand AddImageCommand { get; }
public MyViewModel()
{
MainState = LayoutState.None;
PostImageUrl = "bell.png";
AddImageCommand = CommandFactory.Create(Add_Image_Tapped);
}
async Task Add_Image_Tapped()
{
MainState = LayoutState.Success;
await Task.Delay(3000);
MainState = LayoutState.None;
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set => SetProperty(ref postImageUrl, value);
}
public string PostImageState
{
get => postImageState;
set => SetProperty(ref postImageState, value);
}
}
I'm new to Xamarin framework and want to create an app using Master-Detail Page
I did simple Master-Detail Navigation page demo from xamarin websit
master-detail-page xamarin webise
only difference is I used ViewCell inside DataTemplate.In ViewCell I have Label
instead of Image.
after clicking on MasterPageItems navigation is working fine but now I want to change the label Text color also .
<ListView x:Name="listView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Title}" TextColor="#1ca7ec" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(ContactsPage)));
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
I think you can do in this way:
1- in your model you should have a "TextColor" property and a "Selected" property
public bool Selected { get; set; }
// I think you should not return "Color" type (for strong MVVM) but, for example, a value that you can convert in XAML with a IValueConverter...
public Color TextColor
{
get
{
if (Selected)
return Color.Black;
else
return Color.Green;
}
}
2- In your XAML you should have something like
<ListView SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}" TextColor="{Binding TextColor}" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
3- and in your ViewModel something like
MyModel _selectedItem { get; set; }
public ObservableCollection<MyModel> List { get; set; } = new ObservableCollection<MyModel>();
public MyModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != null)
_selectedItem.Selected = false;
_selectedItem = value;
if (_selectedItem != null)
_selectedItem.Selected = true;
}
}
When your item in the list is selected , SelectedItem property change and Selected property in your model became True or False, changing the TextColor property (I use PropertyChanged.Fody for INPC).
Hope this help
You can find the repo on GitHub
Instead of use a TextColor Property in your Model, I think you can also use only Selected property and an IValueConverter that convert Selected property to a color
I am trying to use a CarouselPage in Xamarin Forms.
<?xml version="1.0" encoding="utf-8" ?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:views="clr-namespace:TestForms.Views;assembly=TestForms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="TestForms.Views.Photos" ItemsSource="{Binding Pages}">
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage >
<StackLayout VerticalOptions="StartAndExpand" Padding="50">
<Label Text="ContentPage"></Label>
<Label Text="{Binding Title}"></Label>
<Label Text="{Binding Description}"></Label>
</StackLayout>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
</CarouselPage>
In the view model I have
List<ContentPage> ContentPages = new List<ContentPage>();
foreach (var photo in Photos)
{
var page = new ContentPage();
page.BindingContext = new PhotoDetailViewModel(photo);
ContentPages.Add(page);
}
Pages = new ObservableCollection<ContentPage>(ContentPages);
When I render this, I get a list of pages for all the photos. but I can't seem to bind the title or description in the individual page.
What am I missing here?
You have your CarouselPage wired up correctly
Just need to change your view model slightly.
I'm assuming your Title and Description Properties are in your PhotoDetailViewModel?
if so the binding you are creating in your CarouselPage is not working because it is binded to the List of ContentPage, which wouldn't have the properties "Title" and "Description"
in your CarouselPage your have already set up an ItemsSource binding which should automatically set the BindingContext of your child pages in your CarouselPage. So you dont need to do it manually.
So instead create an ObservableCollection of PhotoDetailViewModel in your ViewModel and bind the ItemsSource of your CarouselPage to that.
So Remove:
List<ContentPage> ContentPages = new List<ContentPage>();
foreach (var photo in Photos)
{
var page = new ContentPage();
page.BindingContext = new PhotoDetailViewModel(photo);
ContentPages.Add(page);
}
Pages = new ObservableCollection<ContentPage>(ContentPages);
And add:
Pages = new ObservableCollection<PhotoDetailViewModel>(Photos.Select(p => new PhotoDetailViewModel(p));
Make sure to change the Property Type of Pages to ObservableCollection<PhotoDetailViewModel>
And that should be all you need to change
As I said you should use ContentView instead of ContentPage. Below is working example
public partial class AnotherCarouselPage : ContentPage
{
public class Zoo
{
public string ImageUrl { get; set; }
public string Name { get; set; }
}
public ObservableCollection<Zoo> Zoos { get; set; }
public AnotherCarouselPage()
{
Zoos = new ObservableCollection<Zoo>
{
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/23c1dd13-333a-459e-9e23-c3784e7cb434/2016-06-02_1049.png",
Name = "Woodland Park Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/6b60d27e-c1ec-4fe6-bebe-7386d545bb62/2016-06-02_1051.png",
Name = "Cleveland Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/e8179889-8189-4acb-bac5-812611199a03/2016-06-02_1053.png",
Name = "Phoenix Zoo"
}
};
InitializeComponent();
carousel.ItemsSource = Zoos;
carousel.PositionSelected += Carousel_PositionSelected;
carousel.ItemSelected += Carousel_ItemSelected;
}
private void Carousel_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
}
private void Carousel_PositionSelected(object sender, SelectedPositionChangedEventArgs e)
{
}
}
page xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:control="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
x:Class="ButtonRendererDemo.AnotherCarouselPage"
x:Name="devicePage"
BackgroundColor="Gray">
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<control:CarouselView x:Name="carousel" >
<control:CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</DataTemplate>
</control:CarouselView.ItemTemplate>
</control:CarouselView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I have List box in which I am displaying values at runtime. But i want to show an image in list box only when i selected an item from list box.Currently I am using DataTemplate and ItemTemplate in list box for displaying values at runtime (In short data binding).Thanks
One solution is to add a dependency property IsSelected to your item view model, and toggle that when you tap on an item. This would mean that you can select multiple items, i.e. show images from several rows at once.
using some xaml like this:
<phone:PhoneApplicationPage.Resources>
<convert:BooleanToVisibilityConverter x:Key="booltovisibility" />
</phone:PhoneApplicationPage.Resources>
<phone:PhoneApplicationPage.DataContext>
<vm:MainViewModel/>
</phone:PhoneApplicationPage.DataContext>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="Red">
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Green">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ToggleSelected}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Grid.Row="0" Text="{Binding Text}"/>
<Image Grid.Row="1" Source="{Binding Image}" Visibility="{Binding IsSelected, Converter={StaticResource booltovisibility}}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
Note that this xaml uses the System.Windows.Interactivity dll from the Silverlight SDK. It also uses a custom BooleanToVisibilityConverter, which is a IValueConverter that converts a boolean to a Visibility enum value.
Further more, we can define an ItemViewModel like the one below, and have an "Items" property in the MainViewModel
private readonly ObservableCollection<ItemViewModel> items;
public ObservableCollection<ItemViewModel> Items { get { return items; } }
which is populated from code.
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Test.Commands;
namespace Test.ViewModels {
public class ItemViewModel : DependencyObject {
private ICommand toggleCommand;
public ItemViewModel(string title) {
Text = title;
Image = new BitmapImage(new Uri("graphics/someimage.png", UriKind.Relative));
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ItemViewModel), new PropertyMetadata(default(string)));
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(ItemViewModel), new PropertyMetadata(default(bool)));
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ItemViewModel), new PropertyMetadata(default(ImageSource)));
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsSelected {
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public ImageSource Image {
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public ICommand ToggleSelected {
get { return toggleCommand ?? (toggleCommand = new RelayCommand(o => IsSelected = !IsSelected)); }
}
}
}
where RelayCommand is similar to the one found at WPF Apps With The Model-View-ViewModel Design Pattern article. The main difference is that CommandManager is not available in the Windows Phone SDK.
By using this model you can tap on rows to select them, using the ToggleSelected command in the view model, and deselect them by tapping them again, in effect showing or hiding the image.