I have a list view in Xamarin.Forms and I have set ItemTemplate e.g.
listView.ItemTemplate = new DataTemplate(() =>
{
Image img = new Image();
img.SetBinding(Image.SourceProperty, "ImageUrl");
...so on
}
This works if image is stored in the same project but I have moved image to PCL project and set to "Embedded Resource".
I need to state somehting like below but it doesn't work, how can I achieve this so it will bind "embedded resource" to image control in list item.
img.SetBinding(Image.SourceProperty, "ImageSource.FromResource(ImageUrl)");
Thanks
Specify the assembly that the image exists
ImageSource.FromResource("yourNamespace.imageName.png", typeof(className).GetTypeInfo().Assembly))
Created readonly property in model to return ImageSource and it still uses ImageUrl property to return ImageSource and now I just bind to this new readonly property.
//in model
[JsonIgnore]
public ImageSource ImageUrlSource { get { return ImageSource.FromResource(ImageUrl); } }
listView.ItemTemplate = new DataTemplate(() =>
{
Image img = new Image();
img.SetBinding(Image.SourceProperty, "ImageUrlSource");
...so on
}
Related
I'm trying to bind items to a StackLayout via this documentation:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/bindable-layouts
I don't use XAML, so I obviously need to bind from inside my code, the documentation shows how to do it this way:
IEnumerable<string> items = ...;
var stack = new StackLayout();
BindableLayout.SetItemsSource(stack, items);
But I need to reference a property from withing my ViewModel, set earlier via the View's BindingContext. Could someone please help me doing this?
Something ala (pseudocode):
var stack = new StackLayout() { ... };
stack.SetBinding(StackLayout.ItemsSource, "Items")
I dont want my controller to know anything about the actual viewModel, and the way its suggested, I need to use it in a typed matter, where I should know the ViewModel.
Below is an example of what I'm trying to accomplish. Please note I use NO XAML at all! Write all my UI in code:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace LayoutTest
{
public class MyPage : ContentPage
{
public MyPage()
{
BindingContext = new MyViewModel();
var layout = new StackLayout();
BindableLayout.SetItemsSource(layout, "?????");
BindableLayout.SetItemTemplate(layout, new DataTemplate(() =>
{
var lbl = new Label();
lbl.SetBinding(Label.TextProperty, "Name");
return lbl;
}));
Content = layout;
}
}
public class MyViewModel
{
List<Item> Items { get; set; }
public MyViewModel()
{
Items = new List<Item>();
Items.Add(new Item { Name = "Kent" });
Items.Add(new Item { Name = "Tony" });
Items.Add(new Item { Name = "Allan" });
}
}
public class Item
{
public string Name { get; set; }
}
}
Dont know what to write in this line:
BindableLayout.SetItemsSource(layout, "?????");
It only take a collection as a property, but then I need to know about the ViewModel, but I dont want that.
What to do?
layout.SetBinding(BindableLayout.ItemsSourceProperty, new Binding("Items”));
Do that instead of the BindableLayout.SetItemsSource that you have now. The binding will use the existing binding context when setting this.
#KenFonager from code behind you could do this as simple as that.
BindingContext = new ViewModels.MainPages.LiveStreamViewModel();
closeButton.SetBinding(Button.CommandProperty, "BackCommand");
I'm using the mvvm approach to develop a barcode scanning app with xamarin. The main hurdle was that the 3rd party scanner object does not work in xaml. I used a ContentPage to create a simple logic-less c# code view which allows me to have a footer with buttons and a logo overlayed at the bottom of the scanner. My problem is that could not find any great best practices for binding items from your code view to your viewModel, as opposed binding a xaml view to a viewModel. Here is some of my view below.
public class BarcodeScannerPage : ContentPage
{
ZXingScannerView zxing;
BarcodeViewModel viewModel;
public BarcodeScannerPage() : base()
{
try
{
viewModel = new BarcodeViewModel();
BindingContext = viewModel;
zxing = new ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Options = new MobileBarcodeScanningOptions
{
TryHarder = true,
DelayBetweenContinuousScans = 3000
},
ScanResultCommand = viewModel.GetResult
};
var cancelButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Cancel",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.CancelButton
};
Binding cancelBinding = new Binding
{
Source = viewModel.CancelIsAvailable,
//Path = "ShowCancel",
Mode = BindingMode.OneWay,
};
cancelButton.SetBinding(IsVisibleProperty, cancelBinding);
var doneButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Done",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.DoneButton
};
Binding doneBinding = new Binding
{
Source = viewModel.DoneIsAvailable,
//Path = "ShowDone",
Mode = BindingMode.OneWay,
};
doneButton.SetBinding(Button.IsVisibleProperty, doneBinding);
When a barcode is scanned my command, GetResultCommand, sends the result to my BarcodeView model. I have created two Bools in my BarcodeView model named isDoneAvailable and isCancelAvailable. I want to bind these values to the Visibility property of the doneButton and cancelButton in my view. Right now the buttons are bound to whatever the bool values are at the creation of BarcodeViewModel, but they DO NOT update. I need to be able to control visibility from the GetResultCommand method of my BarcodeViewModel. Specifically, when a certain number of barcodes are scanned, I want to make the buttons appear and disappear. I have a feeling they don't update because the path is not set, but when I uncomment the path, the binding doesn't work at all. Any ideas what I've done wrong with the bindings of the buttons, or the correct way to set the Path to my bools in the viewModel? Here is some of my BarcodeViewModel code below.
public class BarcodeViewModel : INotifyPropertyChanged
{
public bool CancelIsAvailable { get { return _cancelIsAvailable; } set { _cancelIsAvailable = value; OnPropertyChanged("ShowCancel"); } }
public bool DoneIsAvailable { get { return _doneIsAvailable; } set { _doneIsAvailable = value; OnPropertyChanged("ShowDone"); } }
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
I still would like to know the correct way to get this binding to update but, I was able to work-around this issue by creating a button in my viewModel and referencing it in my view. Then when I dynamically updated the button in my viewModel, it also updated in my view.
i'm trying to create an image view for a watch app, i'm trying to create programmatically but the code used in a classic view controller doesn't work.
let imageName = "yourImage.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
view.addSubview(imageView)
how i can do the same thing for watch kit?
thanks
You can't dynamically create views in WatchKit. You need to create your entire interface in a storyboard. You can have elements of your storyboard hidden and then programmatically unhide them.
In your storyboard you can use WKInterfaceImage to hold the image. You can set the image at runtime using setImage:.
With SwiftUI, you can add a new image view to your view by using the ForEach and Image components.
struct ContentView: View {
#State var images = [UIImage]()
var body: some View {
VStack {
ForEach(images, id: \.self) { image in
Image(uiImage: image)
}
Button("Add Image") {
images.append(.init(imageLiteralResourceName: "image-name"))
}
}
}
}
From the examples at Xamarin.com you can build basic M.T. Dialog apps, but how do you build a real life application?
Do you:
1) Create a single DialogViewController and tree every view/RootElement from there or,
2) Create a DialogViewController for every view and use the UINavigationController and push it on as needed?
Depending on your answer, the better response is how? I've built the example task app, so I understand adding elements to a table, click it to go to the 'next' view for editing, but how to click for non-editing? How to click a button, go next view if answer is number 1?
Revised:
There is probably no one right answer, but what I've come up with seems to work for us. Number 2 from above is what was chosen, below is an example of the code as it currently exists. What we did was create a navigation controller in AppDelegate and give access to it throughout the whole application like this:
public partial class AppDelegate : UIApplicationDelegate
{
public UIWindow window { get; private set; }
//< There's a Window property/field which we chose not to bother with
public static AppDelegate Current { get; private set; }
public UINavigationController NavController { get; private set; }
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
Current = this;
window = new UIWindow (UIScreen.MainScreen.Bounds);
NavController = new UINavigationController();
// See About Controller below
DialogViewController about = new AboutController();
NavController.PushViewController(about, true);
window.RootViewController = NavController;
window.MakeKeyAndVisible ();
return true;
}
}
Then every Dialog has a structure like this:
public class AboutController : DialogViewController
{
public delegate void D(AboutController dvc);
public event D ViewLoaded = delegate { };
static About about;
public AboutController()
: base(about = new About())
{
Autorotate = true;
about.SetDialogViewController(this);
}
public override void LoadView()
{
base.LoadView();
ViewLoaded(this);
}
}
public class About : RootElement
{
static AboutModel about = AboutVM.About;
public About()
: base(about.Title)
{
string[] message = about.Text.Split(...);
Add(new Section(){
new AboutMessage(message[0]),
new About_Image(about),
new AboutMessage(message[1]),
});
}
internal void SetDialogViewController(AboutController dvc)
{
var next = new UIBarButtonItem(UIBarButtonSystemItem.Play);
dvc.NavigationItem.RightBarButtonItem = next;
dvc.ViewLoaded += new AboutController.D(dvc_ViewLoaded);
next.Clicked += new System.EventHandler(next_Clicked);
}
void next_Clicked(object sender, System.EventArgs e)
{
// Load next controller
AppDelegate.Current.NavController.PushViewController(new IssuesController(), true);
}
void dvc_ViewLoaded(AboutController dvc)
{
// Swipe location: https://gist.github.com/2884348
dvc.View.Swipe(UISwipeGestureRecognizerDirection.Left).Event +=
delegate { next_Clicked(null, null); };
}
}
Create a sub-class of elements as needed:
public class About_Image : Element, IElementSizing
{
static NSString skey = new NSString("About_Image");
AboutModel about;
UIImage image;
public About_Image(AboutModel about)
: base(string.Empty)
{
this.about = about;
FileInfo imageFile = App.LibraryFile(about.Image ?? "filler.png");
if (imageFile.Exists)
{
float size = 240;
image = UIImage.FromFile(imageFile.FullName);
var resizer = new ImageResizer(image);
resizer.Resize(size, size);
image = resizer.ModifiedImage;
}
}
public override UITableViewCell GetCell(UITableView tv)
{
var cell = tv.DequeueReusableCell(skey);
if (cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, skey)
{
SelectionStyle = UITableViewCellSelectionStyle.None,
Accessory = UITableViewCellAccessory.None,
};
}
if (null != image)
{
cell.ImageView.ContentMode = UIViewContentMode.Center;
cell.ImageView.Image = image;
}
return cell;
}
public float GetHeight(UITableView tableView, NSIndexPath indexPath)
{
float height = 100;
if (null != image)
height = image.Size.Height;
return height;
}
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
//base.Selected(dvc, tableView, path);
tableView.DeselectRow(indexPath, true);
}
}
#miquel
The current idea of a workflow is an app that starts with a jpg of the Default.png that fades into the first view, with a flow control button(s) that would move to the main app. This view, which I had working previous to M.T.D. (MonoTouch.Dialog), which is a table of text rows with an image. When each row is clicked, it moves to another view that has the row/text in more detail.
The app also supports in-app-purchasing, so if the client wishes to purchase more of the product, then switch to another view to transact the purchase(s). This part was the main reason for switching to M.T.D., as I thought M.T.D. would be perfect for it.
Lastly there would be a settings view to re-enable purchases, etc.
PS How does one know when the app is un-minimized? We would like to show the fade in image again.
I have been asking myself the same questions. I've used the Funq Dependency Injection framework and I create a new DialogViewController for each view. It's effectively the same approach I've used previously developing ASP.NET MVC applications and means I can keep the controller logic nicely separated. I subclass DialogViewController for each view which allows me to pass in to the controller any application data required for that particular controller. I'm not sure if this is the recommended approach but so far it's working for me.
I too have looked at the TweetStation application and I find it a useful reference but the associated documentation specifically says that it isn't trying to be an example of how to structure a MonoTouch application.
I use option 2 that you stated as well, it works pretty nicely as you're able to edit the toolbar options on a per-root-view basis and such.
Option 2 is more feasible, as it also gives you more control on each DialogViewController. It can also helps if you want to conditionally load the view.
I´m writing on a webpart for sharepoint, so I have to generate a Datagrid problematically.
The Situation is that I get a Dataview, generate the Gris and bind the Data.
One column should show a Image, so I have to generate a template column with item template.
So code looks like this:
//Instantiate the DataGrid, and set the DataSource
_grdResults = new DataGrid();
_grdResults.AutoGenerateColumns = false;
_grdResults.DataSource = view;
TemplateColumn colPic = new TemplateColumn();
colPic.HeaderText = "Image";
I found dozens of example for asp to create the item-template, but how construct one in code and bind it´s ImageUrl to "imgURL" of the Dataview?
thanks for any advice
Ren
You need to create a class that implements that ITemplate interface.
public class TemplateImplementation : ITemplate
{
public void InstantiateIn(Control container)
{
Image image = new Image();
image.DataBinding += Image_DataBinding;
container.Controls.Add(image);
}
void Image_DataBinding(object sender, EventArgs e)
{
Image image = (Image)sender;
object dataItem = DataBinder.GetDataItem(image.NamingContainer);
// If the url is a property of the data item, you can use this syntax
//image.ImageUrl = (string)DataBinder.Eval(dataItem, "ThePropertyName");
// If the url is the data item then you can use this syntax
image.ImageUrl = (string)dataItem;
}
}
You then set your ItemTemplate to an instance of this class.
colPic.ItemTemplate = new TemplateImplementation();