I have created a custom control deriving from Button in WPF. I have overriden DefaultStyleKeyProperty and then set dependency property for Content. This control has its own style for OnMouseEnter, OnMouseLeave , OnMouseDown and OnMouseUp. When used, the events and customized styles appear but content is not shown. How do i set the content?
The control's code is below:
public class SymbolButton : Button
{
public static readonly DependencyProperty ContentProperty;
static SymbolButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SymbolButton), new FrameworkPropertyMetadata(typeof(SymbolButton)));
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.AffectsArrange = true;
ContentProperty = DependencyProperty.Register("Content",
typeof(string), typeof(SymbolButton), new UIPropertyMetadata(null));
}
Point startPoint = new Point(0.5, 0);
Point endPoint = new Point(0.5, 1);
public string Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public SymbolButton()
{
//this.Content = "Symbol";
ButtonDefaultDisplay();
this.BorderThickness = new Thickness(3.3);
this.FontSize = 21;
this.FontStyle = FontStyles.Oblique;
}
private void ButtonDefaultDisplay()
{
this.Foreground = Brushes.BlueViolet;
this.Background = new LinearGradientBrush(Colors.LightBlue,
Colors.MediumAquamarine,
startPoint, endPoint);
this.BorderBrush = Brushes.DarkTurquoise;
}
private void ButtonClickDisplay()
{
this.Foreground = Brushes.LightCyan;
this.Background = new LinearGradientBrush(Colors.MidnightBlue,
Colors.DarkTurquoise,
startPoint, endPoint);
this.BorderBrush = Brushes.Cyan;
}
private void ButtonHighlight()
{
this.Foreground = Brushes.Red;
this.Background = new LinearGradientBrush(Colors.MistyRose,
Colors.LightBlue,
endPoint, startPoint);
this.BorderBrush = Brushes.Plum;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
ButtonClickDisplay();
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
ButtonHighlight();
}
protected override void OnMouseEnter(MouseEventArgs e)
{
ButtonHighlight();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
ButtonDefaultDisplay();
}
}
Related
I am facing the issue with my Recycler view while placing Admob ads after every 5 items. First ads display after 5 items but when swept up/down the ads display random positions. What should I change to display ads after every 5 items?
AdapterFile:
public class TQuoteAdapter extends RecyclerView.Adapter<TQuoteAdapter.ImageHolder> {
private int ad_count = 0;
List<QuotesModel> quotesList;
Context context;
int width;
DBHelper_dbfile dbHelper;
int[] grid_colors;
int c = 0;
public TQuoteAdapter(List<QuotesModel> quotesList, Context context) {
this.quotesList = quotesList;
this.context = context;
dbHelper = new DBHelper_dbfile(context);
try {
dbHelper.openDatabase();
} catch (SQLException sqle) {
throw sqle;
}
DisplayMetrics displayMetrics = context.getResources()
.getDisplayMetrics();
width = displayMetrics.widthPixels;
grid_colors = context.getResources().getIntArray(R.array.grid_colors);
}
public static class ImageHolder extends RecyclerView.ViewHolder {
private NativeAdView nativeAdView;
ImageView favIV;
TextView quoteTV;
LinearLayout share, copy, wapp,fbapp,instaapp;
CardView cardView;
public ImageHolder(#NonNull View itemView) {
super(itemView);
nativeAdView = itemView.findViewById(R.id.native_ad_view);
quoteTV = itemView.findViewById(R.id.quoteTV);
favIV = itemView.findViewById(R.id.favIV);
share = itemView.findViewById(R.id.share);
copy = itemView.findViewById(R.id.copy);
wapp = itemView.findViewById(R.id.wapp);
fbapp = itemView.findViewById(R.id.fbapp);
instaapp = itemView.findViewById(R.id.instaapp);
cardView = itemView.findViewById(R.id.cardView);
}
}
#NonNull
#Override
public ImageHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_tquote, parent, false);
return new ImageHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ImageHolder holder, int position) {
if(ad_count == 5){
AdLoader adLoader = new AdLoader.Builder(context,context.getString(R.string.native_unit_id))
.forNativeAd(new NativeAd.OnNativeAdLoadedListener() {
#Override
public void onNativeAdLoaded(#NonNull NativeAd nativeAd) {
populateNativeAdView(nativeAd,holder.nativeAdView);
if (onDestroy()) {
nativeAd.destroy();
}
}
})
.withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(LoadAdError adError) {
// Handle the failure by logging, altering the UI, and so on.
}
})
.withNativeAdOptions(new NativeAdOptions.Builder()
// Methods in the NativeAdOptions.Builder class can be
// used here to specify individual options settings.
.build())
.build();
adLoader.loadAd(new AdRequest.Builder().build());
ad_count = 0;
}else {
holder.quoteTV.setText(quotesList.get(position).getQuotes());
ad_count ++;
}
setAnimation(holder.itemView);
if (quotesList.get(position).getFavourite().equals("no")) {
holder.favIV.setImageResource(R.drawable.unpress_download);
} else {
holder.favIV.setImageResource(R.drawable.press_download);
}
holder.favIV.setOnClickListener(v -> {
if (Utills.hasPermissions(context, Utills.permissions)) {
ActivityCompat.requestPermissions((Activity) context, Utills.permissions, Utills.perRequest);
} else {
if (quotesList.get(position).getFavourite().equals("no")) {
dbHelper.addToFavourite("yes", quotesList.get(position).getId());
quotesList.get(position).setFavourite("yes");
} else {
dbHelper.addToFavourite("no", quotesList.get(position).getId());
quotesList.get(position).setFavourite("no");
}
notifyDataSetChanged();
}
});
int color = grid_colors[c];
c++;
if (c == grid_colors.length) {
c = 0;
}
holder.cardView.setCardBackgroundColor(color);
}
private void setAnimation(View view){
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
view.setAnimation(animation);
}
protected boolean onDestroy() {
AdManager.destroyFbAd();
return false;
}
#Override
public int getItemCount() {
return quotesList.size();
}
private void populateNativeAdView(NativeAd nativeAd, NativeAdView nativeAdView) {
nativeAdView.setMediaView((MediaView) nativeAdView.findViewById(R.id.media_view));
nativeAdView.setCallToActionView(nativeAdView.findViewById(R.id.cta));
nativeAdView.setHeadlineView(nativeAdView.findViewById(R.id.primary));
nativeAdView.setAdvertiserView(nativeAdView.findViewById(R.id.secondary));
nativeAdView.setStarRatingView(nativeAdView.findViewById(R.id.rating_bar));
nativeAdView.setBodyView(nativeAdView.findViewById(R.id.body));
nativeAdView.setIconView(nativeAdView.findViewById(R.id.icon));
View headlineView = nativeAdView.getHeadlineView();
Objects.requireNonNull(headlineView);
((AppCompatTextView) headlineView).setText(nativeAd.getHeadline());
View bodyView = nativeAdView.getBodyView();
Objects.requireNonNull(bodyView);
((AppCompatTextView) bodyView).setText(nativeAd.getBody());
View callToActionView = nativeAdView.getCallToActionView();
Objects.requireNonNull(callToActionView);
((AppCompatButton) callToActionView).setText(nativeAd.getCallToAction());
MediaView mediaView = nativeAdView.getMediaView();
Objects.requireNonNull(mediaView);
MediaContent mediaContent = nativeAd.getMediaContent();
Objects.requireNonNull(mediaContent);
mediaView.setMediaContent(mediaContent);
if (nativeAd.getIcon() == null) {
View iconView = nativeAdView.getIconView();
Objects.requireNonNull(iconView);
iconView.setVisibility(View.INVISIBLE);
} else {
View iconView2 = nativeAdView.getIconView();
Objects.requireNonNull(iconView2);
((AppCompatImageView) iconView2).setImageDrawable(nativeAd.getIcon().getDrawable());
nativeAdView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getStarRating() == null) {
View starRatingView = nativeAdView.getStarRatingView();
Objects.requireNonNull(starRatingView);
starRatingView.setVisibility(View.INVISIBLE);
} else {
View starRatingView2 = nativeAdView.getStarRatingView();
Objects.requireNonNull(starRatingView2);
((RatingBar) starRatingView2).setRating(nativeAd.getStarRating().floatValue());
nativeAdView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
View advertiserView = nativeAdView.getAdvertiserView();
Objects.requireNonNull(advertiserView);
advertiserView.setVisibility(View.INVISIBLE);
} else {
View advertiserView2 = nativeAdView.getAdvertiserView();
Objects.requireNonNull(advertiserView2);
((AppCompatTextView) advertiserView2).setText(nativeAd.getAdvertiser());
nativeAdView.getAdvertiserView().setVisibility(View.VISIBLE);
}
nativeAdView.setNativeAd(nativeAd);
nativeAdView.setVisibility(View.VISIBLE);
}
I am trying to make a list view. I did it successfully without the photos loading from url without using a custom array adapter. However how can I implement loading images from url without using a custom array adapter?
I am trying to use the working codes from this thread but it is giving an error for holder.
Error Part
icon = new ImageDownloaderTask(holder.imageView).execute(doctorPhoto);
DoctorsActivity.java
public class DoctorsActivity extends AppCompatActivity {
private JSONArray arrayAdapter;
private static final String URL_FOR_BALANCE = "http://192.168.1.28/api2/doctors.php";
String cancel_req_tag = "login";
private ListView lv;
ArrayList<HashMap<String, String>> contactList;
Bitmap icon = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_doctors);
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getSupportActionBar().setCustomView(R.layout.toolbar_doctors);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#003764")));
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
SharedPreferences sharedPreferences = getSharedPreferences(Config.SHARED_PREF_NAME, Context.MODE_PRIVATE);
final String pid = sharedPreferences.getString(Config.UID_SHARED_PREF, null);
contactList = new ArrayList<>();
lv = (ListView) findViewById(R.id.list);
StringRequest strReq = new StringRequest(Request.Method.POST,
URL_FOR_BALANCE, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
if (!error) {
JSONArray contacts = jObj.getJSONArray("user");
for (int i = 0; i < contacts.length(); i++) {
JSONObject c = contacts.getJSONObject(i);
String doctorTitle = c.getString("title");
String doctorName = c.getString("first_name");
String doctorSurname = c.getString("last_name");
String doctorPhoto = c.getString("photo"); //image URL
String doctorMobile = c.getString("mobile");
String doctorFullName = doctorTitle+" "+doctorName+" "+doctorSurname;
icon = new ImageDownloaderTask(holder.imageView).execute(doctorPhoto);
// tmp hash map for single contact
HashMap<String, String> contact = new HashMap<>();
// adding each child node to HashMap key => value
contact.put("photo", icon);
contact.put("doctor", doctorFullName);
contact.put("mobile", doctorMobile);
// adding contact to contact list
contactList.add(contact);
}
ListAdapter adapter = new SimpleAdapter(
DoctorsActivity.this, contactList,
R.layout.activity_doctors_list_item, new String[]{"photo", "doctor",
"mobile"}, new int[]{R.id.photo,
R.id.doctor, R.id.mobile});
lv.setAdapter(adapter);
} else {
String errorMsg = jObj.getString("error_msg");
Toast.makeText(getApplicationContext(),
errorMsg, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() {
// Posting params to login url
Map<String, String> params = new HashMap<String, String>();
params.put("uid", pid);
params.put("lang", Locale.getDefault().getDisplayLanguage());
return params;
}
};
// Adding request to request queue
AppSingleton.getInstance(getApplicationContext()).addToRequestQueue(strReq,cancel_req_tag);
}
class ImageDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public ImageDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
return downloadBitmap(params[0]);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
Drawable placeholder = null;
imageView.setImageDrawable(placeholder);
}
}
}
}
private Bitmap downloadBitmap(String url) {
HttpURLConnection urlConnection = null;
try {
URL uri = new URL(url);
urlConnection = (HttpURLConnection) uri.openConnection();
final int responseCode = urlConnection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
return null;
}
InputStream inputStream = urlConnection.getInputStream();
if (inputStream != null) {
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (Exception e) {
urlConnection.disconnect();
Log.w("ImageDownloader", "Errore durante il download da " + url);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
}
}
Why not use a 3rd party lib like https://github.com/bumptech/glide?
Relevant code:
// ...
new Glide
.with(convertView.getContext())
.load(url)
.centerCrop()
.placeholder(R.drawable.noimage)
.crossFade()
.into(bmImage);
holder.tvName.setText(doctorList.get(position).getName());
holder.tvMobile.setText(doctorList.get(position).getMobile());
// ...
For everyone who wants to have listView with images this my corrected working Custom Adapter:
public class DoctorAdapter extends ArrayAdapter<Doctors>{
ArrayList<Doctors> doctorList;
LayoutInflater vi;
int Resource;
ViewHolder holder;
public DoctorAdapter(Context context, int resource, ArrayList<Doctors> objects) {
super(context, resource, objects);
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Resource = resource;
doctorList = objects;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
holder = new ViewHolder();
v = vi.inflate(Resource, null);
holder.imageview = (ImageView) v.findViewById(R.id.photo);
holder.tvName = (TextView) v.findViewById(R.id.doctor);
holder.tvMobile = (TextView) v.findViewById(R.id.mobile);
holder.callButton = (Button) v.findViewById(R.id.btnCall);
holder.callButton.setTag(holder);
holder.callButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
String message= viewHolder.tvMobile.getText().toString();
Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
}
});
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.imageview.setImageResource(R.drawable.noimage);
new DownloadImageTask(holder.imageview).execute(doctorList.get(position).getImage());
holder.tvName.setText(doctorList.get(position).getName());
holder.tvMobile.setText(doctorList.get(position).getMobile());
return v;
}
static class ViewHolder {
public ImageView imageview;
public TextView tvName;
public TextView tvMobile;
public Button callButton;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}
After updating the device to iOS 10, QLPreviewController stopped to display correctly the documents. It shows the white screen.
I have extracted the sample scenario from the app.
It contains single page with two buttons that should load two different documents:
<?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:QuickLookIOS10Test"
x:Class="QuickLookIOS10Test.QuickLookIOS10TestPage">
<StackLayout Orientation="Vertical">
<Button Text="Load first doc" Clicked="OnLoadFirstClicked"/>
<Button Text="Load second doc" Clicked="OnLoadSecondClicked"/>
<Button Text="Navigate forward" Clicked="OnForwardClicked"/>
<local:QLDocumentView
x:Name="DocumentView"
BackgroundColor="Silver"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"/>
</StackLayout>
</ContentPage>
where:
public class QLDocumentView : View
{
public static readonly BindableProperty FilePathProperty =
BindableProperty.Create(nameof(FilePath), typeof(string), typeof(QLDocumentView), null);
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
}
There is a custom renderer involved:
public class QLDocumentViewRenderer : ViewRenderer<QLDocumentView, UIView>
{
private QLPreviewController controller;
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
//This is a fix to prevent incorrect scaling after rotating from portrait to landscape.
//No idea why does this work :( Bug #101639
return new SizeRequest(Size.Zero, Size.Zero);
}
protected override void OnElementChanged(ElementChangedEventArgs<QLDocumentView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
controller = new QLPreviewController();
SetNativeControl(controller.View);
}
RefreshView();
}
protected override void OnElementPropertyChanged(object sender,
System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == QLDocumentView.FilePathProperty.PropertyName)
{
RefreshView();
}
}
private void RefreshView()
{
DisposeDataSource();
if (Element?.FilePath != null)
{
controller.DataSource = new DocumentQLPreviewControllerDataSource(Element.FilePath);
}
controller.ReloadData();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
DisposeDataSource();
DisposeController();
}
}
private void DisposeDataSource()
{
var dataSource = controller.DataSource;
controller.DataSource = null;
dataSource?.Dispose();
}
private void DisposeController()
{
controller?.Dispose();
controller = null;
}
private class DocumentQLPreviewControllerDataSource : QLPreviewControllerDataSource
{
private readonly string fileName;
public DocumentQLPreviewControllerDataSource(string fileName)
{
this.fileName = fileName;
}
public override nint PreviewItemCount(QLPreviewController controller)
{
return 1;
}
public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
{
NSUrl url = NSUrl.FromFilename(fileName);
return new QlItem(url);
}
private sealed class QlItem : QLPreviewItem
{
private readonly NSUrl itemUrl;
public QlItem(NSUrl uri)
{
itemUrl = uri;
}
public override string ItemTitle { get { return string.Empty; } }
public override NSUrl ItemUrl { get { return itemUrl; } }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this.itemUrl?.Dispose();
}
}
}
}
}
If the application setups the main page like below:
MainPage = new NavigationPage(new QuickLookIOS10TestPage());
it does work on iOS 9.3 but not on iOS 10. If I remove NavigationPage:
MainPage = new QuickLookIOS10TestPage();
it works on both iOS versions.
The code behind for button clicks just sets the FilePath property of the control.
Sample app demonstrating the problem
Xamarin Forms 2.3.2.127
Xamarin Studio 6.1.1 (build 15)
I've faced with the same problem. It looks like something was changed or even broken in QuickLook in iOS10, but the solution is quite simple:
public class PdfViewerControlRenderer : ViewRenderer<PdfViewerControl, UIView>
{
private readonly bool IsOniOS10;
private UIViewController _controller;
private QLPreviewController _qlPreviewController;
public PdfViewerControlRenderer()
{
IsOniOS10 = UIDevice.CurrentDevice.CheckSystemVersion(10, 0);
}
protected override void OnElementChanged(ElementChangedEventArgs<PdfViewerControl> e)
{
if (e.NewElement != null)
{
_controller = new UIViewController();
_qlPreviewController = new QLPreviewController();
//...
// Set QuickLook datasource here
//...
if (!IsOniOS10)
{
_controller.AddChildViewController(_qlPreviewController);
_controller.View.AddSubview(_qlPreviewController.View);
_qlPreviewController.DidMoveToParentViewController(_controller);
}
SetNativeControl(_controller.View);
}
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
_controller.View.Frame = Bounds;
_controller.View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
_qlPreviewController.View.Frame = Bounds;
if (IsOniOS10)
{
_controller.View.AddSubview(_qlPreviewController.View);
_qlPreviewController.DidMoveToParentViewController(_controller);
}
}
}
Result:
Hi I am trying to upgrade our ios app from mvvmcross v1 to v3. I can't figure out how to make my custom buttonrow work.
My view ViewDidLoad it is the button items that binds to the button row
public override void ViewDidLoad()
{
base.ViewDidLoad();
SortingView.ViewModel = ViewModel;
_shown = false;
// Setup View Animatons
Buttons.OnClick = () => { AnimationTransition = ViewTransitionAnimation.TransitionFade; };
TopRightButton.TouchDown +=
(sender, args) => {
AnimationTransition = ViewTransitionAnimation.TransitionCrossDissolve;
};
// Setup Bindings
this.AddBindings(
new Dictionary<object, string>
{
{this.BackgroundImage, "{'ImageData':{'Path':'BackgroundImage','Converter':'ImageItem'}}"},
{this.TopbarBackground, "{'ImageData':{'Path':'TopBarImage','Converter':'ImageItem'}}"},
{this.TopLogo, "{'ImageData':{'Path':'Logo','Converter':'ImageItem'}}"},
{this.Buttons, "{'ItemsSource':{'Path':'ButtonItems'}}"},
{this.SlideMenu, "{'ItemsSource':{'Path':'VisibleViews'}}"},
{
this.SortingView,
"{'ItemsSource':{'Path':'CategoriesName'},'SelectedGroups':{'Path':'SelectedGroups'},'ForceUbracoUpdateAction':{'Path':'ForceUbracoUpdateAction'}}"
},
{this.SettingsButton, "{'TouchDown':{'Path':'TopRightButtonClick'},'Hide':{'Path':'HideTopbarButton'},'ImageData':{'Path':'TopButtonImage','Converter':'ImageItem'}}" },
{this.TopRightButton, "{'TouchDown':{'Path':'SecondaryButtonButtonPushed'},'Hide':{'Path':'HideTopbarButton2'},'ImageData':{'Path':'SettingsButtonImage','Converter':'ImageItem'}}" }
});
// Perform any additional setup after loading the view, typically from a nib.
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, ReEnableSlideMenu);
this.SortingView.Hidden=true;
ViewModel.SettingButtonEvent += HandleSettingButtonPushed;
}
Here is my custom control "ButtonRow
[Register("ButtonRow")]
public class ButtonRow : CustomListViewController
{
private int _width = 0;
private UIImage _backgroundImage = null;
public ButtonRow(IntPtr handle)
: base(handle)
{
_width = (int)this.Frame.Width;
UseImageAsIcon = false;
FontSize=0;
}
public bool UseImageAsIcon { get; set; }
public UIImage BackgroundImage
{
get { return _backgroundImage; }
set { _backgroundImage = value; }
}
public int FontSize
{
get;set;
}
private Action _onClickAction;
private int _spacing = 0;
public Action OnClick
{
get
{
return _onClickAction;
}
set
{
_onClickAction = value;
}
}
/// <summary>
/// The add views.
/// </summary>
/// custom implementation for adding views to the button row
protected override void AddViews()
{
if (ItemsSource == null)
{
Hidden = true;
return;
}
base.AddViews();
foreach (UIView uiView in Subviews)
{
uiView.RemoveFromSuperview();
}
if (ItemsSource.Count == 0)
{
Hidden = true;
return;
}
if (_backgroundImage != null)
{
var frame = new RectangleF(0, 0, Frame.Width, Frame.Height);
var background = new UIImageView(frame) { Image = _backgroundImage };
AddSubview(background);
}
_width = _width - ((ItemsSource.Count - 1) * Spacing);
var buttonWidth = (int)Math.Ceiling (((double)_width) / ItemsSource.Count);
int index = 0;
foreach (ViewItemModel item in ItemsSource)
{
// creating custom button with needed viewmodel, nib etc is loaded in the class constructor
var button = new ButtonWithLabel(item, OnClick);
if (FontSize > 0)
{
button.FontSize(FontSize);
}
if (UseImageAsIcon)
{
button.AddBindings(
new Dictionary<object, string>
{
{ button, "{'IconLabel':{'Path':'Title'},'TitleFontColor':{'Path':'TitleFontColor'}}" },
{ button.icon, "{'ImageData':{'Path':'ImageIcon','Converter':'ImageItem'}}" }
});
}
else
{
// bindings created between the button and its viewmodel
button.AddBindings(
new Dictionary<object, string>
{
{button, "{'Label':{'Path':'Title'},'TitleFontColor':{'Path':'TitleFontColor'},'BackgroundColor':{'Path':'BackgroundColor'}}" },
{button.Background, "{'ImageData':{'Path':'ImageIcon','Converter':'ImageItem'}}" }
});
button.icon.Hidden = true;
}
// new frame of button is set, as the number of buttons is dynamic
int x = index == 0 ? 0 : index * (buttonWidth + Spacing);
button.SetFrame(new RectangleF(x, 0, buttonWidth, Frame.Height));
// the view of the button is added to the buttonrow view
AddSubview(button.View);
index++;
}
}
public int Spacing
{
get
{
return this._spacing;
}
set
{
this._spacing = value;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public override void Cleanup()
{
if(Subviews!=null)
{
foreach (var view in Subviews)
{
view.RemoveFromSuperview();
}
}
if(_backgroundImage!=null)
{
_backgroundImage.Dispose();
}
ItemsSource = null;
}
}
Here is my CustomListViewController
public class CustomListViewController: UIView
{
private IList _itemsSource;
private CustomViewSource _viewSource;
public CustomListViewController(MvxShowViewModelRequest showRequest)
{
ShowRequest = showRequest;
}
protected CustomListViewController()
{
}
public CustomListViewController(IntPtr handle)
: base(handle)
{
}
public bool IsVisible
{
get
{
return this.IsVisible;
}
}
public IList ItemsSource
{
get { return _itemsSource; }
set { _itemsSource = value; if(value!=null){CreateViewSource(_itemsSource); }}
}
public virtual void CreateViewSource(IList items)
{
if (_viewSource == null)
{
_viewSource = new CustomViewSource();
_viewSource.OnNewViewsReady += FillViews;
}
_viewSource.ItemsSource = items;
}
private void FillViews(object sender, EventArgs e)
{
AddViews();
}
protected virtual void AddViews()
{
// get views from source and do custom allignment
}
public virtual void Cleanup()
{
if(_viewSource!=null)
{
_itemsSource.Clear();
_itemsSource=null;
_viewSource.OnNewViewsReady -= FillViews;
_viewSource.ItemsSource.Clear();
_viewSource.ItemsSource = null;
_viewSource=null;
}
}
public MvxShowViewModelRequest ShowRequest { get;
private set;
}
}
And My CustomViewSource
public class CustomViewSource
{
private IList _itemsSource;
private List<UIView> _views=new List<UIView>();
public event EventHandler<EventArgs> OnNewViewsReady;
public CustomViewSource ()
{
}
public List<UIView> Views { get { return _views; } }
public virtual IList ItemsSource
{
get { return _itemsSource; }
set
{
// if (_itemsSource == value)
// return;
var collectionChanged = _itemsSource as INotifyCollectionChanged;
if (collectionChanged != null)
collectionChanged.CollectionChanged -= CollectionChangedOnCollectionChanged;
_itemsSource = value;
collectionChanged = _itemsSource as INotifyCollectionChanged;
if (collectionChanged != null)
collectionChanged.CollectionChanged += CollectionChangedOnCollectionChanged;
ReloadViewData();
}
}
protected object GetItemAt(int position)
{
if (ItemsSource == null)
return null;
return ItemsSource[position];
}
protected void CollectionChangedOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
ReloadViewData();
}
protected virtual void ReloadViewData()
{
if(ItemsSource==null){return;}
foreach (var VARIABLE in ItemsSource)
{
//call create view and add it to Views
}
//event new views created
if(OnNewViewsReady!=null)
OnNewViewsReady(this,new EventArgs());
}
protected virtual UIView CreateUIView(int position)
{
UIView view = null;
/*
//create view from nib
UIView newView=null;
return newView;
* */
return view;
}
}
Any one have any clues on how to make this work in mvvmcross v3 ?
I would like to make it so i can add x number of buttons and load the buttons from nib files. Have looked at the Kittens collection view example, but have not figured out how to make it work for my buttonRow, not sure if the collectionView is the right one to use as base.
The most common way to show a list is to use a UITableView.
There are quite a few samples around that show how to load these in MvvmCross:
n=2 and n=2.5 in http://mvvmcross.wordpress.com/
n=6 and n=6.5 in http://mvvmcross.wordpress.com/
Working with Collections in https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/Working%20With%20Collections
In your case, it looks like the previous coder has implemented some sort of custom control with custom layout. Adapting this to v3 shouldn't be particularly difficult, but you are going to need to read through and understand the previous code and how it works (this is nothing to do with MvvmCross - it's just app UI code).
One sample on dealing with custom views like this in iOS is the N=32 tutorial - http://slodge.blogspot.co.uk/2013/06/n32-truth-about-viewmodels-starring.html
There are a couple of answers out there for this already, but I have not been able to find anything conclusive. This is the jist of what I am trying to do:
EquityInstrument
public class EquityInstrument : INotifyPropertyChanged
{
private string _Symbol;
public string Symbol
{
get
{
return _Symbol;
}
set
{
_Symbol = value;
OnPropertyChanged("Symbol");
}
}
public EquityInstrument(string Symbol)
{
this.Symbol = Symbol;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string FieldName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(FieldName);
}
}
}
OptionInstrument
public class OptionInstrument : INotifyPropertyChanged;
{
public readonly EquityInstrument UnderlyingInstrument;
private double _StrikePrice;
public double StrikePrice
{
get
{
return _StrikePrice;
}
set
{
_StrikePrice = value;
OnPropertyChanged("StrikePrice");
}
}
private DateTime _Expiration;
public DateTime Expiration;
{
get
{
return _Expiration;
}
set
{
_Expiration = value;
OnPropertyChanged("Expiration");
}
}
public OptionInstrument(string Symbol, double StrikePrice, DateTime Expiration)
{
this.Symbol = Symbol;
this.StrikePrice = StrikePrice;
this.Expiration = Expiration;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string FieldName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(FieldName);
}
}
}
This code initiates the Option Table...
GridControl OptionGrid = new GridControl();
BindingList<OptionInstrument> BoundList = new BindingList<OptionInstrument>();
public void InitializeDataTable()
{
OptionGrid.DataSource = new BindingSource() { DataSource = BoundList };
BandedGridColumn Column0 = new BandedGridColumn();
Column0.FieldName = "Symbol";
BandedGridColumn Column1 = new BandedGridColumn();
Column1.FieldName = "StrikePrice";
BandedGridColumn Column2 = new BandedGridColumn();
Column2.FieldName = "Expiration";
BandedGridView MainView = (BandedGridView)OptionGrid.MainView;
MainView.Columns.Add(Column0);
MainView.Columns.Add(Column1);
MainView.Columns.Add(Column2);
BoundList.Add(new OptionInstrument("DELL", 12.22, new DateTime(2012, 10, 12));
BoundList.Add(new OptionInstrument("MSFT", 13.23, new DateTime(2012, 09, 16));
BoundList.Add(new OptionInstrument("SPY", 12.23, new DateTime(2012, 07, 18));
}
What do you think? Are there any good design patterns to accomplish this?