Xamarin Forms ViewCell Swipe to Show Buttons - ios

tl;dr: How do I use swipe to show buttons in Xamarin Forms like the iOS mail app
I am trying to implement swipe to show buttons for a Xamarin Forms iOS app similar to the UI of the iOS mail app or this https://components.xamarin.com/view/swtableviewcell. That component among many other examples I found look great for iOS native implementations but I need to show this UI via Xamarin forms.
Currently I have a custom swipe gesture recognizer like this:
[assembly: ExportRenderer(typeof(SwipeViewCell), typeof(SwipeIosRenderer))]
namespace MyApp.iOS.Renderers
{
public class SwipeIosRenderer : ViewCellRenderer
{
UISwipeGestureRecognizer swipeRightGestureRecognizer;
UISwipeGestureRecognizer swipeLeftGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
swipeRightGestureRecognizer = new UISwipeGestureRecognizer(() => UpdateRight()) { Direction = UISwipeGestureRecognizerDirection.Right };
swipeLeftGestureRecognizer = new UISwipeGestureRecognizer(() => UpdateLeft()) { Direction = UISwipeGestureRecognizerDirection.Left };
if (e.NewElement == null)
{
if (swipeRightGestureRecognizer != null)
{
this.RemoveGestureRecognizer(swipeRightGestureRecognizer);
}
if (swipeLeftGestureRecognizer != null)
{
this.RemoveGestureRecognizer(swipeLeftGestureRecognizer);
}
}
if (e.OldElement == null)
{
this.AddGestureRecognizer(swipeRightGestureRecognizer);
this.AddGestureRecognizer(swipeLeftGestureRecognizer);
}
}
private void UpdateLeft()
{
Console.WriteLine("Left swipe");
}
private void UpdateRight()
{
Console.WriteLine("Right swipe");
}
}
That is bound to viewcells in a list. Now that I can recognize the "swipe" gesture I need help on how to actually move the view cell over and show a button like the examples I gave above?
It would be great to able to do this within the views XAML but am open to anything. I have a UpdateLeft and UpdateRight function that gets called on the respective swipe motions too if that can be used?
**EDIT: I need to do this for both left AND right swipe. ContextActions only provide the left swipe functionality.
Hope that makes sense!

Would Context Actions work for you? I haven't tried on other platforms, but on iOS it will create a swipe menu just like the Mail app. You should be able to use XAML and bind to command properties as well.
Edit:
Since you clarified that you need the left and right side swipe buttons that do not exist in the ContextActions, you could utilize the existing SWTableViewCell component that already has the desired behavior and adapt it to Xamarin.Forms.
iOSRenderer:
public class SwipeIosRenderer : TextCellRenderer
{
static NSString rid = new NSString("SWTableViewCell");
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var forms_cell = (SwipeCell)item;
SWTableViewCell native_cell = reusableCell as SWTableViewCell;
if (native_cell == null)
{
native_cell = new SWTableViewCell(UITableViewCellStyle.Default, rid);
if (forms_cell != null)
{
var cellDelegate = new CellDelegate(forms_cell);
native_cell.Delegate = cellDelegate;
if (forms_cell.LeftContextActions != null)
{
var left = new NSMutableArray();
foreach (var btn in forms_cell.LeftContextActions)
{
AddButton(left, btn);
}
native_cell.LeftUtilityButtons = NSArray.FromArray<UIButton>(left);
}
if (forms_cell.RightContextActions != null)
{
var right = new NSMutableArray();
foreach (var btn in forms_cell.RightContextActions)
{
AddButton(right, btn);
}
native_cell.RightUtilityButtons = NSArray.FromArray<UIButton>(right);
}
}
native_cell.TextLabel.Text = forms_cell.Text;
}
var fs = forms_cell.ImageSource as FileImageSource;
if (fs != null)
{
native_cell.ImageView.Image = UIImage.FromBundle(fs.File);
}
return native_cell;
}
void AddButton(NSMutableArray array,Button btn){
if (!String.IsNullOrEmpty(btn.Image?.File))
{
array.AddUtilityButton(btn.BorderColor.ToUIColor(), UIImage.FromBundle(btn.Image.File));
}
else
{
array.AddUtilityButton(btn.BorderColor.ToUIColor(), btn.Text);
}
}
public class CellDelegate : SWTableViewCellDelegate
{
SwipeCell forms_cell;
public CellDelegate(SwipeCell forms_cell)
{
this.forms_cell = forms_cell;
}
public override void DidTriggerLeftUtilityButton(SWTableViewCell cell, nint index)
{
if (forms_cell.LeftContextActions.Count > index)
{
var c = forms_cell.LeftContextActions[(int)index];
var cmd = c.Command;
if (cmd != null)
{
cmd.Execute(c.CommandParameter);
}
}
}
public override void DidTriggerRightUtilityButton(SWTableViewCell cell, nint index)
{
if (forms_cell.RightContextActions.Count > index)
{
var c = forms_cell.RightContextActions[(int)index];
var cmd = c.Command;
if (cmd != null)
{
cmd.Execute(c.CommandParameter);
}
}
}
}
Example XAML:
<ListView x:Name="SwipeList">
<ListView.ItemTemplate>
<DataTemplate>
<test:SwipeCell Text="{Binding Data}" ImageSource="{Binding Image}">
<test:SwipeViewCell.LeftContextActions>
<Button Text="L1" Command="{Binding LeftAction}" BorderColor="Aqua"/>
<Button Command="{Binding LeftAction2}" BorderColor="Gray" Image="xamarin.png"/>
</test:SwipeViewCell.LeftContextActions>
<test:SwipeViewCell.RightContextActions>
<Button Text="R1" Command="{Binding RightAction}" BorderColor="Blue" />
<Button Text="R2" Command="{Binding RightAction2}" BorderColor="Purple" />
</test:SwipeViewCell.RightContextActions>
</test:SwipeViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Example Code Behind
public class MyListItem
{
Page page;
public MyListItem(Page page)
{
this.page = page;
this.LeftAction= new Command(() => this.page.DisplayAlert("Left 1", this.Data, "OK"));
this.LeftAction2= new Command(() => this.page.DisplayAlert("Left 2", this.Data, "OK"));
this.RightAction= new Command(() => this.page.DisplayAlert("Right 1", this.Data, "OK"));
this.RightAction2= new Command(() => this.page.DisplayAlert("Right 2", this.Data, "OK"));
}
public string Image{ get; set; }
string data;
public string Data
{
get
{
return data;
}
set
{
data = value;
}
}
ICommand leftAction;
public ICommand LeftAction
{
get
{
return leftAction;
}
set
{
leftAction = value;
}
}
ICommand leftAction2;
public ICommand LeftAction2
{
get
{
return leftAction2;
}
set
{
leftAction2 = value;
}
}
ICommand rightAction;
public ICommand RightAction
{
get
{
return rightAction;
}
set
{
rightAction = value;
}
}
ICommand rightAction2;
public ICommand RightAction2
{
get
{
return rightAction2;
}
set
{
rightAction2 = value;
}
}
public override string ToString()
{
return this.Data;
}
}
public TestPage()
{
InitializeComponent();
this.SwipeList.ItemsSource = new List<MyListItem>(){
new MyListItem(this){Data="A"},
new MyListItem(this){Data="B", Image="xamarin.png"},
new MyListItem(this){Data="C"},
new MyListItem(this){Data="D"},
};
}

Context Actions wasn't exactly what my client wanted. The row menu didn't appear on swipe. It appeared when they hold tap on the row, and the menu appeared at the top of the screen.
I was able to accomplish the swipe row behaviour with the new Xamarin.Forms
SwipeView
Pass the current row into the CommandParameter, and use it in the event handler.
FYI: For some reason the SwipeView has a default BackgroundColor of white, which you can override with something else to match your theme.
Xaml:
<ListView Margin="-20,0,0,0" x:Name="photosListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" SeparatorColor="Gray" VerticalScrollBarVisibility="Default" HasUnevenRows="true" SeparatorVisibility="Default" Background="{StaticResource PrimaryDark}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<SwipeView BackgroundColor="{StaticResource PrimaryDark}" >
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Delete" BackgroundColor="LightPink" Clicked="OnDeleteRow" CommandParameter="{Binding .}" />
</SwipeItems>
</SwipeView.RightItems>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal">
<CheckBox IsVisible="{Binding SelectEnabled}" Color="{StaticResource White}" IsChecked="{Binding Selected}" Margin="20,0,-15,0" CheckedChanged="OnItemCheckedChanged" />
<Grid WidthRequest="70" HeightRequest="50">
<Grid.Margin>
<OnPlatform x:TypeArguments="Thickness" Android="15,0,0,0" iOS="10,0,0,0" />
</Grid.Margin>
<Image Aspect="AspectFill" Source="{Binding ThumbImageSource}" HorizontalOptions="Fill" />
</Grid>
</StackLayout>
<StackLayout Grid.Column="1" Spacing="0" Padding="0" Margin="0,5,0,0">
<Label Text="{Binding Photo.Description}" TextColor="{StaticResource TextColour}" FontSize="16" FontAttributes="Bold" />
<Label Text="{Binding DateTakenString}" TextColor="{StaticResource TextColour}" FontSize="14" />
</StackLayout>
</Grid>
</SwipeView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
cs:
public async void OnDeleteRow(object sender, EventArgs e)
{
if (await GetDeleteRowConfirmationFromUser())
{
SwipeItem si = sender as SwipeItem;
PhotoListItem itemToDelete = si.CommandParameter as PhotoListItem;
LocalDatabaseService db = new LocalDatabaseService();
db.DeletePhoto(itemToDelete.Photo);
_listItems.Remove(itemToDelete);
}
}

Related

Update cell background in Xamarin Form iOS

Does anyone encounter annoyed issue when update background color of cell in xamarin form?
I have notification list, when user clicks on item, it's marked as read then background color will be updated.
It works perfectly on android but iOS.
Most of the time the color is changed back to origin.
In the example, cell background is blue if notification is read.
When user clicks on cell, cell background should be red, but it's not changed most of time.
Notification Model
public class Notification : ObservableObject
{
private string _message;
private bool _isRead;
public string Message
{
get => _message;
set => SetProperty(ref _message, value);
}
public bool IsRead
{
get => _isRead;
set => SetProperty(ref _isRead, value);
}
}
ViewModel
public class MainPageViewModel : BaseViewModel
{
private ObservableCollection<Notification> _notifications;
public ObservableCollection<Notification> Notifications
{
get => _notifications;
set => SetProperty(ref _notifications, value);
}
public ICommand TappedCommand => new Command((o => OnTapped(o)));
public MainPageViewModel()
{
_notifications = new ObservableCollection<Notification>()
{
new Notification()
{
IsRead = false,
Message = "First notification"
},
new Notification()
{
IsRead = false,
Message = "Second notification"
},
new Notification()
{
IsRead = false,
Message = "Third notification"
},
new Notification()
{
IsRead = false,
Message = "Fourth notification"
}
};
}
private void OnTapped(object o)
{
if(!(o is Notification noti)) return;
noti.IsRead = true;
}
}
Page:
<?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:local="clr-namespace:NotificationTest"
x:Class="NotificationTest.MainPage">
<ContentPage.Content>
<ListView x:Name="NotiList" ItemsSource="{Binding Notifications}" ItemTapped="OnTapped" ItemSelected="OnSelected" CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="{Binding IsRead,Converter={StaticResource ReadToColorConverter}}">
<Label Text="{Binding Message}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
Page code behind ( just work around to hide selected line color in listview
public partial class MainPage : ContentPage
{
private MainPageViewModel _vm;
public MainPage()
{
InitializeComponent();
_vm = new MainPageViewModel();
BindingContext = _vm;
}
private void OnTapped(object sender, ItemTappedEventArgs e)
{
_vm.TappedCommand.Execute(e.Item);
}
private void OnSelected(object sender, SelectedItemChangedEventArgs e)
{
NotiList.SelectedItem = null;
}
}
The problem was caused by the default behavior when selecting cell in iOS.
The default selected color will cover the red color , so it didn't get working.
Refer to my recent answer Here
Test

Update TextBox bound to DataGrid

I hope I can explain my problem properly.
I have a datagrid bound to an ObservableCollection object, and a TextBox bound to the selected item of my datagrid.
When I programmatically modify the SelectedItem property value (Name), my TextBox text value is not updated !
here is my design code:
<DataGrid Name="grid" HorizontalAlignment="Left" Margin="119,28,0,0" VerticalAlignment="Top" Height="237" Width="200" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="nom" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
<TextBox Name="textbox" Text="{Binding ElementName=grid, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="18" Margin="119,276,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="200"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="392,54,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
and here is my Code Behind:
ObservableCollection<Element> obs;
class Element
{
public string Name { get; set; }
public Element(string name) { Name = name; }
}
public MainWindow()
{
InitializeComponent();
obs = new ObservableCollection<Element>() { new Element("element2"), new Element("element2"), new Element("element3")};
grid.ItemsSource = obs;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var sel = grid.SelectedItem as Element;
sel.Name = "something";
grid.Items.Refresh(); //this updates the selected element to "something" but does nothing to the textbox
}
Problem solved.
I had to fire PropertyChanged event whenever a modification is made to the model.
here my new code:
class Element : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public Element(string name) { Name = name; }
}

Xamarin forms - IOS issue - Content view nested on top of each other

I created a control in Xamarin forms that flips views(Like a card style). both these views have some sort of input (EX: List of buttons) that if you interact with will flip the "Card" control to the next view. I was able to get this working with Android, but when I test with IOS the controls seems to be disabled and I am not able to hit any events. Now I did solve a similar problem before by using the e.NativeView.UserInteractionEnabled property. The only issue is that this property can only be use when initializing the view, I want to be able to use something similar that is more dynamic.
A little progress on the IOS issue.
<?xml version="1.0" encoding="utf-8" ?>
<TemplatedView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="minto.qm.mobile.Views.Controls.PanelView">
<TemplatedView.ControlTemplate>
<ControlTemplate>
<AbsoluteLayout>
//the last item in this list will be the dominant control on runtime
//the other views input will not work
<ContentView Content="{TemplateBinding BackContent}" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
<ContentView Content="{TemplateBinding FrontContent}" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
</AbsoluteLayout>
</ControlTemplate>
</TemplatedView.ControlTemplate>
</TemplatedView>
Does anyone know of any functionality like this?
https://forums.xamarin.com/discussion/81114/how-to-create-a-custom-controls-with-multiple-view-on-top-of-each-other
update:
here is the behind code for the control:
public partial class PanelView : TemplatedView
{
public event EventHandler FrontContentAppeared;
public event EventHandler BackContentAppeared;
public static readonly BindableProperty FrontContentProperty = BindableProperty.Create(nameof(FrontContent), typeof(View), typeof(PanelView), defaultValue: null, propertyChanged: OnFrontContentChanged);
public static readonly BindableProperty BackContentProperty = BindableProperty.Create(nameof(BackContent), typeof(View), typeof(PanelView), defaultValue: null, propertyChanged: OnBackContentChanged);
public static readonly BindableProperty SwitchViewProperty = BindableProperty.Create(nameof(SwitchView), typeof(bool), typeof(PanelView), defaultValue: false, propertyChanged: OnSwitchViewChanged);
private bool _isFrontView = true;
public PanelView()
{
InitializeComponent();
}
private async void SwitchCurrentView()
{
if (_isFrontView)
{
BackContent.IsVisible = true;
//BackContent.InputTransparent = true;
FrontContent.Unfocus();
BackContent.Focus();
await Task.WhenAll(
FrontContent.FadeTo(0, 500, Easing.Linear),
BackContent.FadeTo(1, 500, Easing.Linear),
this.RotateYTo(GetRotation(0, 180), 250, Easing.Linear)
);
FrontContent.IsVisible = false;
//FrontContent.InputTransparent = false;
BackContentAppeared?.Invoke(this, EventArgs.Empty);
}
else
{
FrontContent.IsVisible = true;
//FrontContent.InputTransparent = true;
FrontContent.Focus();
BackContent.Unfocus();
await Task.WhenAll(
FrontContent.FadeTo(1, 500, Easing.Linear),
BackContent.FadeTo(0, 500, Easing.Linear),
this.RotateYTo(GetRotation(180, 180), 250, Easing.Linear)
);
BackContent.IsVisible = false;
//BackContent.InputTransparent = false;
FrontContentAppeared?.Invoke(this, EventArgs.Empty);
}
_isFrontView = !_isFrontView;
SwitchView = false;
}
private static void OnFrontContentChanged(BindableObject bindable, object oldValue, object newValue)
{
var self = (PanelView)bindable;
self.SetFrontContentView((View)newValue);
}
private static void OnBackContentChanged(BindableObject bindable, object oldValue, object newValue)
{
var self = (PanelView)bindable;
self.SetBackContentView((View)newValue);
}
private static void OnSwitchViewChanged(BindableObject bindable, object oldValue, object newValue)
{
var self = (PanelView)bindable;
self.SwitchView = (bool)newValue;
if (self.SwitchView)
{
self.SwitchCurrentView();
}
}
private void SetFrontContentView(View view)
{
FrontContent = view;
if (!_isFrontView)
{
FrontContent.IsVisible = false;
view.FadeTo(0, 1, Easing.Linear);
}
}
private void SetBackContentView(View view)
{
view.FadeTo(0, 1, Easing.Linear);
view.RotateYTo(180, 1, Easing.Linear);
BackContent = view;
if (_isFrontView)
{
BackContent.IsVisible = false;
}
}
private double GetRotation(double start, double amount)
{
var rotation = (start + amount) % 360;
return rotation;
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (FrontContent != null)
{
SetInheritedBindingContext(FrontContent, BindingContext);
}
if (BackContent != null)
{
SetInheritedBindingContext(BackContent, BindingContext);
}
}
public View FrontContent
{
get { return (View)GetValue(FrontContentProperty); }
set { SetValue(FrontContentProperty, value); }
}
public View BackContent
{
get { return (View)GetValue(BackContentProperty); }
set { SetValue(BackContentProperty, value); }
}
public bool SwitchView
{
get { return (bool)GetValue(SwitchViewProperty); }
set { SetValue(SwitchViewProperty, value); }
}
}
the animation runs when the bindable bool variable SwitchView changes
private void OnRoomTypeTapped(object sender, GliderItemTappedEventArgs e)
{
if (e.IsSelected && e.IsSelected == e.LastSelection)
{
PanelView.SwitchView = true;
}
}
UPDATE:
Xaml Page example. note: (Base:ExtendedPage is a custom contentpage) and (controls:Glider is are custom view with list of buttons on them)
<?xml version="1.0" encoding="utf-8" ?>
<base:ExtendedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:minto.qm.mobile.Views.Controls;assembly=minto.qm.mobile"
xmlns:base="clr-namespace:minto.qm.mobile.Views.Pages.Base;assembly=minto.qm.mobile"
x:Class="minto.qm.mobile.Views.Pages.RoomPage"
Navigation="{Binding Navigation, Mode=OneWayToSource}"
Title="Room">
<base:ExtendedPage.Content>
<ScrollView>
<StackLayout Spacing="0">
<ContentView BackgroundColor="#3498DB" Padding="10">
<Label Text="{Binding SelectedRoomName}" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"></Label>
</ContentView>
<StackLayout Spacing="5" Padding="10, 20, 10, 10">
<controls:PanelView x:Name="PanelView">
<controls:PanelView.FrontContent>
<controls:Glider ItemsSource="{Binding RoomTypes}" DisplayProperty="Name" SelectedIndex="0" ItemSelected="OnRoomTypeSelected" ItemTapped="OnRoomTypeTapped" Orientation="Vertical" Lines="3" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"></controls:Glider>
</controls:PanelView.FrontContent>
<controls:PanelView.BackContent>
<controls:Glider ItemsSource="{Binding Rooms}" DisplayProperty="Name" SelectedIndex="0" ItemSelected="OnRoomSelected" ItemTapped="OnRoomTapped" Orientation="Horizontal" Lines="5" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"></controls:Glider>
</controls:PanelView.BackContent>
</controls:PanelView>
<StackLayout Orientation="Horizontal">
<Label Text="Room annotation:" FontAttributes="Bold" Margin="5, 0, 0, 0" VerticalTextAlignment="Center"></Label>
<Button Text="<" FontAttributes="Bold" HorizontalOptions="EndAndExpand" VerticalOptions="Center" BackgroundColor="Transparent" WidthRequest="50" HeightRequest="50" Clicked="OnExpandButtonClicked" FontSize="Medium" AnchorX="0.5" AnchorY="0.5"></Button>
</StackLayout>
<Entry x:Name="AnnotationEditor" HorizontalOptions="FillAndExpand" BackgroundColor="#4D4D4D" TextChanged="Entry_OnTextChanged"/>
<Button Text="Next" Command="{Binding NextPageCommand}" HorizontalOptions="FillAndExpand" />
</StackLayout>
</StackLayout>
</ScrollView>
</base:ExtendedPage.Content>
<base:ExtendedPage.Overlay>
<controls:Tombstone Token="{Binding DeficiencyToken}" BackgroundColor="#444444"></controls:Tombstone>
</base:ExtendedPage.Overlay>
<base:ExtendedPage.ToolbarItems>
<ToolbarItem Text="Details" Order="Primary" Priority="0" Clicked="DetailsClicked"></ToolbarItem>
</base:ExtendedPage.ToolbarItems>
</base:ExtendedPage>
public partial class RoomPage : ExtendedPage
{
public ViewModels.Pages.RoomPage ViewModel => _vm ?? (_vm = BindingContext as ViewModels.Pages.RoomPage);
private ViewModels.Pages.RoomPage _vm;
private bool _buttonExpanded;
public RoomPage()
{
InitializeComponent();
BindingContext = new ViewModels.Pages.RoomPage();
}
protected override void OnAppearing()
{
base.OnAppearing();
ViewModel.OnAppearing();
HandleCaching();
}
private async void HandleCaching()
{
await Task.Run(() =>
{
var pageCache = Services.Caches.Pages.GetInstance();
pageCache.Preload(nameof(InspectionGalleryPage), new InspectionGalleryPage());
});
}
private void DetailsClicked(object sender, EventArgs e)
{
ShowOverlay = !ShowOverlay;
}
private void Entry_OnTextChanged(object sender, TextChangedEventArgs e)
{
ViewModel.RoomAnnotations = e.NewTextValue;
}
private void OnRoomTypeSelected(object sender, GliderItemSelectedEventArgs e)
{
ViewModel.SelectedRoomTypeName = e.Item.ToString();
}
private void OnRoomTypeTapped(object sender, GliderItemTappedEventArgs e)
{
if (e.IsSelected && e.IsSelected == e.LastSelection)
{
PanelView.SwitchView = true;
}
}
private void OnRoomSelected(object sender, GliderItemSelectedEventArgs e)
{
ViewModel.SelectedRoomName = e.Item.ToString();
}
private void OnRoomTapped(object sender, GliderItemTappedEventArgs e)
{
if (e.IsSelected && e.IsSelected == e.LastSelection)
{
PanelView.SwitchView = true;
}
}
private void AnimateHeight(View view, double end)
{
view.Animate("Expander", value => view.HeightRequest = value, view.Height, end, 2, 250, Easing.Linear);
}
private void OnExpandButtonClicked(object sender, EventArgs e)
{
var button = (Button) sender;
if (_buttonExpanded)
{
button.RotateTo(0, 250, Easing.Linear);
AnimateHeight(AnnotationEditor, 45.5);
}
else
{
button.RotateTo(-90, 250, Easing.Linear);
AnimateHeight(AnnotationEditor, 300);
}
_buttonExpanded = !_buttonExpanded;
}
}
As I said "something" is covering your button. Here is the problematic declaration
<AbsoluteLayout>
<ContentView Content="{TemplateBinding BackContent}" BackgroundColor="Transparent" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
<ContentView Content="{TemplateBinding FrontContent}" BackgroundColor="Transparent" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
</AbsoluteLayout>
Your FrontContent ContentView covers BackContent ContentView. When you rotate and hide content you actually working with buttons not views. I found this design a little more complicated than it should be but regardless that was not the question. Below is the solution. When you hide you content (meaning button) also hide your Parent view and show it when you show the button.
private async void SwitchCurrentView()
{
if (_isFrontView)
{
BackContent.IsVisible = true;
((ContentView)BackContent.Parent).IsVisible = true;//************************
FrontContent.Unfocus();
BackContent.Focus();
await Task.WhenAll(
FrontContent.FadeTo(0, 500, Easing.Linear),
BackContent.FadeTo(1, 500, Easing.Linear),
this.RotateYTo(GetRotation(0, 180), 250, Easing.Linear)
);
FrontContent.IsVisible = false;
((ContentView)FrontContent.Parent).IsVisible = false;//******************
BackContentAppeared?.Invoke(this, EventArgs.Empty);
}
else
{
FrontContent.IsVisible = true;
((ContentView)FrontContent.Parent).IsVisible = true;
FrontContent.Focus();
BackContent.Unfocus();
await Task.WhenAll(
FrontContent.FadeTo(1, 500, Easing.Linear),
BackContent.FadeTo(0, 500, Easing.Linear),
this.RotateYTo(GetRotation(180, 180), 250, Easing.Linear)
);
BackContent.IsVisible = false;
((ContentView)BackContent.Parent).IsVisible = false;
FrontContentAppeared?.Invoke(this, EventArgs.Empty);
}
_isFrontView = !_isFrontView;
SwitchView = false;
}

How can I add TwoWay Binding to my CustomControl in WindowsPhone?

I want to use twoway binding in my customcontrol. It's codes;
CustomControl;
<Grid>
<PasswordBox x:Name="passwordB" GotFocus="PasswordBox_GotFocus" LostFocus="PasswordBox_LostFocus" PasswordChanged="passwordB_PasswordChanged" Style="{StaticResource AkbankControlStyleWatermarkPasswordBoxLoginFormInputPasswordBox}"></PasswordBox>
<TextBlock x:Name="lblWaterMark" MouseLeftButtonDown="lblWaterMark_Tapped" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="20,10,20,10" Opacity="0.8" FontFamily="Segoe UI" FontSize="16" Foreground="#FF8E8E8E" FontWeight="SemiBold"></TextBlock>
</Grid>
It's name is WatermarkPasswordTextBox :)
DependencyProperty;
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register(
"PassText",
typeof(string),
typeof(WatermarkPasswordTextBox),
new PropertyMetadata(""));
Properties;
private string _passText = "";
public string PassText
{
get
{
if (passwordB != null)
{
_passText = passwordB.Password;
return _passText;
}
else
{
return String.Empty;
}
}
set
{
if (passwordB != null)
{
SetProperty<string>(ref _passText, value, "PassText");
passwordB.Password = _passText;
passwordB_PasswordChanged(passwordB, null);
}
else
{
SetProperty<string>(ref _passText, value, "PassText");
}
}
}
OnApplyTemplate ;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.SetBinding(
WatermarkPasswordTextBox.PasswordProperty,
new Binding
{
Path = new PropertyPath("PassText"),
Mode = BindingMode.TwoWay,
Source = this
});
}
My Xaml;
<CustomControls:WatermarkPasswordTextBox
PassText="{Binding Password,Mode=TwoWay}"
Padding="5"
x:Name="CustomerPasswordTextBox"
x:FieldModifier="public"
LenghtMax="6"
Watermark="{Binding LocalizedResources.PasswordWatermarkWatermark,Source={StaticResource LocalizedStrings}}"
RelayedKeyUp="CustomerPasswordTextBox_KeyUp"
HorizontalContentAlignment="Left"/>
Error Code;
System.ArgumentException: Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'.
This code is giving runtime error.
Thanks.

how to define events in mvvm model in Silverlight

i have an combox control defined with events in my mainpage.xaml
<Grid x:Name="LayoutRoot">
<ComboBox SelectionChanged="ComboBox_SelectionChanged"></ComboBox>
</Grid>
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
now how do we defined events for combox control in mvvm model .
and how do we bind the collection list to combo box. i am using SL 3
thanks
prince
In your xaml, you can bind the ItemSource and SelectedItem as shown below:
MainPage.xaml
<UserControl x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:App1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.DataContext>
<local:MainPage_ViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White">
<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" SelectionChanged="ComboBox_SelectionChanged" Height="30" Width="100"/>
</Grid>
In the MainPage.xaml.cs, your Selection changed method could just call the method on your ViewModel since you are using SL3:
MainPage.xaml.cs
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private MainPage_ViewModel viewModel
{
get { return this.DataContext as MainPage_ViewModel; }
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.viewModel.SelectionChanged();
}
}
Your ViewModel would have the MyItems collection and the SelectedItem to bind to:
MainPage_ViewModel.cs
public class MainPage_ViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> MyItems
{
get { return myItems; }
set { myItems = value; }
}
private ObservableCollection<string> myItems = new ObservableCollection<string>() { "One", "Two", "Three" };
public string SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; }
}
private string selectedItem = string.Empty;
public void SelectionChanged()
{
//Perform logic that needs to happen when selection changes
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Depending on what you were using your SelectionChanged method for, you may no longer need it since this would bind the SelectedItem to the ViewModel.
Hope this helps!

Resources