I am working on a Xamarin Mac application and am currently creating a newsfeed list.
What I would like to have is that when the user click on one of the row I do some stuff (open up a browser and show the full story).
Here is how my custom cell (row) looks like.
public class CustomLatestNewsCell : NSView
{
public NSText Title;
public NSText Date;
public NSText Details;
public CustomLatestNewsCell()
{
Title = new NSText();
Title.Frame = new CoreGraphics.CGRect(0, 120, 300, 70);
Title.TextColor = NSColor.FromSrgb((nfloat)0.207, (nfloat)0.576, (nfloat)0.639, (nfloat)1);
Title.Font = NSFont.UserFontOfSize(20);
Title.Editable = false;
Title.Selectable = false;
this.AddSubview(Title);
Date = new NSText();
Date.Frame = new CoreGraphics.CGRect(0, 25, 300, 30);
Date.TextColor = NSColor.FromSrgb((nfloat)0.702, (nfloat)0.702, (nfloat)0.702, (nfloat)1);
Date.Editable = false;
Date.Selectable = false;
this.AddSubview(Date);
Details = new NSText();
Details.Frame = new CoreGraphics.CGRect(310, 120, 550, 32);
Details.TextColor = NSColor.FromSrgb((nfloat)0.5, (nfloat)0.5, (nfloat)0.5, (nfloat)1);
Details.Editable = false;
Details.Selectable = false;
this.AddSubview(Details);
this.Frame = new CoreGraphics.CGRect(0, 0, 850, 150);
}
}
I have tried a lot of things but there doesn't seem to be a click/activated/somethingElse function which will trigger when I click on anything in the cell. The only thing that works is
public override void SelectionIsChanging(NSNotification notification)
{
var row = (NSTableView)notification.Object;
var selectedRow = row.SelectedRow;
if(selectedRow < 0)
return;
var selectedFeed = feed[(int)selectedRow];
row.DeselectAll(row);
NSWorkspace ws = new NSWorkspace();
NSError error = new NSError();
ws.OpenURL(new NSUrl(selectedFeed.Url),
NSWorkspaceLaunchOptions.AllowingClassicStartup,
new NSDictionary(),
out error);
}
but in this case the user has to click on some "empty" parts of the row. Is there a way that I catch a click on any of the parts in the cell and then trigger the action?
I am quite new to Mac development so I am quite lost at the moment
Related
First of all i have seen few topics about that but didnt helped i typed thoose in xamarin.
i add a button in that way
Button desk = new Button
{
Text = desknumber.ToString(),
Font = Font.SystemFontOfSize(NamedSize.Large),
BorderWidth = 1,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
so i have to run this command in loop or in a method like that
public void masaekle(int masasayisi)
{
Button desknumber = new Button
{
Text = masasayisi.ToString(),
Font = Font.SystemFontOfSize(NamedSize.Large),
BorderWidth = 1,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
this.Content = new StackLayout
{
Children =
{
desknumber,
}
};
it doesnt work in any variable type so i stuck here i cant give a variable name to the button
every time you assign Content it overwrites what is already there
var sl = new StackLayout();
// some loop conditions
for(;;)
{
var button = new Button { ... };
sl.Children.Add(button);
}
this.Content = sl;
How do I bind a value from my object property to a variable so that I can pass it as an argument/parameter for my method "setFavoriteProduct(id_product, favnumber) which is triggered by my button "choiceButton". I require the property value "id_product".
When I bind it to Label "id_productLabel" then I can see it in the View just fine but I need to use the string value in my "setFavoriteProduct" function.
listView.ItemTemplate = new DataTemplate(() =>
{
Image productImage = new Image();
productImage.SetBinding(Image.SourceProperty, "imageURL");
//productImage.HeightRequest = 60;
productImage.HeightRequest = 120;
//productImage.WidthRequest = 60;
productImage.WidthRequest = 120;
productImage.Aspect = Aspect.AspectFit;
Label nameLabel = new Label();
nameLabel.SetBinding(Label.TextProperty, "name");
Label id_productLabel = new Label();
id_productLabel.SetBinding(Label.TextProperty, "id_product");
id_productLabel.IsVisible = true;
Button choiceButton = new Button();
choiceButton.Text = "Vali";
choiceButton.Clicked += (sender, args) => setFavoriteProduct(
id_product,
favnumber
);
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(0, 5),
Orientation = StackOrientation.Horizontal,
Children = { productImage, id_productLabel , nameLabel, choiceButton }
}
};
the simplest way to do this is to get the BindingContext from the sender (button) and then get the property from that
choiceButton.Clicked += (sender, args) => {
var context = (MyClass)((Button)sender).BindingContext;
var id = context.id_product;
setFavoriteProduct(id,favnumber);
};
alternately, you could use the Button's Command and CommandParameter properties instead of the Clicked event
How do i get row id of grid row selection? I want to add row unique id and i want it on tapped or on clicked. please suggest me if anyone have any idea.
I want to do some thing like this, if i click on the row and i get id. with this id i will fetch another records and send user on another page. for this i need unique id of the row.
i have grid like bellow:
Below is the code i use:
public BusList(Common busdata)
{
InitializeComponent();
this.BindingContext = this;
List<BusDetail> Bus = new List<BusDetail>
{
...
new BusDetail("Nita Travel","Mumbai", "Navsari",new DateTime(2017, 3, 9), "10:15 AM", "09:15 AM")
};
Label header = new Label
{
Text = "GridView Demo",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
};
var controlGrid = new Grid
{
RowSpacing = 2,
ColumnSpacing = 2,
Margin = new Thickness(5, Device.OnPlatform(5, 0, 0), 5, 5)
};
controlGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) });
for (int i = 0; i < Bus.Count(); i++)
{
controlGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) });
}
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
Bus = Bus.Where(x => x.FromDest.ToLower().Contains(busdata.FromDest.ToLower()) && x.FromDate == busdata.FromDate).ToList();
controlGrid.Children.Add(new Label { Text = "ID", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 0, 0);
controlGrid.Children.Add(new Label { Text = "Travel Name", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 1, 0);
controlGrid.Children.Add(new Label { Text = "Arr Time", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 2, 0);
controlGrid.Children.Add(new Label { Text = "Dep Time", Font = Font.SystemFontOfSize(NamedSize.Large).WithAttributes(FontAttributes.Bold), BackgroundColor = Color.Gray }, 3, 0);
for (int i = 0; i < Bus.Count(); i++)
{
var clickableRow = new ContentView();
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "RowTappedCommand");
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, i.ToString());
clickableRow.GestureRecognizers.Add(tapGestureRecognizer);
clickableRow.BindingContext = i.ToString();
controlGrid.Children.Add(clickableRow, 0, i);
Grid.SetColumnSpan(clickableRow, 3);
controlGrid.Children.Add(new Label { Text = Bus[i].TravelName, BindingContext = Bus[i].TravelName, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 1, i + 1);
controlGrid.Children.Add(new Label { Text = Bus[i].ArrivalTime, BindingContext = Bus[i].ArrivalTime, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 2, i + 1);
controlGrid.Children.Add(new Label { Text = Bus[i].DepartureTime, BindingContext= Bus[i].DepartureTime, BackgroundColor = Color.LightGray, VerticalTextAlignment = TextAlignment.Center }, 3, i + 1);
}
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
this.Content = new StackLayout
{
Children =
{
header,
controlGrid
}
};
}
public void RowTappedCommand(string index)
{
var tappedRow = int.Parse(index);
}
You could add a ContentView to each row on the first column and span it across all columns by using the ColumnSpan property. Then you'd handle the taps with a TouchGestureListener. When creating the Command, you'd also include a CommandParameter which contains the index of the row in question.
Outside the constructor, define this:
public ICommand RowTappedCommand { get; private set; }
Then add the following code somewhere after InitializeComponent:
RowTappedCommand = new Command<string>(RowTapped);
Then create the clickable ContentView controls for each row:
var clickableRow = new ContentView {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
}
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "RowTappedCommand");
var binding = new Binding();
binding.Source = i.ToString();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, binding); // This is the index of the row
clickableRow.GestureRecognizers.Add(tapGestureRecognizer);
controlGrid.Children.Add(clickableRow, 0, 0);
Grid.SetColumnSpan(clickableRow,3);
You also need to define the method that matches the name defined in the CommandProperty. If you have a view model defined for the view you need to do it there. Otherwise, you'll need to add the method to your view's code behind file (xaml.cs).
void RowTapped(string index)
{
var tappedRow = int.Parse(index);
// Do something with the value
}
Remember to set the BindingContext correctly so that your command gets called. Here's more information about Commands in Xamarin.Forms:
Update: There were a few mistakes that needed to be fixed.
Command wasn't defined correctly.
The binding for the command property needs to be pointing to a static value
Simplifying Events with Commanding
What is the function of Message sender in xamarin.forms? In my app I have cart contain list view and a Grant Total label. Is it possible to update the label using message sender? I can get the total amount from my sqlite db I need to update it to the view.
This is my number picker index change event in view cell
numPicker.SelectedIndexChanged += (sender, args) =>
{
// var price = _cartQuery.GetSum();
sender = BindingContext;
// cm_items item = (cm_items)sender;
if(Int32.Parse(btn_NumBtn.Text)<=1)
{
lbl_Price.Text = ((numPicker.SelectedIndex + 1) * (Int32.Parse(lbl_Price.Text))).ToString();
btn_NumBtn.Text = (numPicker.SelectedIndex + 1).ToString();
}
else
{
int a = Int32.Parse(lbl_Price.Text);
int b = Int32.Parse(btn_NumBtn.Text);
int c = a / b;
lbl_Price.Text = ((numPicker.SelectedIndex + 1) * c).ToString();
btn_NumBtn.Text = (numPicker.SelectedIndex + 1).ToString();
}
_cartQuery.UpdatePicker((BindingContext as CartDB).Cart_Item_Id, numPicker.SelectedIndex + 1, Int32.Parse(lbl_Price.Text));
price = _cartQuery.GetSum();
// App.Instance.ViewModel.TotalAmount = price;
// _cartDB.total = App.Instance.ViewModel.TotalAmount;
Calculate_price();
numPicker.IsEnabled = false;
};
Calculate_price method
public double Calculate_price()
{
try
{
var price = 0;
price = _cartQuery.GetSum();
App.Instance.ViewModel.TotalAmount = price;
return price;
}
catch (Exception ex)
{
throw ex;
}
}
In my view i have a label named grant total, i need to update the total on e number picker change
Label lbl_amnt = new Label
{
// Text = viewModel.Price.ToString(),
// Text=CartCell.price.ToString(),
Text = price.ToString(),
FontSize = 18,
FontAttributes = FontAttributes.Bold,
TextColor = Color.FromRgb(102, 204, 102),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.EndAndExpand,
};
lbl_amnt.SetBinding(Label.TextProperty, "TotalAmount");
update to my post as per the comment from #Grish
In my view model i have this TotalAmount as a property
public double _TotalAmount;
public double TotalAmount
{
get { return _TotalAmount; }
set { _TotalAmount = value; OnPropertyChanged("TotalAmount");}
}
I think the better solution is i notify but the thing is view is not binding
Binding is definitely the answer in your case. I think the problem is that you bind string (label's text) to property of type double.
You should specify IValueConverter or stringFormat parameters in your call to SetBinding.
Check this link:
https://forums.xamarin.com/discussion/19146/binding-to-integers
What I am trying to do is similar to DisplayAlert, popup a display page that contains image, content and a small close button at top right. The display page shouldn't cover the whole phone but just around 80% of the phone UI, background remain as parent page.
I am trying to play around with PushModalAsync and PopModalAsync, however with no luck. The output is not what I expected.
Basically, I have a listview, whenever item is selected from screen it will call for popUpMethod:
list.ItemSelected += MyMethod;
inside MyMethod i will call popUpPage
async void MyMethod(object sender, SelectedItemChangedEventArgs e){
Content = await PopUpPage();
}
and this is my PopUpPage method
private async Task<StackLayout> PopUpPage()
{
StackLayout objPopUp = new StackLayout() { HeightRequest = 100, WidthRequest= 100, VerticalOptions = LayoutOptions.CenterAndExpand};
Label lblMessage = new Label();
lblMessage.Text = "Welcome";
objPopUp.Children.Add(lblMessage);
return objPopUp;
}
I am trying to set the height and width inside my popup page. However, it is still covering the whole screen which is not what I want. let me know if any top up information is needed, thank you.
P/S : i designed it in xamarin.Form(portable)
You can create a custom pop-up to accomplish this in Xamarin.Forms
Here is a custom ContentView that I created. It uses a BoxView to give the appearance of fading the background, and uses a Frame to add a shadow to the pop-up.
I also use animations to make the custom pop-up appear as if it is springing off of the screen!
Sample App
The code for this sample app is available on Github:
https://github.com/brminnick/InvestmentDataSampleApp
Code Snippet
public class WelcomeView : ContentView
{
readonly BoxView _backgroundOverlayBoxView;
readonly Frame _overlayFrame;
readonly StackLayout _textAndButtonStack;
readonly RelativeLayout _relativeLayout;
public WelcomeView()
{
const string titleText = "Welcome";
const string bodyText = "Enjoy InvestmentDataSampleApp";
const string okButtonText = "Ok, thanks!";
var whiteWith75Opacity = new Color(255, 255, 255, 0.75);
_backgroundOverlayBoxView = new BoxView
{
BackgroundColor = whiteWith75Opacity
};
_backgroundOverlayBoxView.Opacity = 0;
_overlayFrame = new Frame
{
HasShadow = true,
BackgroundColor = Color.White
};
_overlayFrame.Scale = 0;
var titleLabel = new Label
{
FontAttributes = FontAttributes.Bold,
Text = titleText,
HorizontalTextAlignment = TextAlignment.Center
};
var bodyLabel = new Label
{
Text = bodyText,
HorizontalTextAlignment = TextAlignment.Center
};
var blackWith75PercentOpacity = new Color(0, 0, 0, 0.75);
var okButton = new Button
{
BackgroundColor = blackWith75PercentOpacity,
TextColor = Color.White,
BorderWidth = 1,
BorderColor = blackWith75PercentOpacity,
FontAttributes = FontAttributes.Bold,
Margin = new Thickness(5),
Text = okButtonText
};
okButton.Clicked += (sender, e) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await this.FadeTo(0);
this.IsVisible = false;
this.InputTransparent = true;
});
}
_textAndButtonStack = new StackLayout
{
HorizontalOptions = LayoutOptions.CenterAndExpand,
Spacing = 20,
Children = {
titleLabel,
bodyLabel,
okButton
}
};
_textAndButtonStack.Scale = 0;
_relativeLayout = new RelativeLayout();
Func<RelativeLayout, double> gettextAndButtonStackHeight = (p) => _textAndButtonStack.Measure(_relativeLayout.Width, _relativeLayout.Height).Request.Height;
Func<RelativeLayout, double> gettextAndButtonStackWidth = (p) => _textAndButtonStack.Measure(_relativeLayout.Width, _relativeLayout.Height).Request.Width;
_relativeLayout.Children.Add(_backgroundOverlayBoxView,
Constraint.Constant(-10),
Constraint.Constant(0),
Constraint.RelativeToParent(parent => parent.Width + 20),
Constraint.RelativeToParent(parent => parent.Height)
);
_relativeLayout.Children.Add(_overlayFrame,
Constraint.RelativeToParent(parent => parent.Width / 2 - gettextAndButtonStackWidth(parent) / 2 - 20),
Constraint.RelativeToParent(parent => parent.Height / 2 - gettextAndButtonStackHeight(parent) / 2 - 10),
Constraint.RelativeToParent(parent => gettextAndButtonStackWidth(parent) + 30),
Constraint.RelativeToParent(parent => gettextAndButtonStackHeight(parent) + 30)
);
_relativeLayout.Children.Add(_textAndButtonStack,
Constraint.RelativeToView(_overlayFrame, (parent, view) => view.X + 15),
Constraint.RelativeToView(_overlayFrame, (parent, view) => view.Y + 15)
);
if (Device.OS == TargetPlatform.Android)
{
_overlayFrame.IsVisible = false;
_textAndButtonStack.BackgroundColor = whiteWith90Opacity;
}
Content = _relativeLayout;
}
public void DisplayView()
{
Device.BeginInvokeOnMainThread(async () =>
{
var animationList = new List<Task>
{
_backgroundOverlayBoxView.FadeTo(1,AnimationConstants.WelcomeViewAnimationTime),
_textAndButtonStack.ScaleTo(AnimationConstants.WelcomeViewMaxSize, AnimationConstants.WelcomeViewAnimationTime),
_overlayFrame.ScaleTo(AnimationConstants.WelcomeViewMaxSize,AnimationConstants.WelcomeViewAnimationTime)
};
await Task.WhenAll(animationList);
animationList = new List<Task>
{
_textAndButtonStack.ScaleTo(AnimationConstants.WelcomeViewNormalSize, AnimationConstants.WelcomeViewAnimationTime),
_overlayFrame.ScaleTo(AnimationConstants.WelcomeViewNormalSize, AnimationConstants.WelcomeViewAnimationTime)
};
await Task.WhenAll(animationList);
});
}
}
PushModalAsync will push page over your current page. Instead use same page add one frame in it. configure frame with whatever controls you want.
1. set frame Visibility to false and on ItemSelected make frame visible. OR
2. add frame dynamically on ItemSelected (haven't tried 2nd approach.).
Change your code like as following.
private async Task<StackLayout> PopUpPage () {
StackLayout objPopUp = new StackLayout () {HeightRequest = 0, WidthRequest = 0, VerticalOptions = LayoutOptions.Center, Padding = 10 };
Label lblMessage = new Label ();
lblMessage.Text = "Welcome";
objPopUp.Children.Add (lblMessage);
return objPopUp;
}
You may use the XLabs-PopUp-Control.
With that control, you can show a PopUp from you page where you can define the size without problems. I use it on various pages.
Link how to use it
Link how to install and setup XLabs