I need a event for the slider it should fire only if user ends touching the control - slider

My Requriment : I need a event for slider it should fire only if user ends touching the control
Custom Control :
public class ExtendedSlider : Slider
{
public event EventHandler StopedDraging;
public void OnStopedDrag()
{
if (StopedDraging != null)
{
StopedDraging(this,EventArgs.Empty);
}
}
}
UI :
<ListView.ItemTemplate >
<Label Text="{Binding luminaireLevel, StringFormat='{0:F0}%'}" />
<PCAControls:ExtendedSlider Maximum="100" Minimum="25"
Value="{Binding luminaireLevel, Mode=TwoWay}"
LuminaireID="{Binding id}"
StopedDraging="ExtendedSlider_StopedDraging"
/>
</ListView.ItemTemplate>
Renderer :
class ExtendedSliderRenderer : SliderRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var slider = (PCA.CustomControls.ExtendedSlider)e.NewElement;
Control.Max = (int)(slider.Maximum - slider.Minimum);
Control.Progress = (int)(slider.Value - slider.Minimum);
Control.StopTrackingTouch += Control_StopTrackingTouch;
}
}
void Control_StopTrackingTouch(object sender, SeekBar.StopTrackingTouchEventArgs e)
{
var slider = (PCA.CustomControls.ExtendedSlider)Element;
slider.Value = Control.Progress + slider.Minimum;
slider.OnStopedDrag();
}
}
Problem is : I achieved what i expected, but user stop draging the slider or tap between the slider , luminaireLevel (vewmodel property) is updating but the slider always showing the full progess

When your renderer changes the value of the iOS control the "binding" isn't Two=Way in that respect. To achieve what you want you need to bind the Xamarin.Forms Slider to value in your viewmodel then in your renderer you change the value in your viewmodel.
If you bind all your properties (min, max, value, progress) itll be easier

Related

Xamarin binding IsEnabled does not works

I have this strange problem, where the binding seems completely ignored.
my xaml
<Button IsEnabled="{Binding ButtonEnabled}" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
my C#
private bool _buttonEnabled = false;
public bool ButtonEnabled
{
get
{
// breakpoint 1, which never hits with value = false
return _buttonEnabled;
}
set
{
// breakpoint 2, which hits
_buttonEnabled = value;
OnPropertyChanged(nameof(ButtonEnabled));
}
}
private void ChassisEntry_TextChanged(object sender, TextChangedEventArgs e)
{
ButtonEnabled = ChassisEntry.Text != "";
}
private void PageScan_Appearing(object sender, EventArgs e)
{
ChassisEntry.Text = "";
}
I expect that when this page opens that ButtonOK is disabled, but it is not.
When I set breakpoints then breakpoint 1 (in the getter) never hits, its like if the xaml IsEnabled="{Binding ButtonEnabled}" is ignored.
The breakpoint 2 does hits, with value = false
What am I missing here ?
I googled this problem and found many similar questions, but all solutions given do not help with my problem.
Button IsEnabled binding not working properly
How to disable a button until all entries are filled?
Disable/Enable save button based on the mandatory field being null or empty using Behaviors
and many more
I am guessing you are using the xaml.cs page for holding your Bindings and hence if you are doing that there are two ways to do this
Set the BindingContext to the current class in the constructor before or right after InitializeComponent
BindingContext= this;
Or In your XAML
<ContentPage
....
x:Name="currentPage">
And in your button
<Button IsEnabled="{Binding ButtonEnabled, Source={x:Reference currentPage}}"
I would go with this: Set my Button in my XAML disabled.
<Button IsEnabled="False" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
Then on my Entry control i would add the property TextChanged.
<Entry x:Name="ChassisEntry"
PlaceholderColor="DarkGray"
TextChanged="ChassisEntryChanged">
On xaml.cs file:
private void ChassisEntryChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Text != "")
{
ButtonOK.IsEnabled = true;
}
else
{
ButtonOK.IsEnabled = false;
}
}

Event Raising in Custom Render

I have a custom Renderer which is a DatePicker Control placed inside a StackLayout. I have a label which will invoke the CustomRenderer and shows up date picker control. When i tap on the Label there is no event raised in the CustomRenderer (OnElementChanged && OnElementPropertyChanged) events. When we change the date the picker event fires and the value gets set to date picker and datepicker closes. When I try to open the date picker again the previous set date is showing. which i want to change to current date. i dont have the event to reset the date.
OnPlatform x:TypeArguments="View">
<OnPlatform.Android>
<local:CustomDatePicker AbsoluteLayout.LayoutBounds="1,0.5,-1,-1" WidthRequest="140" TextColor="Black" Date="{Binding ResourceDate}"
AbsoluteLayout.LayoutFlags="PositionProportional"/>
</OnPlatform.Android>
<OnPlatform.iOS>
<Label Text="{Binding ResourceDateIOS}" AbsoluteLayout.LayoutBounds="1,0.5,-1,-1" AbsoluteLayout.LayoutFlags="PositionProportional" FontSize="Small" TextColor="Black" >
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnDateClickIOSForResource}" />
</Label.GestureRecognizers>
</Label>
</OnPlatform.iOS>
</OnPlatform>
IOS Stack Custom Render:
using Xamarin.Forms;
using UIKit;
using CoreGraphics;
using Xamarin.Forms.Platform.iOS;
using System;
using VCS.FieldManager.UI.ViewModels;
[assembly: ExportRenderer(typeof(CustomStackLayout), typeof(DatePickerExtendedRender))]
namespace App.UI.iOS
{
class DatePickerExtendedRender : ViewRenderer<CustomStackLayout, UIView>
{
UIDatePicker datePicker = new UIDatePicker();
protected override void OnElementChanged(ElementChangedEventArgs<CustomStackLayout> e)
{
if (e.OldElement != null || Element == null)
{
return;
}
var customStackLayout = e.NewElement;
if (customStackLayout != null)
{
datePicker.Mode = UIDatePickerMode.Date;
UIView _view = new UIView()
{
Frame = new CGRect(10, 200, 50, 50),
// BackgroundColor = UIColor.Brown
};
datePicker.ValueChanged += (sender, arg) =>
{
var DateChangeEvent = DateTime.SpecifyKind(datePicker.Date.ToDateTime(), DateTimeKind.Utc).ToLocalTime();
var changedDate = DateChangeEvent.ToString("MMMM dd, yyyy");
//shouldnot set in application variable for Add Resources
if (GridEntryViewModel.IsResourceDate)
{
Xamarin.Forms.Application.Current.Properties["ResourceSelectedDate"] = DateChangeEvent.ToString("MMMM dd, yyyy");
datePicker.SetDate(datePicker.Date, true);
}
else
{
Xamarin.Forms.Application.Current.Properties["GlobalDateSelected"] = changedDate;
GridEntryViewModel.IsResourceDate = false;
datePicker.SetDate(datePicker.Date, true);
}
MessagingCenter.Send(changedDate, "DatePickerCallback");
};
_view.Add(datePicker);
SetNativeControl(_view);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
}
UIDatepicker defaults to current date unless you change it. Since you instatiating your Datepicker at the class level it maintains the date. Id think one or both of the following would fix it.
if (customStackLayout != null)
{
datePicker = new UIDatePicker();
or
if (customStackLayout != null)
{
datePicker.Date = DateTime.Now;
Not sure on the second whats dataType Date is you'll have to check it and set it as appropriate but hopefiully you get my point.

Value of property binded to a TextBlock inside a User Control is not being detected

I've created a UserControl, LiveTile.xaml (streamlined for brevity):
<UserControl
x:Class="Weathercast.Core.LiveTile"
xmlns:local="clr-namespace:Weathercast.Core">
<StackPanel
x:Name="LayoutRoot">
<StackPanel
x:Name="TileRegularFront"
Width="336"
Height="336"
Background="Red">
<TextBlock Text="{Binding TempCurrentHour}"/>
</StackPanel>
</UserControl>
Its code behind, LiveTile.xaml.cs:
public partial class LiveTile : UserControl
{
public LiveTile()
{
InitializeComponent();
LiveTileViewModel vm = new LiveTileViewModel();
this.DataContext = vm;
}
}
Its view model, LiveTileViewModel.cs:
public class LiveTileViewModel : ObservableObject
{
/** PROPERTIES **/
private string _tempCurrentHour;
public string TempCurrentHour
{
get { return _tempCurrentHour; }
set
{
_tempCurrentHour = value;
RaisePropertyChanged("TempCurrentHour");
}
}
/** CONSTRUCTOR **/
public LiveTileViewModel()
{
this.TempCurrentHour = "15"; // dummy value set
}
}
ObservableObject.cs:
public abstract class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The problem is the value I'm binding ("TempCurrentHour") is not being displayed. Any ideas on what I need to do in order to get the User Control's View Model's binded property value to display? Based on my research, I believe binding a value to a User Control is less straightforward than normal. However I can't get my head around what needs to be done to get the User Control to detect binded property values.
UPDATE: Just to be clear, the LiveTile class is in a Library project for my solution. An instance of it is created when the user toggles on the Live Tile via the Settings PhoneApplicationPage located in the Windows Phone App project in the solution. This is the event handler that instantiates a LiveTile in Settings.xaml.cs:
private void LiveTile_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Weathercast.Core.LiveTile l = new Weathercast.Core.LiveTile();
l.CreateOrUpdateTile(1);
}
The CreateOrUpdateTile method is doing its job correctly and takes the user back to their phone's Start screen with the Live Tile now there. This is its code in any case (I'm using Telerik's LiveTileHelper):
RadFlipTileData tileData = new RadFlipTileData()
{
VisualElement = this.TileRegularFront,
BackVisualElement = this.TileRegularBack,
SmallVisualElement = this.TileSmall
};
// Tile's uri has a unique paramater which is the location Id of the currently viewed location.
Uri tileUri = new Uri("/MainPage.xaml?locationId=" + locationId, UriKind.RelativeOrAbsolute);
// If the tile for this location previously existed, delete it before adding it anew.
ShellTile tile = LiveTileHelper.GetTile(tileUri);
if (tile != null)
{
tile.Delete();
}
// Create brand new tile for location if didn't have tile previously or fresh tile if it did.
LiveTileHelper.CreateOrUpdateTile(tileData, tileUri, true);
this.tile = tileData;
// Add the Background Agent for this tile with the agent's name
// unique for the location.
AddAgent("PeriodicTaskForLocation" + locationId);
I should note another problem I'm having, that may or may not be related to the original issue, is that the background property I'm setting in LiveTile.xaml for the StackPanel or LayoutRoot element even is being neglected and the Live Tile that's being added to the Start screen is transparent (black).

Child controls grow unlimited in custom XAML control. What's wrong?

I've implemented a Windows 8 XAML VisibilitySwitchControl that displays the first child on certain condition; otherwise the other controls are shown. The code is as follows
[ContentProperty(Name = "Items")]
public class VisibilitySwitchControl : ItemsControl
{
public VisibilitySwitchControl()
{
DefaultStyleKey = typeof(VisibilitySwitchControl);
if (Items != null)
Items.VectorChanged += OnItemsChanged;
}
public bool ShowFirst
{
get { return (bool)GetValue(ShowFirstProperty); }
set { SetValue(ShowFirstProperty, value); }
}
public static readonly DependencyProperty ShowFirstProperty =
DependencyProperty.Register("ShowFirst", typeof(bool), typeof(VisibilitySwitchControl), new PropertyMetadata(true, OnShowFirstChanged));
public object VisibleContent
{
get { return GetValue(VisibleContentProperty); }
private set { SetValue(VisibleContentProperty, value); }
}
public static readonly DependencyProperty VisibleContentProperty =
DependencyProperty.Register("VisibleContent", typeof(object), typeof(VisibilitySwitchControl), new PropertyMetadata(null));
private static void OnShowFirstChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
var visibilityItemsControl = d as VisibilitySwitchControl;
if (visibilityItemsControl != null)
{
visibilityItemsControl.Evaluate();
}
}
void OnItemsChanged(IObservableVector<object> sender, IVectorChangedEventArgs evt)
{
Evaluate();
}
void Evaluate()
{
if (Items != null && Items.Any())
{
var controls = Items.OfType<FrameworkElement>().ToList();
for (var i = 0; i < controls.Count; i++)
{
if (i == 0)
{
VisibleContent = controls[i];
controls[i].Visibility = ShowFirst ? Visibility.Visible : Visibility.Collapsed;
}
else
{
controls[i].Visibility = !ShowFirst ? Visibility.Visible : Visibility.Collapsed;
}
}
}
else
{
VisibleContent = null;
}
}
}
However, if I place two ListView controls inside my VisibilitySwitchControl the ListView can grow in way that it is larger than the page and no scrollbars are shown. It doesn't stop a the parent containers bounds.
<custom:VisibilitySwitchControl ShowFirst="{Binding Path=IsFirstLevelNav}">
<ListView x:Name="FirstListView"
VerticalAlignment="Stretch"
ItemsSource="{Binding ...}"
SelectedItem="{Binding ..., Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
<ListView x:Name="SecondListView"
VerticalAlignment="Stretch"
ItemsSource="{Binding ...}"
SelectedItem="{Binding ..., Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
</custom:VisibilitySwitchControl>
How can I enforce a VerticalAlignment="Stretch" behavior of the children? If I remove my control and place only one the lists directly in the code, everything works as expected.
Thanks for suggestions.
you want to stretch the Height of the listview try binding it to the actual height of the parent
Heres the code part you need to include
Height="{Binding ActualHeight, ElementName=parentContainer}"
where parentContainer is the name of the custom:VisibilitySwitchControl you are using . this will bind the height to the parent container's display height. Try and let me know
If what you want is that you scroll one ListView and then when you reach the end it show the second ListView then you just need to add a ScrollViewer around the ItemPresenter inside the style of VisibilitySwitchControl and disable the ListView ScrollViewer. Just note that it mean that you will lost the virtualisation inside the ListView.
If what you want is each ListView taking half the screen than the easiest is probably to just set a Fix height for each items depending on Window.Current.Bounds.Height and register for Window.Current.SizeChanged to update it when the windows heigh changed (make sure to unregister it in unloaded to prevent memory leak).
An alternative which I think would be more complicated, would be to change the ItemsPanel of VisibilitySwitchControl to something else (by default it is a Stack panel so it will grow larger than the screen) like for example to a Grid in which you set as many row with star heigh as items you have (and then you will need to set the row of each item) or by creating a custom Panel.

How to databind control height to another control's height?

I'm trying to have 2 controls have the same height. Can I do it with XAML only?
If I did something like <Canvas Height="{Binding Height, ElementName=AnotherControl}" /> it doesn't actually do anything and the height goes to zero. The Output panel doesn't complain about any binding errors so AnotherControl.Height really exists. I tried binding to ActualHeight but it doesn't do anything either.
Anything else I missed?
My guess is that you AnotherControl is not explicitly given a Height. Unfortunately, in WinRT (unlike WPF, but the same as Silverlight), ActualWidth and ActualHeight are what are known as "calculated properties". This means that a property changed event doesn't internally get raised when they change. As a result, binding to them is not reliable, and as you've noticed, it wouldn't quite work.
Side note: it may work from time to time, but that is purely because of the timing of the get call the binding framework makes to ActualHeight.
So as it stands, you cannot do it with XAML only. You have to handle the ActualControl.SizeChanged event in code-behind, and set the Height to AnotherControl.ActualHeight explicitly.
As Kshitij Mehta mentioned, binding to ActualHeight and ActualWidth in WinRT isnt reliable. But there is a nice work-around, where you dont have to use the SizeChanged-Event:
Add this class:
public class ActualSizePropertyProxy : FrameworkElement, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public FrameworkElement Element
{
get { return (FrameworkElement)GetValue(ElementProperty); }
set { SetValue(ElementProperty, value); }
}
public double ActualHeightValue
{
get { return Element == null ? 0 : Element.ActualHeight; }
}
public double ActualWidthValue
{
get { return Element == null ? 0 : Element.ActualWidth; }
}
public static readonly DependencyProperty ElementProperty =
DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(ActualSizePropertyProxy),
new PropertyMetadata(null, OnElementPropertyChanged));
private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ActualSizePropertyProxy)d).OnElementChanged(e);
}
private void OnElementChanged(DependencyPropertyChangedEventArgs e)
{
FrameworkElement oldElement = (FrameworkElement)e.OldValue;
FrameworkElement newElement = (FrameworkElement)e.NewValue;
newElement.SizeChanged += new SizeChangedEventHandler(Element_SizeChanged);
if (oldElement != null)
{
oldElement.SizeChanged -= new SizeChangedEventHandler(Element_SizeChanged);
}
NotifyPropChange();
}
private void Element_SizeChanged(object sender, SizeChangedEventArgs e)
{
NotifyPropChange();
}
private void NotifyPropChange()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ActualWidthValue"));
PropertyChanged(this, new PropertyChangedEventArgs("ActualHeightValue"));
}
}
}
Place it in the resources:
<UserControl.Resources>
<c:ActualSizePropertyProxy Element="{Binding ElementName=YourElement}" x:Name="proxy" />
</UserControl.Resources>
And bind to its properties:
<TextBlock x:Name="tb1" Text="{Binding ActualWidthValue, ElementName=proxy}" />
This Question is very old, but here is my solution.
You can use this Code
<!--First Button-->
<Button x:Name="button1" Height="50" Width="100"/>
<!--Second Button-->
<Button x:Name="button2" Height="50" Width="{Binding ElementName=button1, Path=Width}"/>
I've tested it on my Windows / Windows Phone 8.1 Device and it workes great.