I've got ListBox bound to List. I want to load more items when scrolled to bottom of list so that some animation indicating loading is shown and bound list is expanded. If I understand correctly, I can use ObservableCollection instead of List and expand that collection. Also, I could wrap ItemPresenter into StackPanel with ItemPresenter and Image at bottom.
But how do I detect that list has been scrolled to bottom and initiate expanding of collection?
Check tutorials:
http://blog.slimcode.com/2010/09/11/detect-when-a-listbox-scrolls-to-its-end-wp7/
http://danielvaughan.orpius.com/post/Scroll-Based-Data-Loading-in-Windows-Phone-7.aspx
I found this http://blogs.microsoft.co.il/blogs/shair/archive/2011/04/06/wp7-how-to-extend-listbox-when-reaching-last-item.aspx in my opinion is more coincided, easy and self-explanatory than other examples.
I'm use the following code to solve the same problem. As base I use the following solution, added one DependecyProperties and removed references on assembly
Microsoft.Practices.Prism.Interactivity.
public class ScrollViewMonitor
{
public static readonly DependencyProperty ReachBottomCommandProperty = DependencyProperty.RegisterAttached("ReachBottomCommand",
typeof(ICommand), typeof(ScrollViewMonitor), new PropertyMetadata(null, ReachBottomCommandChanged));
public static ICommand GetReachBottomCommand(DependencyObject dpObj)
{
return (ICommand)dpObj.GetValue(ReachBottomCommandProperty);
}
public static void SetReachBottomCommand(DependencyObject dpObj, ICommand command)
{
dpObj.SetValue(ReachBottomCommandProperty, command);
}
private static FrameworkElement _frmElemenet;
private static void ReachBottomCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
_frmElemenet = obj as FrameworkElement;
if (_frmElemenet != null)
_frmElemenet.Loaded += frmElement_Loaded;
}
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset",
typeof(double), typeof(ScrollViewMonitor), new PropertyMetadata(0.0, VerticalOffsetChanged));
private static void VerticalOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
ScrollViewer scrollView = (ScrollViewer)obj;
if (scrollView.VerticalOffset > scrollView.ScrollableHeight * 0.8)
{
ICommand command = GetReachBottomCommand(_frmElemenet);
if (command != null)
command.Execute(null);
}
}
private static void frmElement_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement frmElement = (FrameworkElement)sender;
frmElement.Loaded -= frmElement_Loaded;
var scrollView = FindChildOfType<ScrollViewer>(frmElement);
if (scrollView != null)
scrollView.SetBinding(VerticalOffsetProperty, new Binding("VerticalOffset")
{
Source = scrollView
});
}
private static T FindChildOfType<T>(DependencyObject obj) where T : class
{
Queue<DependencyObject> queue = new Queue<DependencyObject>();
queue.Enqueue(obj); //Adds an object to the end of the
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue(); //Removes and returns the object at the beginning of the Queue
for (int i = 0, count = VisualTreeHelper.GetChildrenCount(current); i < count; i++)
{
DependencyObject dpObj = VisualTreeHelper.GetChild(current, 0);
T typeChild = dpObj as T;
if (typeChild != null)
return typeChild;
queue.Enqueue(dpObj); //Adds an object to the end of the Queue
}
}
return null;
}
}
In XAML
<ListBox x:Name="_lstBoxNews"
DataContext="{Binding Mode=OneTime}"
ItemsSource="{Binding Items, Mode=OneWay}"
ItemTemplate="{Binding Mode=OneWay, Source={StaticResource ShortArticleTemlate}}"
hlp:ScrollViewMonitor.ReachBottomCommand="{Binding LoadAdditionalArticles, Mode=OneTime}"
>
</ListBox>
Related
I want to show long text of picker item in two lines using custom render how can I achieve it?
First , you need a custom renderer for picker .
To override the item you need to replace the original view with a AlertDialog .
Then custom a ListView and set it as AlertDialog.View .
Then you can customize everything in the Adapter(here we need to customize the textview ).
Sample code
[assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
namespace FormsApp.Droid
{
public class MyAdapter : ArrayAdapter
{
private IList<string> _data;
Context _context;
public MyAdapter(Context context, int resource , IList<string> data) : base(context,resource)
{
_context = context;
_data = data;
}
public override int Count => _data.Count;
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
TextView textview = new TextView(_context);
textview.TextSize = 18;
textview.SetTextColor(Android.Graphics.Color.DarkGray);
textview.Ellipsize = TruncateAt.End;
textview.SetMaxLines(2); //this line
textview.Text = _data[position];
return textview;
}
}
public class MyListView : Android.Widget.ListView
{
public MyListView(Context context, IList<string> data) : base(context)
{
this.DividerHeight = 0;
this.Adapter = new MyAdapter(context, 0, data);
}
}
class MyPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyPickerRenderer(Context context):base(context)
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyListView(Context, model.Items);
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.SetPadding(35, 30, 35, 0);
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
Testing code in Forms xaml
<Picker x:Name="picker" Title="Select a monkey">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>abc</x:String>
<x:String>aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccdddddddddddddddddddddd</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
Before
After
I am having an grid holding in Button. I want to set corner radius for this button by programmatically in xamarin.uwp. If it possible means i want to set corner radius for Bottom left alone. Please suggest your ideas for this query.
For your requirement, you could custom BottomLeft BindableProperty. then use it in your custom button render like the following.
Custom Forms buttom
public class MyButton : Button
{
public static readonly BindableProperty BottomLeftProperty = BindableProperty.Create(
propertyName: "BottomLeft",
returnType: typeof(int),
declaringType: typeof(MyButton),
defaultValue: default(int));
public int BottomLeft
{
get { return (int)GetValue(BottomLeftProperty); }
set { SetValue(BottomLeftProperty, value); }
}
}
CustomButtonRenderer.cs
public class CustomButtonRenderer : ButtonRenderer
{
Windows.UI.Xaml.Controls.ContentPresenter _contentPresenter;
MyButton _myElement;
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control == null)
{
SetNativeControl(new FormsButton());
}
if (e.NewElement != null)
{
Control.Loaded += Control_Loaded;
_myElement = Element as MyButton;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyButton.BottomLeftProperty.PropertyName)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void Control_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
_contentPresenter = MyFindChildByName(Control, "ContentPresenter") as Windows.UI.Xaml.Controls.ContentPresenter;
if (_myElement.IsSet(MyButton.BottomLeftProperty) && _myElement.BottomLeft != (int)MyButton.BottomLeftProperty.DefaultValue)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void UpdateBottomLeftBorderRadius(int bottomLeft)
{
if (_contentPresenter != null)
{
_contentPresenter.CornerRadius = new CornerRadius(0, 0, 0, bottomLeft);
}
}
public static DependencyObject MyFindChildByName(DependencyObject parant, string ControlName)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is FrameworkElement && ((FrameworkElement)MyChild).Name == ControlName)
return MyChild;
var FindResult = MyFindChildByName(MyChild, ControlName);
if (FindResult != null)
return FindResult;
}
return null;
}
}
Usage
<local:MyButton x:Name="Hello" Text="hello"
WidthRequest="100"
HeightRequest="100"
Margin="0,100,0,0"
BottomLeft="15"
VerticalOptions="Center"
HorizontalOptions="Center"
Clicked="MyButton_Clicked"/>
For edit the bottom left property programatically.
HelloBtn.BottomLeft = 30;
I have a template that I use for a button. I placed four of these buttons on a page like this:
<template:Button Meta="gst" Grid.Column="1" Selected="{Binding Mode[0].Selected}" Text="{Binding Mode[0].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="2" Selected="{Binding Mode[1].Selected}" Text="{Binding Mode[1].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="3" Selected="{Binding Mode[2].Selected}" Text="{Binding Mode[2].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="4" Selected="{Binding Mode[3].Selected}" Text="{Binding Mode[3].Name}" TapCommand="{Binding ModeBtnCmd }" />
I declare an array called Mode that I use to store some values for the Selected parameter in an array declared like this:
public partial class HomePageViewModel : ObservableObject
{
ParamViewModel[] _mode;
public ParamViewModel[] Mode { get => _mode; set => SetProperty(ref _mode, value); }
In the OnAppearing I set the value of Selected for each element in the array:
protected async override void OnAppearing()
{
base.OnAppearing();
vm.Mode[0].Selected = false;
vm.Mode[1].Selected = false;
vm.Mode[2].Selected = true;
vm.Mode[3].Selected = false;
However I do not see the HandleSelectedPropertyChanged called for each of the Buttons. If I set a debug point in the HandleSelectedPropertyChanged event, I only see it being called once.
I expect only the 3rd button to be selected but when the code runs the buttons appear as:
Selected Selected Selected Selected
Can someone give me advice on this. The code works when I click on the buttons one by one and the back-end changes the selected and then the above SetButtons is called. But it doesn't work initially and all buttons appear as selected.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class Button : Frame
{
public static readonly BindableProperty TapCommandProperty = BindableProperty.Create("TapCommand", typeof(Command), typeof(Button), defaultBindingMode: BindingMode.TwoWay, defaultValue: default(Command));
public static readonly BindableProperty TapCommandParamProperty = BindableProperty.Create(nameof(TapCommandParam), typeof(object), typeof(Button), default(object));
public static readonly BindableProperty
SelectedProperty = BindableProperty.Create(nameof(Selected),
typeof(bool),
typeof(Button),
false,
propertyChanged: HandleSelectedPropertyChanged);
public static readonly BindableProperty
TextProperty = BindableProperty.Create(nameof(Text),
typeof(string),
typeof(Button),
default(string));
public static readonly BindableProperty
MetaProperty = BindableProperty.Create("Meta",
typeof(string),
typeof(Button),
default(string),
propertyChanged: HandleMetaPropertyChanged);
public static readonly BindableProperty VisibleProperty = BindableProperty.Create(nameof(Visible), typeof(bool), typeof(Button), true);
public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
public string Meta { get => (string)GetValue(MetaProperty); set => SetValue(MetaProperty, value); }
public Command TapCommand { get => (Command)GetValue(TapCommandProperty); set => SetValue(TapCommandProperty, value); }
public object TapCommandParam { get => (object)GetValue(TapCommandParamProperty); set => SetValue(TapCommandParamProperty, value); }
public bool Selected { get => (bool)GetValue(SelectedProperty); set => SetValue(SelectedProperty, value); }
public bool Visible { get => (bool)GetValue(VisibleProperty); set => SetValue(VisibleProperty, value); }
string b;
string s;
string f;
public Button()
{
InitializeComponent();
HasShadow = false;
HorizontalOptions = LayoutOptions.Center;
VerticalOptions = LayoutOptions.Center;
}
private static void HandleMetaPropertyChanged(BindableObject bindable, object oldValue, object meta)
{
var control = (Button)bindable;
if (control != null) {
control.b = ((string)meta).Substring(0, 1); // background surface
control.s = ((string)meta).Substring(1, 1); // shape
control.f = ((string)meta).Substring(2, 1); // font
control.BackgroundColor = control.b == "g" ?
(Color)Application.Current.Resources["GridButtonBackgroundColor"] :
(Color)Application.Current.Resources["PageButtonBackgroundColor"];
control.BorderColor = control.b == "g" ?
(Color)Application.Current.Resources["GridButtonBorderColor"] :
(Color)Application.Current.Resources["PageButtonBorderColor"];
if (control.s == "s")
{
control.CornerRadius = 5;
control.Padding = new Thickness(10, 5);
}
else
{
control.WidthRequest = 50;
control.HeightRequest = 50;
control.CornerRadius = 25;
control.Padding = new Thickness(0);
}
Application.Current.Resources.TryGetValue("TextButtonsLabelRes", out object textRes);
Application.Current.Resources.TryGetValue("IconButtonsLabelRes", out object iconRes);
control.ButtonLabel.Style = (control.f == "t") ?
(Style)textRes :
(Style)iconRes;
}
}
private static void HandleSelectedPropertyChanged(BindableObject bindable, object oldValue, object selected)
{
var control = (Button)bindable;
if (control != null)
control.ButtonLabel.TextColor =
((bool)selected) ?
(control.b == "g" ?
(Color)Application.Current.Resources["GridEButtonTextColor"] :
(Color)Application.Current.Resources["PageEButtonTextColor"]) :
(control.b == "g" ?
(Color)Application.Current.Resources["GridButtonTextColor"] :
(Color)Application.Current.Resources["PageButtonTextColor"]);
}
private async void ChangeTheColours(Object sender, EventArgs e)
{
if ((string)this.ButtonLabel.Text.Substring(0, 1) != " ")
{
BackgroundColor = this.b == "g" ?
(Color)Application.Current.Resources["GridCButtonBackgroundColor"] :
(Color)Application.Current.Resources["PageCButtonBackgroundColor"];
BorderColor = this.b == "g" ?
(Color)Application.Current.Resources["GridCButtonBorderColor"] :
(Color)Application.Current.Resources["PageCButtonBorderColor"];
await Task.Delay(500);
BackgroundColor = this.b == "g" ?
(Color)Application.Current.Resources["GridButtonBackgroundColor"] :
(Color)Application.Current.Resources["PageButtonBackgroundColor"];
BorderColor = this.b == "g" ?
(Color)Application.Current.Resources["GridButtonBorderColor"] :
(Color)Application.Current.Resources["PageButtonBorderColor"];
}
}
}
}
For reference here's the SetProperty function:
public class ObservableObject : INotifyPropertyChanged
{
protected virtual bool SetProperty<T>(
ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Just as a reminder the code and method here is working good except for the initial set up where it does not seem to call the propertyChanged event as expected.
The default value on your SelectedProperty is false, OnPropertyChanged is only fired when the current value differs from the new value. Hence only the 3rd button, whose value is being set to true is being fired.
You have to define your initial state of the control in its constructor. Upon any property changes the subsequent changes will be handled in the PropertyChanged handler
im trying to change between two images of a button when the same button is clicked
my xaml code is
<Button x:Name="BidderOne"
Click="BidderOne_Click" Height="5"
Grid.Row="2"
BorderBrush="#FFE7E3E3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="350,0,0,280"
>
<Button.Template>
<ControlTemplate>
<Image x:Name="BidderOneImage"
Source="/Assets/Star.png"
Width="50"/>
</ControlTemplate>
</Button.Template>
</Button>
im not sure what to write in my class, but what i found was something along the lines of this
private void BidderOne_Click(object sender, RoutedEventArgs e)
{
var selectedBidder = sender as Button;
// Button btn = new Button();
// btn = BidderOne;
ControlTemplate dt = BidderOne.Template;
Image btnImage = (Image)dt.TargetType = ;
var img = (Image)(selectedBidder.ContentTemplateRoot as StackPanel).Children[0];
var uri = new Uri("/Assetes/ClubsLogo.png", UriKind.Relative);
// var sri = Windows.UI.Xaml.Application.("Assetes/ClubsLogo.png", UriKind.Relative);
imgOn.UriSource=uri;
img.Source = imgOn;
}
It is not recommended to change the ControlTemplate of a Control at runtime. For these situations you should create a custom control.
I have written a simple ImageButton just so you can get the idea of creating what is needed for your problem.
Here is the code:
public sealed class ImageButton : Button
{
private Image _image;
public ImageButton()
{
this.DefaultStyleKey = typeof (Button);
_image = new Image();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.Content = _image;
OnImage1SourceChanged(null, null);
}
public ImageSource Image1Source
{
get { return (ImageSource)GetValue(Image1SourceProperty); }
set { SetValue(Image1SourceProperty, value); }
}
public static readonly DependencyProperty Image1SourceProperty =
DependencyProperty.Register("Image1Source",
typeof (ImageSource),
typeof (ImageButton),
new PropertyMetadata(null, OnImage1SourceChangedCallback));
private static void OnImage1SourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var imageButton = d as ImageButton;
if (imageButton == null) return;
imageButton.OnImage1SourceChanged((ImageSource)e.OldValue, (ImageSource)e.NewValue);
}
private void OnImage1SourceChanged(ImageSource oldValue, ImageSource newValue)
{
if (_image != null)
{
_image.Source = Image1Source;
}
}
public ImageSource Image2Source
{
get { return (ImageSource)GetValue(Image2SourceProperty); }
set { SetValue(Image2SourceProperty, value); }
}
public static readonly DependencyProperty Image2SourceProperty =
DependencyProperty.Register("Image2Source",
typeof (ImageSource),
typeof (ImageButton),
new PropertyMetadata(null));
protected override void OnTapped(TappedRoutedEventArgs e)
{
base.OnTapped(e);
if (_image != null)
{
_image.Source = Image2Source;
}
}
}
and you use it like this:
<controls:ImageButton Width="60" Height="60" Image1Source="/Assets/Images/Chat/ic_comment_favorite_yellow.png" Image2Source="/Assets/Images/Flags/afghanistan.png"/>
Try this
EDITED
private void BidderOne_Click(object sender, RoutedEventArgs e) {
var selectedBidder = sender as Button;
// WRONG -> Image image = selectedBidder.Template.FindName("BidderOneImage", selectedBidder) as Image;
Image image = VisualTreeHelper.GetChild(selectedBidder, 0) as Image;
image.Source = new BitmapImage(new Uri("NEW IMAGE URI"));
}
OK, so I wanted to use a color picker and found Alex Yakhnin's blog...
http://blogs.msdn.com/b/priozersk/archive/2010/09/17/customizing-picker-box-dialog.aspx
after implementing this code from the blog:
DialogViewModel viewModel;
PickerBoxDialog customDialog;
ColorItem currentColorItem;
private void InitCustomPickerDialog()
{
// Initialize viewmodel
this.viewModel = new DialogViewModel();
this.currentColorItem = viewModel.Items[0];
// Assing it to the page's DataContext
this.DataContext = currentColorItem;
this.customDialog = new PickerBoxDialog();
this.customDialog.Title = "ACCENTS";
// Assign our style to the dialog
this.customDialog.Style = this.Resources["Custom"] as Style;
this.customDialog.ItemSource = viewModel.Items;
this.customDialog.Closed += new EventHandler(customDialog_Closed);
}
void customDialog_Closed(object sender, EventArgs e)
{
this.currentColorItem = (ColorItem)this.customDialog.SelectedItem;
this.DataContext = currentColorItem;
}
private void buttonColor_Click(object sender, RoutedEventArgs e)
{
this.customDialog.Show();
}
I realized that the page's datacontext is being used to set the colors for the picker. I am using a listbox on this same page which also sets the datacontext of the page to display a list of fish.
public FishsPage()
{
InitializeComponent();
DataContext = App.vmFish;
InitCustomPickerDialog();
}
Therefore, I need the page's datacontext for 2 different things now. Is there a way to use the color picker control and the fish list simultaneously?
Erno's suggestion?:
public class FishViewModelComplete : INotifyPropertyChanged
{
private readonly ReefServiceClient wcfProxy;
public FishViewModelComplete()
{
vmFish = new FishViewModel();
vmDialog = new DialogViewModel();
}
private FishViewModel _vmFish;
public FishViewModel vmFish
{
get
{
return _vmFish;
}
set
{
_vmFish = value;
}
}
private DialogViewModel _vmDialog;
public DialogViewModel vmDialog
{
get
{
return _vmDialog;
}
set
{
_vmDialog = value;
}
}
}
Create a third ViewModel that exposes the two ViewModels through properties and bind the appropriate controls to these.