In my Xamarin Forms application I have Label:
<Label Text="{Binding Number}" HorizontalTextAlignment ="Center">
In ViewModel:
private int _number;
public int Number
{
get { return _number; }
set { SetProperty(ref _number, value); }
}
When message is received, I update number:
MessagingCenter.Subscribe<EmptyMessage>(this, "NUMBER_CHANGED", OnNumberChanged);
private async void OnNumberChanged(EmptyMessage emptyMessage)
{
Number = ReadNumberFromDB();
}
All of this is on MainPage. When "NUMBER_CHANGED" message is triggered from MainPage, Number is updated on UI and label containing Number is properly centered.
From MainPage, one can PushAsync SecondPage. From SecondPage "NUMBER_CHANGED" can also be triggered. After triggering it and returning to MainPage, Number is updated, label containing it has proper value but instead of being centered it is left aligned.
Is this Xamarin UI bug? Is there a workaround for this? I need somehow to tell label to refresh it's position. I want label to always be centered. I don't know why it is left aligned after refreshing it's content.
HorizontalTextAlignment and VerticalTextAlignment, not working after a data change is a known bug. Bug 55359 details this for a TabbedPage, but it occurs in many places, such as Bug 49311.
The workaround is to use HorizontalOptions or VerticalOptions for the moment.
I ran into this problem recently. I simply made a constant variable to track what page needed to be updated OnResume and just reassigned my bindings accordingly.
Related
.net maui app.
Dragging value element along the slider bar does not work if the the slider put into CarouselView's template like this:
<CarouselView ItemsSource="{Binding Items}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Slider Minimum="0" Maximum="30" WidthRequest="200" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
CarouselView takes over the swipe event for scrolling through the items, and Slider does not get the event (DragStarted is not even called). You can actually click along the slider bar to change its value, so it's not completely frozen, but not how it's supposed to work. Drag & drop is main way user deal with slider control.
Could anyone advise any workaround? I want users to be able scroll through carousel view items also. It's just if they swipe inside the control, event should not handed over to its parent container, if it's possible to do so.
If I add it outside of the corouselview, combine both in Grid and use padding to align slider inside the corouselview, it works as expected, but I need to add lots of additional code, calculate the desirable location and redirect all bindings, which ends up to be an awkward workaround.
At first, I don't suggest you use the slider in the CarouselView. Becasue you want the same behavior has two effects. There is a conflict between them.
But for the android, you can use the custom handler to deal with the swipe event.
Put the Handler class in the /Platform/Android:
public class MySliderHander : SliderHandler
{
protected override void ConnectHandler(SeekBar platformView)
{
base.ConnectHandler(platformView);
platformView.SetOnTouchListener(new SliderListener());
// the listener will make the slider deal with the swip event not the CarouselView.
}
}
Put the SliderListener class in the /Platform/Android
public class SliderListener : Java.Lang.Object, IOnTouchListener
{
public bool OnTouch(global::Android.Views.View v, MotionEvent e)
{
if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
{
v.Parent.RequestDisallowInterceptTouchEvent(true);
}
else
{
v.Parent.RequestDisallowInterceptTouchEvent(false);
}
return false;
}
}
And in the MauiProgram.cs:
builder
.UseMauiApp<App>()
.ConfigureMauiHandlers(handlers => {
#if ANDROID
handlers.AddHandler(typeof(Slider), typeof(YourProjectName.Platforms.Android.MySliderHander));
#endif
})
In addition, the Slider's height is same as the CarouselView. So you can use a frame to contain the Slider and swipe the CarouselView by swiping the frame.
I'd like to redraw my QR Code every time a particular TextChanged event is triggered.
The ZXingBarcodeImageView object gets drawn in the view when the page loads with the value BarcodeValue set in the XAML file like this:
<forms:ZXingBarcodeImageView
Margin="5,5,5,0"
x:Name="QRCodeView"
BarcodeFormat="QR_CODE"
BarcodeValue="-1" //this is the value of the QR code
/>
I have an Entry with a TextChanged event attached, which triggers a function UpdateQRLabel. This function should redraw the QRCode with the new value in Entry
<Entry
x:Name="Message"
TextChanged="UpdateQRLabel"
/>
If I change the BarcodeValue parameter after the QRCode has been drawn, it DOES NOT get redrawn automatically.
I need to force the ZXingBarcodeImageView object to redraw every time the TextChanged event is triggered.
Question
How do I force the ZXingBarcodeImageView to redraw when the TextChanged event is triggered?
I'm not sure if you're using data-binding or not. Since you are using events I guess not, however, I did get it to work with data-binding. A sample repo can be found here: https://github.com/jfversluis/ZXingValueBinding
It comes down to this. Create a property which will hold your barcode value:
private string _barcodeValue = "-1";
public string BarcodeValue
{
get { return _barcodeValue; }
set
{
_barcodeValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BarcodeValue)));
}
}
The object holding this property needs to implement the INotifyPropertyChanged interface. You could consider using PropertyChanged.Fody for these.
I have put this property in the code-behind of my page, this can also be a separate class. Now, change your barcode image view to this: <forms:ZXingBarcodeImageView ... BarcodeValue="{Binding BarcodeValue}">.
Whenever you set a new value to BarcodeValue, the value should change because the UI is notified because of the INotifyPropertyChanged mechanism.
I have a SeachBar inside a ScrollView. In iOS all is good. On Android the ScrollView automatically scrolls to the SearchBar, adds focus to it and displays the soft keyboard. I can hide the softkeyboard by adding android:windowSoftInputMode="stateHidden" as an activity in the AndroidManifest.xml file but I can't work out how to prevent the focus (and hence the auto scroll). Any help would be much appreciated.
Using Angular2:
app/my.component.html
<StackLayout (loaded)="onSearchLayoutLoaded($event)">
<SearchBar hint="search here" (loaded)="onSearchBarLoaded($event)">
</SearchBar>
</StackLayout>
app/my.component.ts
onSearchLayoutLoaded(event) {
if (event.object.android) {
event.object.android.setFocusableInTouchMode(true);
}
}
onSearchBarLoaded(event) {
if (event.object.android) {
event.object.android.clearFocus();
}
}
This eliminates unnecessarily having to use template reference variables.
For Android you need to do a couple of things. If you were using a native Android layout, lets say LinerLayout you would set android:focusableInTouchMode="true" and that should work for most use cases. So with NativeScript you're going to use the associated method on the parent of your SearchBar and then call `clearFocus() on the searchbar.
Example
function removeSearchFocus() {
// get the parent of the searchbar
var parent = page.getViewById('parentLayout');
var searchBar = page.getViewById('mySearchBar');
if (parent.android) {
parent.android.setFocusableInTouchMode(true);
parent.android.setFocusable(true);
searchBar.android.clearFocus();
}
}
Then attach this function to one of the page navigation event, or dump the important pieces into a current page event you might already have in your app. Just assign the parent an ID and the SearchBar so you can get it by the ID and this should work.
How about adding a endEditing to the page loaded or loading event?
searchBar = page.getViewById('your-searchbar');
searchBar.ios.endEditing(true);
or set focus to another element, where you want the focus to be, e.g.
somethingElse = page.getViewById('your-top-bar');
somethingElse.focus();
I've come across an issue using the Pivot control with Caliburn Micro and WP8. When I update DisplayName for the child ViewModels (pivot items), the spacing of the pivot headers does not update to reflect this. Thus, pivot headers overlap each other, and it looks very jumbled.
Example:
WP7 Version:
WP8 Version:
This problem only started when we migrated from targetting WP7/WP8 to WP8 only. Does anyone have any ideas about how to get the spacing to update when changing DisplayName, and thus the pivot header text? Thanks!
I know a long time has passed since you asked this, but I got a solution.
Microsoft is promising that this issue is fixed in WP8, but it still isn't.
In order to render the headers properly, you have to change the width of the pivot item.
So here is an example code width x:Name="PivotControl" set for Pivot:
double width = PivotControl.Width;
double width2 = Application.Current.Host.Content.ActualWidth+10;
PivotControl.Width = width2;
await Task.Delay(1);
PivotControl.Width = width;
This will change the width to a bit more than yoru screen width and than after 1ms change it back so the items will be rendered properly and the Layout will be updated (because LayoutUpdate is not working).
Here is better solution
Pivot header template:
<!-- The SizeChanged event is key here. We have to invalidate an ancestor when we get this event. -->
<DataTemplate x:Key="pivotHeaderTemplate">
<TextBlock SizeChanged="OnHeaderSizeChanged" Text="{Binding SomeData}"/>
</DataTemplate>
Pivot:
<phone:Pivot Title="WHATEVER"
DataContext="{StaticResource query}"
ItemsSource="{Binding source}"
HeaderTemplate="{StaticResource pivotHeaderTemplate}"
ItemContainerStyle="{StaticResource pivotItemStyle}"/>
Code-behind:
/// <summary>
/// When something inside of the template changes, then we need to invalidate the measure of the ancestor PivotHeadersControl
/// </summary>
private void OnHeaderSizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement fe = (FrameworkElement)sender;
while (fe != null)
{
if (fe is PivotHeadersControl)
{
fe.InvalidateMeasure();
break;
}
fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
}
}
I have created a Scrollviewer in WP7, which harbors 3 usercontrol, each one of which hold as their content XAML created UserControls. This works fine. This scrollviewer should be able to scroll between these items, but make this not possible for the user to scroll. So when an item in one of these contents are clicked upon, the scrollviewer slides left or right depending on the item selected, and bring into view one of the other usercontrols. I use a mediator to accomplish this:
<Grid.Resources>
<Storyboard x:Name="ItemAnimation">
<DoubleAnimation x:Name="ItemAnimationContent"
Storyboard.TargetName="Mediator"
Storyboard.TargetProperty="ScrollableWidthMultiplier"/>
</Storyboard>
</Grid.Resources>
<ScrollViewer Name="ScrollableItemPanel"
Grid.Row="2"
Grid.RowSpan="3"
Grid.ColumnSpan="3"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Orientation="Horizontal">
<UserControl Name="NewsListBoxControl" Width="480" />
<UserControl Name="DetailedItemControl" Width="480"/>
<UserControl Name="ExternalBrowserItemControl" Width="480"/>
</StackPanel>
</ScrollViewer>
<local:ScrollableItemAnimationMediator x:Name="Mediator"
ScrollViewer="{Binding ElementName=ScrollableItemPanel}"/>
In basic, this works fine too, I can navigate between the items, and load upon them the content as usercontrols. But the problem lies in granting the user the abillity to scroll. Before the item scrolls, I set the hittestvisibilty to true, and the horizontalscrollbarvisibility to visible. After the animation is done, I want to grant back the hittestvisibility and set the horizontalscrollbarvisibility to Disabled again. This latter is where the problem is: when I set the horizontalscrollbarvisibility to Disabled, the scrollviewer automatically brings back into view the first of three items in the stackpanel. How can I stop this? This is the code I use to scroll the mediator:
private void CreateDetailedArticleItem( Dictionary<string, string> itemQuery )
{
_articleDetailPage.ItemQuery = itemQuery;
DetailedItemControl.Content = _articleDetailPage as UserControl;
Animate( _articleDetailPage, 0.0f, 0.5f, 250 );
}
private void Animate( IContentControl control, float from, float to, double milliseconds )
{
//this eventhandler will fire when the animation has completed
EventHandler handler = null;
//we take away the User Input just for the moment, so that we can animate without the user interfering. Also, we make horizontalScroll Visible
IsUserEnabled = false;
//we then set the content of the animation. Where from will it move, towards where and in what duration?
ItemAnimationContent.From = from;
ItemAnimationContent.To = to;
ItemAnimationContent.Duration = TimeSpan.FromMilliseconds( milliseconds );
//we start the animation
ItemAnimation.Begin( );
//we tell the new control that it will appear soon, so it can load its main content
control.ViewWillAppear( );
//also, we tell the currentcontrol that it will disappear soon, so it can unload its content and eventhandlers and so on
CurrentControl.ViewWillDisAppear( );
//the handler is a delegate. This way, it becomes rather easy and clean to fire the completed event, without creating a strong reference ( well, actually,
//we do create a strong reference, but as soon as it is fired, we remove it again, shhhh! ).
handler = delegate( object sender, EventArgs e )
{
//as stated, we remove the eventlistener again, so it won't keep firing all the time
ItemAnimation.Completed -= handler;
//after the animation, we tell the new control that it is now in screen, and can start downloading its data
control.ViewDidAppear( );
//at the same time, the "current" control has fully moved out of view, so it can now fully unload all its content.
CurrentControl.ViewDidDisAppear( );
//now, all we have to do is to make sure that the next time an item is being loaded, the new content is spoken to, not the old one
CurrentControl = control;
//and finally, enable the users input again, and remove the horizontal scrollbarvisibility
IsUserEnabled = true;
};
ItemAnimation.Completed += handler;
}
private bool IsUserEnabled
{
set
{
//when the user can control the scrollviewer, then the horizontal scrollvisibility is disabled, so that the user cannot move horizontally,
//otherwise, so we only make it visible when the program needs to animate.
ScrollableItemPanel.IsHitTestVisible = value;
ScrollableItemPanel.HorizontalScrollBarVisibility = value ? ScrollBarVisibility.Disabled : ScrollBarVisibility.Visible;
}
}
I had already asked this question, then regarded it as answered, as I thought it to be answered, namely using ScrollbarVisibility.Hidden instead of ScrollbarVisibility.Disabled, only the scrollbarvisibility stays visible this way, and the user can still scroll. Is there a native way to deal with this problem?
Any help would be greatly appreciated. Greetz
Rather than fight the behavior of the native control it may be easier to just manipulate the position of items yourself using a custom control (wrapping your other controls) which animates between different visual states (adjust the translate transform) depending on the "selected" item.