TapGesture in Xamarin From Bound property - c#

OK, I have a ViewCell class that is bound to a viewModel. If I create a label I can do something like this:
var taskName = new Label()
{
XAlign = TextAlignment.Start,
YAlign = TextAlignment.Start,
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold,
LineBreakMode = LineBreakMode.TailTruncation
};
taskName.SetBinding(Label.TextProperty, "CompanyName");
What I want to do is bind the The value of the object to a tapGesture.
var tapGesturePhone = new TapGestureRecognizer ();
var phoneIcon = new Image {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Source = "phone.png"
};
phoneIcon.GestureRecognizers.Add (tapGesturePhone);
tapGesturePhone.Tapped += (sender, e) => {
var uri = new Uri("tel:" + );
Device.OpenUri(uri);
};
//uri.setbinding("Uri.text", "PhoneNo")?
Any Ideas as to how I can do this? a work around would be fine too.
I have a model named ProjectContacts With a ProjectContactsViewModel set as its BindingContext.
My ContentView looks like this:
public ProjectContactsView ()
{
//bind the view model to the context
this.BindingContext = _viewModel;
_list.ItemTemplate = new DataTemplate(typeof(ProjectContactsListCell));
_list.IsGroupingEnabled = true;
_list.GroupDisplayBinding = new Binding("Key");
_list.GroupHeaderTemplate = new DataTemplate(typeof(ProjectContactsListHeaderCell));
_list.HasUnevenRows = true;
}
Solution
Thanks to #Sten Petrov I managed to fix this. I ended up having to declare a new instance of my ProjectContactsViewModel inside my ViewCell
private TapGestureRecognizer tapGesturePhone = new TapGestureRecognizer ();
tapGesturePhone.BindingContext = new ProjectContactsViewModel ();
Then I bound my TapGestureRecognizer in the ViewCell this way:
tapGesturePhone.SetBinding<ProjectContactsViewModel> (TapGestureRecognizer.CommandProperty, vm => vm.DialPhoneCommand);
tapGesturePhone.SetBinding<ProjectContactsViewModel> (TapGestureRecognizer.CommandParameterProperty,vm => vm.PhoneNo);
From Here I just had to Create a Setter and Getter for the ViewModel and init the Command in the constructor:
public Command DialPhoneCommand {get;set;}
public string PhoneNo { get {return "http://google.com"; } set{ PhoneNo = value;}}
public ProjectContactsViewModel ()
{
//Assign the dialcommand.
DialPhoneCommand = new Command((phoneNo)=>
Device.OpenUri(new Uri(phoneNo.ToString())));
}

You can create a Command in your ProjectContactsViewModel and bind the tapGesture.Command to that.
The same way you bind
taskName.SetBinding(Label.TextProperty, "CompanyName");
you can bind the command of a tap gesture
var tapGesturePhone = new TapGestureRecognizer ();
tapGesturePhone.SetBinding(TapGestureRecognizer.CommandProperty,"DialPhoneCommand");
tapGesturePhone.SetBinding(TapGestureRecognizer.CommandParameterProperty,"PhoneNo");
var phoneIcon = new Image {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Source = "phone.png"
};
phoneIcon.GestureRecognizers.Add (tapGesturePhone);
The command could be initialized in ProjectContactsViewModel (for simplicity):
... ProjectContactsViewModel {
public string PhoneNo {get;set;}
public Command DialPhoneCommand {get;set;}
...
public ProjectContactsViewModel(){
DialPhoneCommand = new Command((phoneNo)=>
Device.OpenUri(new Uri("tel:" + phoneNo ));
);
}
}

Related

My code-generated ListBox's items isn't showing any content

I have a code-generated window in WPF, (using dotnet core 3.0 preview 6), and when running my application gets the correct data, and the ListBox is populated with the correct number of rows, but none of them contains values
This is a test-project I'm doing to get familiar with code generated WPF as it's needed for an upcoming project we're doing at work; I would have preferred using XAML but my lead says that that will create issues with code-re-usability.
I at first made sure I used am object which is "clean", (my entities are setup for Linq2db, so I ensured that the attributes couldn't be the culprit), then I tested binding, (just got the "Error 40" -error code, but that isn't relevant to the main problem). I have also changed the type of boxes, but It does not help, (DataGrid did work but it's not what I'm looking for in a visual).
public class ChatWindow : IChatWindow
{
private ObservableCollection<MessageDto> _observableMessages;
private readonly IMessagesRepository _messagesRepository;
public ChatWindow(IMessagesRepository messagesRepository)
{
_messagesRepository = messagesRepository;
Task.Run(async () => { await Updater(); }).ConfigureAwait(false);
}
public async Task ShowAsync(User user)
{
var chatLog = new ListBox()
{
Name = "Chatview",
ItemsSource = _observableMessages,
ItemTemplate = new DataTemplate(typeof(MessageDto)),
DataContext = _observableMessages
};
//var myBinding = new Binding("_observableMessages");
//myBinding.Source = _observableMessages;
//chatLog.SetBinding(ListBox.ItemsSourceProperty, myBinding);
var input = new TextBox()
{
Name = "InputField",
Background = new SolidColorBrush(Color.FromRgb(35, 35, 35))
};
var stackPanel = new StackPanel()
{
Children =
{
chatLog,
input
}
};
var window = new Window()
{
Name = "ChatWindow",
Content = stackPanel,
};
window.Show();
}
private async Task Updater()
{
while (true)
{
var messages = await _messagesRepository.GetAllMessages(1);
_observableMessages = new ObservableCollection<MessageDto>(messages.Select(m => new MessageDto()
{
Timestamp = m.Timestamp,
From = m.From,
Message = m.Message
}));
await Task.Delay(TimeSpan.FromSeconds(10));
}
}
}
class MessageDto
{
public DateTime Timestamp { get; set; }
public long From { get; set; }
public string Message { get; set; }
}
Image of the resultant window, (some styling code was removed from the example code to reduce noise)
Based on Flithor's comment, i did this and it worked perfectly:
private DataTemplate GetDataTemplate()
{
var dataTemplate = new DataTemplate(typeof(MessageDto));
FrameworkElementFactory stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory timestamp = new FrameworkElementFactory(typeof(Label));
timestamp.SetBinding(Label.ContentProperty, new Binding("Timestamp"));
FrameworkElementFactory from = new FrameworkElementFactory(typeof(Label));
from.SetBinding(Label.ContentProperty, new Binding("From"));
FrameworkElementFactory message = new FrameworkElementFactory(typeof(Label));
message.SetBinding(Label.ContentProperty, new Binding("Message"));
stackPanelFactory.AppendChild(timestamp);
stackPanelFactory.AppendChild(from);
stackPanelFactory.AppendChild(message);
dataTemplate.VisualTree = stackPanelFactory;
return dataTemplate;
}

Binding Grid ColumnSpan in ListView

In ListView I want to bind a grid columnSpan and then set it this span in ItemSource object. This is the way I am trying to achieve this but i get:
,,Object reference not set to an instance of an object". I would like to know what is a proper way of doing it and also if it is needed to use any ValueConverter to set span property.
"Title" Text property is not important for now.
ViewCell:
class CustomPerformanceCell : ViewCell
{
public CustomPerformanceCell()
{
var titleLabel = new Label();
titleLabel.SetBinding(Label.TextProperty , "Title");
var resultGrid = new Grid
{
HorizontalOptions = LayoutOptions.FillAndExpand
};
resultGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
resultGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
resultGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
resultGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
resultGrid.BackgroundColor = Color.DarkGray;
var ColorBox = new BoxView();
ColorBox.SetBinding(VisualElement.BackgroundColorProperty, "BackgroundColor", BindingMode.Default, new BackgroundConverter(), null);
resultGrid.Children.Add(ColorBox ,0,0);
ColorBox.SetBinding(Grid.ColumnSpanProperty, "Performance");
}
}
PerformanceCell:
public class PerformanceCellItem
{
public string Title { get; set; }
public int Performance { get; set; }
public string BackgroundColor { get; set; }
public PerformanceCellItem(string title, int performance ,string backgroundColor)
{
Title = title;
Performance = performance;
BackgroundColor = backgroundColor;
}
}
Function for creating it:
public void PreparePerformanceStack()
{
PerformanceList = new ListView
{
ItemTemplate = new DataTemplate(typeof(CustomPerformanceCell))
};
ObservableCollection<PerformanceCellItem> cellItems = new ObservableCollection<PerformanceCellItem>
{
new PerformanceCellItem("test 1", 2,"#00AA00"),
new PerformanceCellItem("test 2", 2,"#00AA00"),
new PerformanceCellItem("test 3", 2,"#00AA00")
};
PerformanceList.ItemsSource = cellItems;
}
ColumnSpan can't be set to Grid unless there is a parent grid; if you want to set it to BoxView - use colorBox.SetBinding(Grid.ColumnSpanProperty, "Performance").
Also, do make sure that the value of Performance is >= 1 (otherwise coerce condition will throw an exception). You don't need a value-converter, as long as source type is either int or a parse-able string.
EDIT 1 - as per question edit
You need assign View property in your custom view-cell
this.View = resultGrid;
in your constructor CustomPerformanceCell()

Xamarin Forms add input field to each item of listview

I'm new to Xamarin Forms and I would want to add a input field (Numeric) on the right side of each item in my listview, so that I can type a quanty to my items. Somthing like a shopping list of items with the quantity of how many you want.
here is my code of my listview
List<string> item = new List<string>();
public MainPage()
{
InitializeComponent();
item.Add("Apple");
item.Add("Banana");
item.Add("Graps");
item.Add("Orange");
item.Add("Pineapple");
item.Add("Strawberry");
item.Add("Lemon");
item.Add("Mango");
item.Add("Cherry");
item.Add("Watermelon");
item.Add("Add");
var listView = new ListView
{
RowHeight = 40
};
listView.ItemsSource = item;
StackLayout layout = new StackLayout();
layout.Children.Add(listView);
listView.ItemSelected += (sender, e) =>
{
if (e.SelectedItem.ToString() == "Add")
{
var MyEntry = new Entry { Placeholder = "new item" };
layout.Children.Add(MyEntry);
MyEntry.Completed += MyEntry_Completed;
}
};
this.Content = layout;
}
private void MyEntry_Completed(object sender, EventArgs e)
{
var text = ((Entry)sender).Text;
item.Add(text);
}
I had a lot of trouble wrapping my head around databinding from the C# side in Xamarin Forms. I would recommend you checkout DataBinding to get a better understanding of this. I created a custom ViewCell for you to play around with, and a basic implementation so you can get a visual of how to implement it. You can adjust the view cell as much as needed. I just wanted to give you a bases of how to implement it. Hope this helps.
ViewCell
This is the view that displays for each list item in the list view.
using Xamarin.Forms;
namespace BountyApp.Controls
{
public class CustomViewCell : ViewCell
{
private Grid _grid = new Grid();
private Label _lbl = new Label() { HorizontalTextAlignment = TextAlignment.End, VerticalTextAlignment = TextAlignment.Center };
private Entry _entry = new Entry();
public CustomViewCell()
{
_lbl.SetBinding(Label.TextProperty, new Binding("Title"));
_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.3, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.7, GridUnitType.Star) });
_grid.Children.Add(_lbl, 0, 0);
_grid.Children.Add(_entry, 1, 0);
View = _grid;
}
}
}
Implementing ViewCell
using Xamarin.Forms;
namespace BountyApp.Pages
{
public class ViewModel
{
public string Title { get; set; }
}
public class StepperPage : ContentPage
{
public ObservableCollection<ViewModel> List { get; set; }
public StepperPage()
{
List = new ObservableCollection<ViewModel>();
List.Add(new ViewModel { Title = "Apple" });
List.Add(new ViewModel { Title = "Banana" });
List.Add(new ViewModel { Title = "Graps" });
List.Add(new ViewModel { Title = "Orange" });
List.Add(new ViewModel { Title = "Pineapple" });
List.Add(new ViewModel { Title = "Strawberry" });
var listView = new ListView
{
RowHeight = 40,
ItemTemplate = new DataTemplate(typeof(CustomViewCell)),
ItemsSource = List
};
Content = listView;
}
}
}
Result

Add image to ListView C# WPF (without xaml)

How can I make an image item in ListView? I need to do it without xaml, only C#. I was surfing about 40 min about it and have nothin found.
I've tried a lot of things and i stopped here
in first column i see just "System.Windows.Controls.StackPanel".
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
GridView grView = new GridView();
GridViewColumn gwCol1 = new GridViewColumn();
GridViewColumn gwCol2 = new GridViewColumn();
gwCol1.Header = "Avatar";
gwCol2.Header = "Name";
gwCol2.DisplayMemberBinding = new Binding("name");
gwCol1.DisplayMemberBinding = new Binding("stack");
grView.Columns.Add(gwCol1);
grView.Columns.Add(gwCol2);
listView1.View = grView;
//
var list = new List<XData>();
listView1.ItemsSource = list;
//
BitmapImage image1 = new BitmapImage(new Uri(#"C:\Users\montana\OneDrive\Pictures\Saved Pictures\WVQfZqY.jpg"));
Image img = new Image();
img.Width = 100;
img.Height = 100;
img.Source = image1;
StackPanel stackPanel1 = new StackPanel();
stackPanel1.Orientation = Orientation.Horizontal;
stackPanel1.Children.Add(img);
list.Add(new XData() { name="hola", stack=stackPanel1 });
}
}
class XData
{
public string name { get; set; }
// public string url { get; set; }
public StackPanel stack { get; set; }
}
Here is something what produce wanted result.
Change XData definition to avoid having view elements there (you didn't mention MVVM, but it's good to stick to):
class XData
{
public string Name { get; set; }
public string Path { get; set; }
}
Now you can either define data template in xaml (common solution) or generate it in code behind like this:
var factory = new FrameworkElementFactory(typeof(Image));
factory.SetValue(Image.SourceProperty, new Binding(nameof(XData.Path)));
factory.SetValue(Image.WidthProperty, 100.0);
factory.SetValue(Image.HeightProperty, 100.0);
var dataTemplate = new DataTemplate { VisualTree = factory };
Data template can be as complicated as you like, but obviously defining it in xaml and then loading with FindResource() is way easier, consider to use this option instead.
And then this datatemplate has to be specified as CellTemplate like this:
GridView grView = new GridView();
grView.Columns.Add(new GridViewColumn { Header = "Avatar", CellTemplate = dataTemplate });
grView.Columns.Add(new GridViewColumn { Header = "Name", DisplayMemberBinding = new Binding(nameof(XData.Name)) });
listView1.View = grView;
Note: you shouldn't use DisplayMemberBinding for column with CellTemplate.
Now, finally, you can fill ListView by setting its ItemSource as usual:
var list = new List<XData>();
list.Add(new XData { Name = "hola", Path = #"c:\temp\1.jpg" });
list.Add(new XData { Name = "hola2", Path = #"c:\temp\2.png" });
listView1.ItemsSource = list;

How to change Font/Color/Size in Xamarin C#

I'm making an Hybrid application in C#/Xamarin, and i want to make a custom menu for all applications(iOS, Android, Windows Phone).
So, I create a MasterPage to be my Menu.
public MasterPage()
{
InitializeComponent();
var masterPageItems = new List<MenuItem>();
masterPageItems.Add(new MenuItem
{
Title = "AdministraĆ§Ć£o",
});
masterPageItems.Add(new MenuItem
{
Title = "Meus Dados",
IconSource = "contacts.png",
TargetType = typeof(MeusDados),
});
masterPageItems.Add(new MenuItem
{
Title = "Dados Cadastrais",
IconSource = "contacts.png",
TargetType = typeof(MeusNegocios),
});
var listView = new ListView
{
ItemsSource = masterPageItems,
ItemTemplate = new DataTemplate(() =>
{
var imageCell = new ImageCell();
imageCell.SetBinding(TextCell.TextProperty, "Title");
imageCell.SetBinding(ImageCell.ImageSourceProperty, "IconSource");
return imageCell;
}),
VerticalOptions = LayoutOptions.FillAndExpand,
SeparatorVisibility = SeparatorVisibility.None
};
Padding = new Thickness(0, 20, 0, 0);
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Fill,
Children = {
listView
}
};
}
This is the MenuItem:
public class MenuItem
{
public string Title { get; set; }
public string IconSource { get; set; }
public Type TargetType { get; set; }
public string Parameter { get; set; }
}
So now I want to change the size of content page, font, font color, font size in C#. How should I do that?
Xamarin Forms doc on Fonts:
Fonts: https://developer.xamarin.com/guides/xamarin-forms/user-interface/text/fonts/
Example:
var about = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold,
Text = "Medium Bold Font"
};
I do note that you are using an ImageCell, which does not have the Font properties but only a TextColor and DetailColor property. Also there are no properties to get the underlying Labels in the ImageCell, so your best bet if you want full customization is to create your own ViewCell and add the Image and the Labels to the ViewCell. Then you can style your Labels with the Font properties.
Alternately, you can use Themes, which is in Preview: https://developer.xamarin.com/guides/xamarin-forms/themes/
StyleClass
The StyleClass property allows a view's appearance to be changed according to a definition provided by a theme.

Categories