Enable and disable buttons in the AppBar using Binding - xaml

I have an applicationbar with 2 buttons "edit" and "save" on a xaml page.
When the pages is loaded the "edit" button has to be enabled and the "save" button disabled.
When you press the "edit" button the "save" button gets enabled same for when you press the "save" button.
The problem is that both buttons are disabled when the page is loaded.
My code:
Button properties:
private bool _editButtonIsEnabled;
public bool EditButtonIsEnabled
{
get
{
return _saveButtonIsEnabled;
}
set
{
_editButtonIsEnabled = value;
RaisePropertyChanged("EditButtonIsEnabled");
}
}
private bool _saveButtonIsEnabled;
public bool SaveButtonIsEnabled
{
get
{
return _saveButtonIsEnabled;
}
set
{
_saveButtonIsEnabled = value;
RaisePropertyChanged("SaveButtonIsEnabled");
}
}
Xaml page with binding:
<Sh:AdvancedApplicationBar>
<Grid>
<Sh:AdvancedApplicationBarIconButton Text="edit"
IconUri="/Assets/ActionBarButtons/btn_actionbar_edit.png"
Command="{Binding EditFavorithProgramsCommand}"
VerticalAlignment="Bottom"
IsEnabled="{Binding EditButtonIsEnabled}"/>
<Sh:AdvancedApplicationBarIconButton Text="save"
IconUri="/Assets/ActionBarButtons/btn_actionbar_save.png"
Command="{Binding SaveFavorithProgramsCommand}"
VerticalAlignment="Bottom"
IsEnabled="{Binding SaveButtonIsEnabled}" />
</Grid>
</Sh:AdvancedApplicationBar>

you got a typo in your EditButtonIsEnabled getter
public bool EditButtonIsEnabled
{
get
{
return _saveButtonIsEnabled;
}
//...
}

Related

Why is MenuFlyout.Items always null in Closing event?

I can successfully add a MenuFlyoutItem to a MenuFlyout dynamically in the ContextMenuFlyout_Opening event, but when I try to remove it in ContextMenuFlyout_Closing the MenuFlyout.Items is always null and the MFI isnt found and removed.
Any ideas why this is so?
<Page.Resources>
<MenuFlyout
x:Key="ListViewContextMenu"
Closing="{x:Bind ViewModel.ContextMenuFlyout_Closing}"
Opening="{x:Bind ViewModel.ContextMenuFlyout_Opening}">
<MenuFlyoutItem
Click="{x:Bind ViewModel.EditParty}"
Icon="Edit"
Text="Edit" />
<MenuFlyoutItem Text="Open in new window">
<MenuFlyoutItem.Icon>
<FontIcon
FontFamily="Segoe MDL2 Assets"
FontSize="40"
Glyph="ξΆ§" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutItem
Click="MenuFlyoutItem_Click"
Icon="Delete"
Text="Delete" />
</MenuFlyout>
</Page.Resources>
ViewModel event handlers
public void ContextMenuFlyout_Opening(object sender, object e)
{
MenuFlyout flyout = sender as MenuFlyout;
if (flyout != null)
{
// If party.IsConflict = true then add the MFI
if (SelectedTalent.IsConflict)
{
flyout.Items.Add(new MenuFlyoutItem()
{
Icon = new FontIcon() { Glyph = "\uEC4F" },
Text = "Resolve Conflict"
});
}
}
}
public void ContextMenuFlyout_Closing(object sender, object e)
{
// Remove 'Resolve Conflict' MFI if its found
MenuFlyout flyout = sender as MenuFlyout;
if (flyout != null)
{
var rc = flyout.Items.FirstOrDefault(o => o.Name == "Resolve Conflict");
if (rc != null)
{
flyout.Items.Remove(rc);
}
}
}
ListView that uses the MenuFlyout
<ListView
ContextFlyout="{StaticResource ListViewContextMenu}"
Based on your code, it seems that you are trying to get the MenuFlyoutItem object by name. But you forget to give the MenuFlyoutItem object a Name when you add it to the MenuFlyout, you just added a Text property.
flyout.Items.Add(new MenuFlyoutItem()
{
Icon = new FontIcon() { Glyph = "\uEC4F" },
Text = "Resolve Conflict",
Name = "Resolve Conflict",
});

Refresh xaml page every x seconds and keep the current expander state

I have a xamarin project. There is a scrollview with a list of expanders.
I like to refresh the page every x seconds, but keep the state of my expanders (isExpanded boolean).
How do I check the state of my expanders (or label, button, whatever) and keep these values during a refresh every x seconds?
I feel like I have to add a parameter to my behindcode function, similar to the 'object sender' during a tap or click event.
In the behindcode I am trying to refresh the page every x seconds with
Device.StartTimer(TimeSpan.FromSeconds(x),Updatefunction);
Currently they all have their default isExpanded (false) state when the page refreshes.
You can add a bool property in the viewModel, then binding this property to IsExpanded="{Binding Expand1Opened}" in <Expander> tab. When user click the Expander, IsExpanded will depend on the value of Expand1Opened property. no matter the refresh the page every x seconds, it will keep the current expander state. And I add Command for Expander, if Expander is clicked, value of Expand1Opened property will be changed in the ViewModel.
<RefreshView IsRefreshing="{Binding IsRefreshing}"
RefreshColor="Teal"
Command="{Binding RefreshCommand}">
<ScrollView>
<StackLayout>
<Expander IsExpanded="{Binding Expand1Opened}" Command="{Binding Expand1OpenedCommand}">
<Expander.Header>
<Label Text="List1"
FontAttributes="Bold"
FontSize="Medium" />
</Expander.Header>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FlexLayout Direction="Row"
Wrap="Wrap"
AlignItems="Center"
AlignContent="Center"
BindableLayout.ItemsSource="{Binding Items}"
BindableLayout.ItemTemplate="{StaticResource ColorItemTemplate}" />
</Grid>
</Expander>
<Expander IsExpanded="{Binding Expand2Opened}" Command="{Binding Expand2OpenedCommand}">
<Expander.Header>
<Label Text="List2"
FontAttributes="Bold"
FontSize="Medium" />
</Expander.Header>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FlexLayout Direction="Row"
Wrap="Wrap"
AlignItems="Center"
AlignContent="Center"
BindableLayout.ItemsSource="{Binding Items}"
BindableLayout.ItemTemplate="{StaticResource ColorItemTemplate}" />
</Grid>
</Expander>
</StackLayout>
</ScrollView>
</RefreshView>
Here is viewModel. I have two <Expander>, So I add two properties Expand1Opened and Expand2Opened, and add two Commands called Expand1OpenedCommand and Expand2OpenedCommand, if the <Expander> were clicked, Expand1OpenedCommand will be invoked, then value of Expand1Opened will be changed, If the refreshview was refreshed, value of Expand1Opened will not be changed, so expander's state wil be kept.
public class MainPageViewModel : INotifyPropertyChanged
{
const int RefreshDuration = 2;
int itemNumber = 1;
readonly Random random;
bool isRefreshing;
public bool IsRefreshing
{
get { return isRefreshing; }
set
{
isRefreshing = value;
OnPropertyChanged();
}
}
bool expand1Opened = false;
public bool Expand1Opened
{
get { return expand1Opened; }
set
{
expand1Opened = value;
OnPropertyChanged();
}
}
bool expand2Opened=false;
public bool Expand2Opened
{
get { return expand2Opened; }
set
{
expand2Opened = value;
OnPropertyChanged();
}
}
public ObservableCollection<Item> Items { get; private set; }
public ICommand RefreshCommand => new Command(async () => await RefreshItemsAsync());
public ICommand Expand1OpenedCommand { get; set; }
public ICommand Expand2OpenedCommand { get; set; }
public MainPageViewModel()
{
random = new Random();
Items = new ObservableCollection<Item>();
Expand1OpenedCommand = new Command((() =>
{
expand1Opened = !expand1Opened;
}));
Expand2OpenedCommand = new Command((() =>
{
expand2Opened = !expand2Opened;
}));
AddItems();
}
void AddItems()
{
for (int i = 0; i < 1; i++)
{
Items.Add(new Item
{
Color = Color.FromRgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)),
Name = $"Item {itemNumber++}",
Isfavourite = false
});
}
}
async Task RefreshItemsAsync()
{
IsRefreshing = true;
await Task.Delay(TimeSpan.FromSeconds(RefreshDuration));
AddItems();
IsRefreshing = false;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
Here is running GIF.

Custom MaterialFrame control with only one platform renderer

In my Xamarin.Forms app, I have MaterialFrame custom control.
iOS renderer works great and looks like:
public class MaterialFrameRenderer : FrameRenderer
{
private const int ShadowColor = 0x939393;
public override void Draw(
CGRect rect)
{
base.Draw(rect);
// Update shadow to match better material design standards of elevation
Layer.ShadowRadius = Layer.CornerRadius;
Layer.ShadowColor = ColorHelper.FromHex(ShadowColor).CGColor;
Layer.ShadowOffset = new CGSize(1, 1);
Layer.ShadowOpacity = 0.30f;
Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
}
On Android platform I want to use implementation based on my other cross-platform control. My .net standard(shared project) implementation:
public class MaterialFrame : Frame
{
public MaterialFrame()
{
if (Device.RuntimePlatform == Device.Android)
{
Content = new MyOtherCustomControl
{
BackgroundColor = Color.Red
};
}
}
}
Unfortunately this implementation doesn't work on Android. Do you have any suggestion?
I create the MyOtherCustomControl.
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="MyOtherCustomControl"
VerticalOptions="CenterAndExpand" />
<Button />
</StackLayout>
And use it in MaterialFrame custom control.
public class MaterialFrame : Frame
{
public MaterialFrame()
{
if (Device.RuntimePlatform == Device.Android)
{
Content = new MyOtherCustomControl
{
BackgroundColor = Color.Red
};
}
}
}
Usage of MaterialFrame:
<ContentPage.Content>
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand" />
<pages:MaterialFrame />
</StackLayout>
</ContentPage.Content>
Screenshot:

Keep highlighting the cell when user deselect

I'm using Xamarin Forms ListView as a SideBar. How can I prevent users from deselecting cell? Or at least keep highlighting the cell when users deselect it.
This is how I'm binding
<ListView x:Name="listView" SelectionMode="Single">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Management</x:String>
<x:String>Information</x:String>
<x:String>Language</x:String>
<x:String>Settings</x:String>
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
According to your description, when you select item from ListView, this item highlighting, you want to this item still highlighting when this item is not selected state. It seems that you want to select multiple item from ListView.
I've made a sample, you can take a look:
<ContentPage.Content>
<StackLayout>
<ListView
ItemTapped="ListView_ItemTapped"
ItemsSource="{Binding Items}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="{Binding background}" Orientation="Horizontal">
<Label
HorizontalOptions="StartAndExpand"
Text="{Binding DisplayName}"
TextColor="Fuchsia" />
<BoxView
HorizontalOptions="End"
IsVisible="{Binding Selected}"
Color="Fuchsia" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class Page10 : ContentPage
{
public Page10 ()
{
InitializeComponent ();
this.BindingContext = new MultiSelectItemsViewModel();
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Model m = e.Item as Model;
if(m!=null)
{
m.Selected = !m.Selected;
if(m.background==Color.White)
{
m.background = Color.BlueViolet;
}
else
{
m.background = Color.White;
}
}
}
}
public class Model:ViewModelBase
{
public string DisplayName { get; set; }
private bool _Selected;
public bool Selected
{
get { return _Selected; }
set
{
_Selected = value;
RaisePropertyChanged("Selected");
}
}
private Color _background;
public Color background
{
get { return _background; }
set
{
_background = value;
RaisePropertyChanged("background");
}
}
}
public class MultiSelectItemsViewModel
{
public ObservableCollection<Model> Items { get; set; }
public MultiSelectItemsViewModel()
{
Items = new ObservableCollection<Model>();
Items.Add(new Model() { DisplayName = "AAA", Selected = false,background=Color.White });
Items.Add(new Model() { DisplayName = "BBB", Selected = false , background = Color.White });
Items.Add(new Model() { DisplayName = "CCC", Selected = false, background = Color.White });
Items.Add(new Model() { DisplayName = "DDD", Selected = false, background = Color.White });
Items.Add(new Model() { DisplayName = "EEE", Selected = false, background = Color.White });
}
}
Update:
Don't allow user to unselect the selected item.
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Model m = e.Item as Model;
if(m!=null)
{
m.Selected = true;
m.background = Color.Blue;
}
}
Depending on your needs, I've done something similar but with controls inside each row, like a checkbox.
https://xamarinhelp.com/multiselect-listview-xamarin-forms/
Use the SelectedItem property of the ListView. As long as SelectedItem property is not set back to null, the currently selected item will remain highlighted.

Xamarin reusable xaml user control and custom command

first of all sorry for my english.
I am working on a iOS and Android project using Xamarin.Form
I would like to have a 'xaml user control' reusable in different way, and I need to make it clickable with ICommand
This is the StackLayoutButton component:
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Installa.Controls.StackLayoutButton">
<Image x:Name="Icon" Source="{Binding Icon}" />
<Label x:Name="Text" Text="{Binding Title}" HorizontalOptions="Center" LineBreakMode="NoWrap" Font="Small" TextColor="Red" />
</StackLayout>
This is the CalendarioPage xaml page where the component is used
<?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:controls="clr-namespace:Installa.Controls;assembly=Installa"
x:Class="Installa.CalendarioPage">
<StackLayout>
<Label Text="{Binding ViewName}" Font="42" IsVisible="{Binding IsWindowsPhone}" />
<ActivityIndicator IsRunning="{Binding IsLoading}" IsVisible="{Binding IsLoading}" Color="Red" />
<controls:StackLayoutButton BindingContext="{Binding Blog}" TextColor="Blue" /> <!-- Command="{Binding SubmitCommand}" -->
<controls:StackLayoutButton BindingContext="{Binding Facebook}" TextColor="Red" /> <!-- Command="{Binding SubmitCommand}" -->
</StackLayout>
</ContentPage>
This is the CalendarioPage c# page:
public partial class CalendarioPage : ContentPage
{
private CalendarioViewModel vm;
public CalendarioPage()
{
InitializeComponent();
vm = new CalendarioViewModel();
this.BindingContext = vm;
}
}
This is the viewmodel class:
namespace Installa
{
public class CalendarioViewModel: BaseViewModel
{
public CalendarioViewModel()
{
blog = new Activity();
blog.Link = "www.google.it";
blog.Title = "Titolo del blog";
blog.Icon = "logomenu.png";
facebook = new Activity();
facebook.Title = "Tito Fbook";
facebook.Link = "www.facebook.it";
facebook.Icon = "icon.png";
ViewName = "nome della view";
IsLoading = false;
}
Activity blog = null;
public Activity Blog
{
get {return blog;}
}
Activity facebook = null;
public Activity Facebook
{
get { return facebook; }
}
string viewName = string.Empty;
public string ViewName
{
get { return viewName; }
set { SetProperty(ref viewName, value); }
}
public bool IsWindowsPhone
{
get
{
return Device.OS == TargetPlatform.WinPhone;
}
}
bool isLoading = false;
public bool IsLoading
{
get { return isLoading; }
set { SetProperty(ref isLoading, value); }
}
}
}
With Activity a simple class with:
public string Title { get; set; }
public string Link { get; set; }
public String Icon { get; set; }
Till now, all is working right, but now I need to implement the ICommand interface.
In StackLayoutButton c# code I try to add:
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "TapCommand");
Icon.GestureRecognizers.Add(tapGestureRecognizer)
Text.GestureRecognizers.Add(tapGestureRecognizer)
Furthermore I try to add into CalendarioViewModel INotifyPropertyChanged and the 'OnTapped' method.
Into Activity.cs i add 'ICommand tapCommand' and the related get...but is not working.
I try even other..but I am not able to enable the tap on the StackLayoutButton components.
In wich way I should do ?
I'd like to be able to have a 'programmable' command...for example I would like browse to 'the link property' of Activity or be able to open a new view.
Thanks for help!
Update:
I was able to add TapGestureRecognizer into the xaml user control (StackLayoutButton.xaml.cs),
but I'd like to implement it in MVVM way,
using Xamarin.Forms;
namespace Installa.Controls
{
public partial class StackLayoutButton : StackLayout
{
public StackLayoutButton()
{
InitializeComponent();
TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer
{
Command = new Command(OnMyComponentTapped),
CommandParameter = "ciao"
};
this.Icon.GestureRecognizers.Add(tapGestureRecognizer);
this.Text.GestureRecognizers.Add(tapGestureRecognizer);
}
async void OnMyComponentTapped(object parameter)
{
// do action
}
public Color TextColor
{
get { return this.Text.TextColor; }
set { this.Text.TextColor = value; }
}
public Label TextControl
{
get { return this.Text; }
set { this.Text = value; }
}
}
}
can anyone suggest me the way ?
Thanks
This is how I add gesture recognizer on my xaml view:
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SelectEquipmentCommand}"/>
</Label.GestureRecognizers>
And this is the ICommand property in my ViewModel:
public ICommand SelectEquipmentCommand
{
get
{
return fSelectEquipmentCommand ?? (fSelectEquipmentCommand = new Command(async () => await SelectEquipmentTask()));
}
}
private async Task SelectEquipmentTask()
{
}