On my main page I have an ActivityIndicator running when IsBusy is true, this works fine, but when I use the same configuration on another page it does not run.
IsBusy resides in my BaseViewModel which is then initiated in the ViewModelBase, my code as follows...
public class BaseViewModel : BaseSession
{
//Some other properties .....
public bool IsConnected
{
get { return _IsConnected; }
set { SetProperty(ref _IsConnected, value); }
}
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
}
ViewModelBase...
public class ViewModelBase : BaseViewModel
{
//Lots of other Properties
}
page properties...
class BaseNetWork : ViewModelBase
{
public BaseNetWork(Grid grid, Image img, Label lbl)
{
BaseImages Images = new BaseImages();
NetworkShares NetWorkData = new NetworkShares();
img.GestureRecognizers.Add((new TapGestureRecognizer((view) => OpenShares())));
void OpenShares()
{
if (IsConnected)
{
grid.Children.Clear();
img.Source = Images.GetImages(4);
lbl.Text = "Connect";
IsConnected = false;
} else {
IsBusy = true; // Set the IsBusy to true for the ActivityIndicator.
NetWorkData.DeploySharesToGrid(grid, null, ConnectToShares);
IsConnected = true;
if (IsConnected)
{
img.Source = Images.GetImages(3);
lbl.Text = "Disconnect";
};
IsBusy = false; // Set the IsBusy back to false.
}
}
}
}
View.xaml.cs...
public partial class FolderView : ContentPage
{
BaseImages Images = new BaseImages();
BaseNetWork viewModel;
public FolderView()
{
InitializeComponent();
BindingContext = viewModel = new BaseNetWork(GridFolders, btnConnect, lblNet);
}
void CloseEntries(object sender, EventArgs e)
{
if (viewModel.IsConnected)
{
GridFolders.Children.Clear();
btnConnect.Source = Images.GetImages(4);
lblNet.Text = "Connect";
viewModel.IsConnected = false;
}
}
}
View.xaml...
<ScrollView>
<StackLayout Padding="{StaticResource PagePadding}" Spacing="{StaticResource PageSpacing}">
<Grid RowDefinitions="{StaticResource BrowseShareRows}" ColumnDefinitions="{StaticResource BrowseShareColumns}">
<Label x:Name="lblNet" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalTextAlignment="Start" Text="Connect"/>
<Image x:Name="btnConnect" Grid.Row="1" Grid.Column="0" HorizontalOptions="Start" Grid.ColumnSpan="2" Source="folder_darkblue.png"></Image>
<StackLayout Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2">
<controls:Checkbox x:Name="cbHello" Text="{Binding ViewMessage}" Checked="{Binding ConnectToShares}" PropertyChanged="CloseEntries"/>
</StackLayout>
<ScrollView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5">
<StackLayout>
<Grid x:Name="GridFolders" Style="{StaticResource FolderStyle}" IsVisible="{Binding AllowViewGrid}"></Grid>
</StackLayout>
</ScrollView>
<ActivityIndicator Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="5" Color="red" IsRunning="{Binding IsBusy}"/>
</Grid>
</StackLayout>
</ScrollView>
viewthatworks.xaml.cs
public MainPage()
{
InitializeComponent();
//ConnectivityTest();
BindingContext = new ViewModelBase();
}
viewthatworks.xaml
<ScrollView>
<StackLayout Padding="{StaticResource PagePadding}" VerticalOptions="CenterAndExpand" Spacing="{StaticResource PageSpacing}">
<Grid x:Name="controlGrid" RowDefinitions="{StaticResource MainRowStyle}" ColumnDefinitions="{StaticResource MainColumnStyle}" Style="{StaticResource MainGridStyle}">
<Grid RowDefinitions="{StaticResource GridRowLayout3}" ColumnDefinitions="{StaticResource GridColumnLayout3}" >
<Entry x:Name="txtLocation" Text="{Binding GetFolder}"
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="1"
Style="{StaticResource MainEntryStyle}"
Placeholder="Name"/>
<Button x:Name="btnSave" Text="Save Folder"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1"
Style="{StaticResource MainButtonStyle}"
IsEnabled="{Binding IsNotBusy}"
Command="{Binding SaveContactCommand}"/>
<Button x:Name="btnCreate" Text="Create"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="1"
Style="{StaticResource MainButtonStyle}"
IsEnabled="{Binding IsNotBusy}"
Command="{Binding CreateDir}"/>
<Button x:Name="btnGrid" Text="App"
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="1"
Style="{StaticResource MainButtonStyle}"
Clicked="Get_Grid"/>
</Grid>
</StackLayout>
<ActivityIndicator IsRunning="{Binding IsBusy}" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="6" Color="red"/>
</Grid>
</StackLayout>
</ScrollView>
Can anyone please say why the ActivityIndicator is not running?
Sorry for the misunderstanding, I didn't place the question as answered, I think maybe I didn't press save after updating some code doh :-) because the object is updated and the page is getting the updated property
Related
On initial load of the page, everything is fine and displays all data correctly.
But when I update the page (either by switching page and switching back, or Pull to refresh) I get null reference exception. I don't know how to debug this or how to fix it.
About my page:
My page displays a list of Displays. Each Display has a list of videos and a list of images.
XAML (DisplaysPage.xaml):
<StackLayout>
<Button Text="+ Create new display" Command="{Binding UploadDisplayCommand}" CornerRadius="0" BackgroundColor="ForestGreen" FontAttributes="Bold" />
<Label Text="No displays found in database." TextColor="White" FontSize="20" HorizontalTextAlignment="Center" IsVisible="{Binding IsEmpty}" Padding="20" />
<RefreshView IsRefreshing="{Binding IsRefreshing}" RefreshColor="Cyan" Command="{Binding LoadDisplaysCommand}" Margin="0, 5, 0, 1">
<CollectionView ItemsSource="{Binding Displays}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Display">
<Grid Padding="5">
<Frame CornerRadius="5" Padding="10">
<StackLayout Spacing="10">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" FontAttributes="Bold" FontSize="23" TextColor="White" />
<StackLayout Padding="0, -10, 0, -10" HorizontalOptions="EndAndExpand">
<Switch IsToggled="{Binding IsOn}" Toggled="Switch_Toggled" OnColor="LightGreen" ThumbColor="White" />
</StackLayout>
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding Videos}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Video">
<Grid Padding="0, 5">
<Frame CornerRadius="5" Padding="0" BackgroundColor="AliceBlue">
<Image Source="{Binding ThumbnailPath}" Aspect="AspectFill" />
</Frame>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding Images}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Video">
<Grid Padding="0, 5">
<Frame CornerRadius="5" Padding="0" BackgroundColor="AliceBlue">
<Image Source="{Binding FilePath}" Aspect="AspectFill" />
</Frame>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<Label Text="{Binding Description}" FontSize="16" TextColor="White" />
<StackLayout Orientation="Horizontal">
<Label Text="Number of videos associated with the display: " FontSize="16" TextColor="White" />
<Label Text="{Binding Videos.Count}" FontSize="16" TextColor="White" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Number of images associated with the display: " FontSize="16" TextColor="White" />
<Label Text="{Binding Images.Count}" FontSize="16" TextColor="White" />
</StackLayout>
</StackLayout>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</StackLayout>
ViewModel (DisplaysViewModel.cs)
[INotifyPropertyChanged]
public partial class DisplaysViewModel
{
private readonly Service<Display> displayService;
[ObservableProperty]
private ObservableCollection<Display> displays = new();
[ObservableProperty]
private bool isEmpty;
[ObservableProperty]
private bool isRefreshing;
public DisplaysViewModel(Service<Display> displayService)
{
this.displayService = displayService;
}
[ICommand]
internal async Task LoadDisplaysAsync()
{
#if ANDROID || IOS || tvOS || Tizen
UserDialogs.Instance.ShowLoading("Loading displays from the database...");
#endif
if (Displays.Count is not 0)
{
Displays.Clear();
}
try
{
await foreach (Display display in displayService.GetAllAsync().OrderBy(x => x.Id))
{
Displays.Add(display);
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
if (Displays.Count is 0)
{
IsEmpty = true;
}
else
{
IsEmpty = false;
}
IsRefreshing = false;
#if ANDROID || IOS || tvOS
UserDialogs.Instance.HideLoading();
#endif
}
}
[ICommand]
private async Task UploadDisplayAsync()
{
await Shell.Current.DisplayAlert("Create a new display", "Under construction!", "OK");
}
}
Edit:
From inspiration from Jason, I wrapped my code with try-catch and debugged it, and it showed the issue. It was in my code-behind DisplaysPage.xaml.cs and the null reference is on my call to GetByIdAsync method inside Switch_Toggled event:
public partial class DisplaysPage : ContentPage
{
private readonly Service<Display> displayService;
private readonly Service<Log> logService;
public DisplaysPage(DisplaysViewModel displaysViewModel, Service<Display> displayService, Service<Log> logService)
{
InitializeComponent();
this.displayService = displayService;
this.logService = logService;
BindingContext = displaysViewModel;
}
protected override async void OnAppearing()
{
await ((DisplaysViewModel)BindingContext).LoadDisplaysAsync();
}
private async void Switch_Toggled(object sender, ToggledEventArgs e)
{
Switch displaySwitch = sender as Switch;
Display selectedDisplay = displaySwitch.BindingContext as Display;
try
{
Display displayToUpdate = await displayService.GetByIdAsync(selectedDisplay.Id);
displayToUpdate.IsOn = displaySwitch.IsToggled;
string isOnString = displaySwitch.IsToggled ? "on" : "off";
await displayService.UpdateAsync(displayToUpdate);
await logService.CreateAsync(new Log()
{
Description = $"{JwtParser.ParseClaimsFromJwt(await SecureStorage.GetAsync("JWT")).Where(x => x.Type == ClaimTypes.Name).FirstOrDefault()?.Value} has {isOnString} the display.",
UserId = JwtParser.ParseClaimsFromJwt(await SecureStorage.GetAsync("JWT")).Where(x => x.Type == ClaimTypes.NameIdentifier).FirstOrDefault()?.Value
});
}
catch (Exception)
{
throw new Exception("null exception");
}
}
}
This sort of exception as shown in the post was found by wrapping code with try-catch and doing a null-check.
I am trying to create a custom Action Sheet using CollectionView, but I have a problem with auto resizing the height. When I insert the CollectionView into the page, it takes up all the free space. What needs to be done so that the height of the CollectionView changes depending on the height of its elements?
<?xml version="1.0" encoding="utf-8"?>
<StackLayout
Margin="60, 0">
<CollectionView
BackgroundColor="White"
x:Name="CollectionViewControl">
<CollectionView.Header>
<Label
x:Name="HeaderLabel"
TextTransform="Uppercase"
HorizontalTextAlignment="Center"
FontAttributes="Bold" />
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0, 10" x:DataType="modalDialogs:ActionSheetItem">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" />
<Image Grid.Column="1" Source="arrow_down" IsVisible="{Binding IsSelected}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Button
BackgroundColor="Transparent"
TextColor="Green"
Text="Cancel"
HorizontalOptions="End" />
</CollectionView.Footer>
</CollectionView>
</StackLayout>
Current UI
When I insert the CollectionView into the page, it takes up all the free space. What needs to be done so that the height of the CollectionView changes depending on the height of its elements?
From Jason's opinion, little rows in Collectionview, you can set a value to collectionView.HeightRequest. when item increase, the height will increase as well.
I create a property called.RowHeigth. If I add item in the CollectionView(with AddCommand), RowHeigth will increase.
<StackLayout Margin="60,0">
<CollectionView
x:Name="CollectionViewControl"
BackgroundColor="Blue"
HeightRequest="{Binding rowHeigth}"
ItemsSource="{Binding items}">
<CollectionView.Header>
<Label
x:Name="HeaderLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center"
TextTransform="Uppercase" />
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" />
<Image
Grid.Column="1"
HeightRequest="30"
IsVisible="{Binding IsSelected}"
Source="a11.png" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Button
BackgroundColor="Transparent"
HorizontalOptions="End"
Text="Cancel"
TextColor="Green" />
</CollectionView.Footer>
</CollectionView>
<Button
x:Name="btnadd"
Command="{Binding AddCommand}"
Text="add item" />
</StackLayout>
public partial class Page5 : ContentPage
{
public Page5()
{
InitializeComponent();
this.BindingContext = new itemviewmodel();
}
}
public class itemviewmodel:ViewModelBase
{
public ObservableCollection<ActionSheetItem> items { get; set; }
private int _rowHeigth;
public int rowHeigth
{
get { return _rowHeigth; }
set
{
_rowHeigth = value;
RaisePropertyChanged("rowHeigth");
}
}
public ICommand AddCommand { protected set; get; }
public itemviewmodel()
{
items = new ObservableCollection<ActionSheetItem>();
for(int i=0;i<5;i++)
{
ActionSheetItem item = new ActionSheetItem();
item.Name = "name " + i;
item.IsSelected = true;
items.Add(item);
}
rowHeigth = items.Count * 60;
AddCommand = new Command<ActionSheetItem>(async (key) => {
items.Add(new ActionSheetItem() { Name = "test letter ", IsSelected=true });
rowHeigth = items.Count * 60;
});
}
}
The ViewModelBase is class that implementing INotifyPropertyChanged interface.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In my case BindableLayaout attached to StackLayout was best solution.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/layouts/bindablelayout
In Xamarin Forms, I want to implement a horizontal listview (like shown in the image below). Via Rotation this is possible, but the I cannot change the row width. Is there also a possibility the let the second layout start under the first one?
Thanks in advance!
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Recipe.Pages.SearchPage"
Title="Search">
<ContentPage.Content>
<StackLayout Spacing="5" x:Name="layout" Orientation="Vertical" >
<StackLayout x:Name="layoutButtons" HorizontalOptions="FillAndExpand" Orientation="Horizontal" Spacing="5" BackgroundColor="Red">
<Button x:Name="btn1" BackgroundColor="White" Image="#drawable/scan" />
<Button x:Name="btn2" BackgroundColor="White" Image="#drawable/menu" />
<Button x:Name="btn3" BackgroundColor="White" Image="#drawable/search" />
</StackLayout>
<StackLayout x:Name="layoutList" >
<ListView x:Name="listView" Rotation="270" RowHeight="75" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical" >
<StackLayout Orientation="Horizontal" >
<Button BackgroundColor="White" Rotation="90" Image="{Binding Recipe}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
EDIT
I also tried with a grid in the listview. Having the same issue.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Recipe.Pages.SearchPage"
Title="Search">
<ContentPage.Content>
<StackLayout Spacing="5" x:Name="layout" Orientation="Vertical" >
<StackLayout x:Name="layoutButtons" HorizontalOptions="FillAndExpand" Orientation="Horizontal" Spacing="5" BackgroundColor="Red">
<Button x:Name="btn1" BackgroundColor="White" Image="#drawable/scan" />
<Button x:Name="btn2" BackgroundColor="White" Image="#drawable/menu" />
<Button x:Name="btn3" BackgroundColor="White" Image="#drawable/search" />
</StackLayout>
<StackLayout x:Name="layoutList" >
<ListView x:Name="listView" Rotation="270" RowHeight="75" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" BackgroundColor="White" Rotation="90" Image="{Binding Recipe}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I have also facing the same issue. I used below xaml code to manage the height and width of ListView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VCRoom.HorizontalScroll">
<ContentPage.Content >
<RelativeLayout>
<ListView x:Name="TestListView"
RowHeight="80"
Rotation="270"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5, Constant=-40}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=-0.5, Constant=40}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=Constant, Constant=80}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}"
>
</ListView>
</RelativeLayout>
</ContentPage.Content>
</ContentPage>
Change RowHeight and Constant in XConstraint and YConstraint to manage Width and height of horizontal ListView accordingly.
Just for reference Below is custom cell I used to populate ListView items. I displayed vertical labels in each list item.
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="VCRoom.TestCell">
<ViewCell.View>
<StackLayout Orientation="Horizontal" HorizontalOptions="End" VerticalOptions ="Center">
<Label Rotation="90" Text="{Binding Day}" TextColor="#000000" HorizontalOptions="StartAndExpand" VerticalOptions="Start"/>
<Label Rotation="90" Text="{Binding mDate}" TextColor="#000000" HorizontalOptions="StartAndExpand" VerticalOptions="Start"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
Hope this will help future users.
The best solution that i found to resolve this problem is making a CustomScrollView with properties from Listview, like is showed in the tutorial from the Fabio Cozzolino.
Pay attention that he has a updated version in the comments.
He created a custom scrollview:
public class TLScrollView : ScrollView
{ public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(CustomScrollView), default(IEnumerable),
BindingMode.Default, null, new BindableProperty.BindingPropertyChangedDelegate(HandleBindingPropertyChangedDelegate));
private static object HandleBindingPropertyChangedDelegate(BindableObject bindable, object value)
{
throw new NotImplementedException();
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(CustomScrollView), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public event EventHandler<ItemTappedEventArgs> ItemSelected;
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(CustomScrollView), null);
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public static readonly BindableProperty SelectedCommandParameterProperty =
BindableProperty.Create("SelectedCommandParameter", typeof(object), typeof(CustomScrollView), null);
public object SelectedCommandParameter
{
get { return GetValue(SelectedCommandParameterProperty); }
set { SetValue(SelectedCommandParameterProperty, value); }
}
static void HandleBindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue)
{
var isOldObservable = oldValue?.GetType().GetTypeInfo().ImplementedInterfaces.Any(i => i == typeof(INotifyCollectionChanged));
var isNewObservable = newValue?.GetType().GetTypeInfo().ImplementedInterfaces.Any(i => i == typeof(INotifyCollectionChanged));
var tl = (CustomScrollView)bindable;
if (isOldObservable.GetValueOrDefault(false))
{
((INotifyCollectionChanged)oldValue).CollectionChanged -= tl.HandleCollectionChanged;
}
if (isNewObservable.GetValueOrDefault(false))
{
((INotifyCollectionChanged)newValue).CollectionChanged += tl.HandleCollectionChanged;
}
if (oldValue != newValue)
{
tl.Render();
}
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Render();
}
}
Created a method to render the scrollview:
public void Render ()
{ if (ItemTemplate == null || ItemsSource == null)
{
Content = null;
return;
}
var layout = new StackLayout();
layout.Orientation = Orientation == ScrollOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in ItemsSource)
{
var command = SelectedCommand ?? new Command((obj) =>
{
var args = new ItemTappedEventArgs(ItemsSource, item);
ItemSelected?.Invoke(this, args);
});
var commandParameter = SelectedCommandParameter ?? item;
var viewCell = ItemTemplate.CreateContent() as ViewCell;
viewCell.View.BindingContext = item;
viewCell.View.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = command,
CommandParameter = commandParameter,
NumberOfTapsRequired = 1
});
layout.Children.Add(viewCell.View);
}
Content = layout; }
Then, a customRenderer to each platform (Here iOS):
[assembly: ExportRenderer(typeof(TLScrollView), typeof(TLScrollViewRenderer))]
namespace TitiusLabs.Forms.iOS.Controls
{
class CustomScrollViewRenderer : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var element = e.NewElement as CustomScrollView;
element?.Render();
}
} }
You can use the FlowListView plugin to archive horizontal listview easily on xamarin forms.
NuGet - DLToolkit.Forms.Controls.FlowListView
In the Windows UWP XAML Navigation sample from git hub, how can you hide the very top part of the hamburger menu flyout that obscures the section title?
Currently it renders like this so there is a strip that hides the section title of the page.
How can I get it to look like this? So the Section title is not obscured when I open the menu.
I tried playing with the z-index of the page header, but it had no effect. The hamburger menu always renders over top everything else.
Just check the Microsoft weather app for windows 10, I think it's more like there is a region out of the SplitView control, which is to hold like "hamburger button", "back button", "commandbar", and "AutoSuggestBox".
Here I wrote a sample:
<Page.Resources>
<local:BoolToVisiableConverter x:Key="visiblecvt" />
<local:BackgroundConverter x:Key="backgroundcvt" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="15*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="{Binding ElementName=listmenu, Path=SelectedItem.MenuText, Converter={StaticResource backgroundcvt}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Button BorderThickness="0" Background="LightBlue" Click="Button_Click_Pane" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Button.Content>
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="24" />
</Button.Content>
</Button>
<Button BorderThickness="0" Background="Transparent" Click="Button_Click_Back" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Button.Content>
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="24" />
</Button.Content>
</Button>
<TextBlock FontSize="24" Grid.Column="2" x:Name="title" VerticalAlignment="Center" Text="{Binding ElementName=listmenu, Path=SelectedItem.MenuText}" />
<CommandBar Grid.Column="3" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Visibility="{Binding ElementName=title, Path=Text, Converter={StaticResource visiblecvt}}">
<CommandBar.Content>
<Grid />
</CommandBar.Content>
<AppBarButton Icon="Accept" FontSize="24" Label="Accept" />
<AppBarButton Icon="Cancel" FontSize="24" Label="Cancel" />
</CommandBar>
<AutoSuggestBox Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Stretch" IsSuggestionListOpen="True" />
</Grid>
<SplitView Grid.Row="1" x:Name="RootSpiltView" OpenPaneLength="300" CompactPaneLength="50" DisplayMode="CompactOverlay">
<SplitView.Pane>
<ListView x:Name="listmenu" ItemsSource="{x:Bind menu}" SelectionChanged="ListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MenuIcon}" FontFamily="Segoe MDL2 Assets" FontSize="24" VerticalAlignment="Center" />
<TextBlock Text="{Binding MenuText}" Margin="15" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</SplitView.Pane>
<SplitView.Content>
<Frame x:Name="splitviewContent" Navigated="splitviewContent_Navigated" />
</SplitView.Content>
</SplitView>
</Grid>
code behind:
private ObservableCollection<NavigationItem> menu = new ObservableCollection<NavigationItem>();
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
menu.Clear();
menu.Add(new NavigationItem { PageLink = typeof(Page1), MenuText = typeof(Page1).Name, MenuIcon = "\xE715" });
menu.Add(new NavigationItem { PageLink = typeof(Page2), MenuText = typeof(Page2).Name, MenuIcon = "\xE716" });
menu.Add(new NavigationItem { PageLink = typeof(Page3), MenuText = typeof(Page3).Name, MenuIcon = "\xE722" });
menu.Add(new NavigationItem { PageLink = typeof(Page4), MenuText = typeof(Page4).Name, MenuIcon = "\xE72D" });
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
listmenu.SelectedIndex = 0;
}
private void Button_Click_Pane(object sender, RoutedEventArgs e)
{
this.RootSpiltView.IsPaneOpen = !this.RootSpiltView.IsPaneOpen;
}
private void Button_Click_Back(object sender, RoutedEventArgs e)
{
if (splitviewContent.CanGoBack)
{
splitviewContent.GoBack();
}
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var naviitem = listmenu.SelectedItem as NavigationItem;
splitviewContent.Navigate(naviitem.PageLink);
}
private void splitviewContent_Navigated(object sender, NavigationEventArgs e)
{
var page = splitviewContent.CurrentSourcePageType.Name;
switch (page)
{
case "Page1":
listmenu.SelectedIndex = 0;
break;
case "Page2":
listmenu.SelectedIndex = 1;
break;
case "Page3":
listmenu.SelectedIndex = 2;
break;
case "Page4":
listmenu.SelectedIndex = 3;
break;
}
}
The NavigationItem class and two converters:
public class NavigationItem
{
public string MenuIcon { get; set; }
public string MenuText { get; set; }
public Type PageLink { get; set; }
}
public class BoolToVisiableConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var text = (string)value;
if (text == "Page1")
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var text = (string)value;
if (text == "Page1")
{
return "#FFFFC0CB";
}
return "#00000000";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
I didn't follow to the official XAML Navigation sample to wrote this code, here my sample renders like this:
#Henk Holterman's comment also makes sense. In the official sample, the title is part of the page content. For different page, the title may have different size. But in Weather app, the title is separated from the content, so it will be easy to achieve.
I am implementing an MVVM Pattern using Entity as my model... When I instantiate the Model in the setter I add a propertychanged handler to the object to capture the field changes within the entity object...
When I step through code I find that after I modify the field within the object it is firing the Property Change event multiple times...
It has led me to believe that I have done something wrong...
I want to use the entity as my model as I do not want to recreate the object as an abstacted object...
Can any find any fault in the code base below that would cause the SelectedCompany.PropertyChanged to fire more then once when the datagrid field is modified...
I was expecting that that the property changed event would only fire once and am a bit perplexed as to why it fires multiple times...
Was it wrong to add the propertyChange event in the Setter...
Here is the ViewModel...
public class CompanyMaintenanceVM : INotifyPropertyChanged
{
private ServiceHandler _serviceHandler = new ServiceHandler();
private bool _ignorePropertyChange = false; //by default we will execute on every property change...
public CompanyMaintenanceVM()
{
_companyList = new ObservableCollection<Company>(_serviceHandler.GetCompanies().ToList());
_selectedCompany = _companyList.First();
UpdateCommand = new RelayCommand(Update) { IsEnabled = true };
SearchCommand = new RelayCommand(Search) { IsEnabled = true };
_companyTypeList = new ObservableCollection<CompanyType>(_serviceHandler.GetCompanyTypes().ToList());
}
private string _selectedKey;
public string SelectedKey
{
get { return _selectedKey; }
set
{
if (_selectedKey != value)
{
_selectedKey = value;
OnPropertyChanged("SelectedKey");
}
}
}
private Company _selectedCompany = new Company();
public Company SelectedCompany
{
get { return _selectedCompany; }
set
{
if (_selectedCompany != value)
{
_selectedCompany = value;
OnPropertyChanged("SelectedCompany");
SelectedCompany.PropertyChanged += new PropertyChangedEventHandler(SelectedCompany_PropertyChanged);
}
}
}
private ObservableCollection<Company> _companyList;
public ObservableCollection<Company> CompanyList
{
get { return _companyList; }
set
{
_companyList = value;
OnPropertyChanged("CompanyList");
}
}
private ObservableCollection<CompanyType> _companyTypeList;
public ObservableCollection<CompanyType> CompanyTypeList
{
get { return _companyTypeList; }
set { _companyTypeList = value; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
switch (propertyName)
{
case "SelectedKey":
if (_ignorePropertyChange == false)
{
SelectedKeyChanged();
}
_ignorePropertyChange = false;
break;
case "SelectedCompany":
_ignorePropertyChange = true;
SelectedKey = SelectedCompany.CompanyID;
break;
}
}
void SelectedCompany_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//
}
#endregion
private void SelectedKeyChanged()
{
var companies = _serviceHandler.GetCompanyByID(SelectedKey);
if (companies.FirstOrDefault() != null)
{
CompanyList = new ObservableCollection<Company>(companies);
SelectedCompany = CompanyList.First();
}
else
{
MessageBox.Show("No Company Record Exsists Matching CompanyID " + _selectedKey);
}
}
private ICommand _updateCommand;
public ICommand UpdateCommand
{
get
{
if (_updateCommand == null)
_updateCommand = new Updater();
return _updateCommand;
}
set
{
_updateCommand = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(Object parameter)
{
}
#endregion
}
private void Update()
{
_serviceHandler.UpdateRepository(SelectedCompany);
_serviceHandler.CommitRepository();
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
_searchCommand = new Searcher();
return _searchCommand;
}
set
{
_searchCommand = value;
}
}
private class Searcher : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(Object parameter)
{
}
#endregion
}
private void Search()
{
}
}
Here is the XAML...
<Window x:Class="XERP.Client.WPF.CompanyMaintenance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Company Maintenance" Height="800" Width="600">
<Grid>
<Grid.Resources>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="422*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="156*"></ColumnDefinition>
<ColumnDefinition Width="422*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Center" Grid.Column="1" Text="Company Maintenance Form"
FontSize="13"
Margin="130,3,129,5"></TextBlock>
<Menu IsMainMenu="True" Grid.ColumnSpan="2" Margin="2,2,370,2" Width="180">
<MenuItem Header="_File" >
<MenuItem Header="_New" Command="New">
</MenuItem>
<MenuItem Header="_Save"
IsCheckable="True"
Command="{Binding UpdateCommand}" Click="SaveMenuItem_Click">
</MenuItem>
<MenuItem Header="_Exit" Command="Close">
</MenuItem>
</MenuItem>
<MenuItem Header="_Edit">
<MenuItem Header="_Cut" Command="Cut">
</MenuItem>
<MenuItem Header="_Copy" Command="Copy">
</MenuItem>
<MenuItem Header="_Paste" Command="Paste">
</MenuItem>
</MenuItem>
<MenuItem Header="_Tools" />
<MenuItem Header="_Actions" />
<MenuItem Header="_Help" />
</Menu>
<GridSplitter Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" Width="4" Background="Yellow"/>
<ListView Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5"
ItemsSource="{Binding CompanyList}"
SelectedItem="{Binding SelectedCompany, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
>
<ListView.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=CompanyID, Mode=TwoWay}" Margin="5"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TabControl Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabItem Header="Detail">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="46"></RowDefinition>
<RowDefinition Height="657*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="165*"></ColumnDefinition>
<ColumnDefinition Width="246*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="0" Grid.ColumnSpan="2">
<Button Width="100" Height="20" Margin="10"
Command="{Binding SearchCommand}">Company...</Button>
<TextBox Width="100" Height="20" Margin=" 10"
Text="{Binding Path=SelectedKey, Mode=TwoWay}"
/>
</StackPanel>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="1" Grid.Column="0">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" Margin="8">Name:</TextBlock>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" Margin="8">Description:</TextBlock>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" Margin="8">Type:</TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="1" Grid.Column="1">
<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" Width="200"
Text="{Binding Path=SelectedCompany.Name, Mode=TwoWay}"
/>
<TextBox
HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" Width="200"
Text="{Binding Path=SelectedCompany.Description, Mode=TwoWay}"
/>
<ComboBox HorizontalAlignment="Left" Width="200" Margin="5"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CompanyTypeList, Mode=TwoWay}"
DisplayMemberPath="Type"
SelectedValuePath="CompanyTypeID"
SelectedValue="{Binding Path=SelectedCompany.CompanyTypeID, Mode=TwoWay}"/>
<TextBox Height="23" Name="ghost" Width="0" />
</StackPanel>
</Grid>
</TabItem>
<TabItem Header="List" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding CompanyList}"
SelectedItem="{Binding SelectedCompany, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CompanyID, Mode=TwoWay}"
Header="ID" Width="auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Name" Width="Auto"/>
<DataGridTextColumn Binding="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Description" Width="Auto"/>
<DataGridComboBoxColumn Header="Type" Width="Auto"
SelectedValueBinding ="{Binding CompanyTypeID, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
DisplayMemberPath="Type"
SelectedValuePath="CompanyTypeID">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CompanyTypeList, Mode=TwoWay}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CompanyTypeList, Mode=TwoWay}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox Height="23" Name="ghost2" Width="0" />
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
be aware of using selecteditem with twoway binding AND IsSynchronizedWithCurrentItem="True" - maybe there you get your roundtrips.
and what your cause for the following?
if (_selectedCompany != value)
{
_selectedCompany = value;
OnPropertyChanged("SelectedCompany");
SelectedCompany.PropertyChanged += new PropertyChangedEventHandler(SelectedCompany_PropertyChanged); //<-- whats that?
}
and why you do this?
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
switch (propertyName)//why you do this?
{
case "SelectedKey":
if (_ignorePropertyChange == false)
{
SelectedKeyChanged();
}
_ignorePropertyChange = false;
break;
case "SelectedCompany":
_ignorePropertyChange = true;
SelectedKey = SelectedCompany.CompanyID;
break;
}
}